1 <?php
2 3 4 5 6 7 8 9 10
11 class File extends DataObject {
12
13 static $default_sort = "\"Name\"";
14
15 static $singular_name = "File";
16
17 static $plural_name = "Files";
18
19 static $db = array(
20 "Name" => "Varchar(255)",
21 "Title" => "Varchar(255)",
22 "Filename" => "Varchar(255)",
23 "Content" => "Text",
24 "Sort" => "Int",
25 "FileSize" => "Int",
26 "Hidden" => "Boolean",
27 );
28
29 static $has_one = array(
30 "Parent" => "File",
31 "Owner" => "Member"
32 );
33
34 static $has_many = array();
35
36 static $many_many = array();
37
38 static $belongs_many_many = array();
39
40 static $defaults = array();
41
42 static $extensions = array(
43 "Hierarchy",
44 );
45
46 static $indexes = array(
47 "Filename" => true,
48 );
49
50 51 52 53
54 public static $allowed_extensions = array(
55 '','html','htm','xhtml','js','css',
56 'bmp','png','gif','jpg','jpeg','ico','pcx','tif','tiff',
57 'au','mid','midi','mpa','mp3','ogg','m4a','ra','wma','wav','cda',
58 'avi','mpg','mpeg','asf','wmv','m4v','mov','mkv','mp4','swf','flv','ram','rm',
59 'doc','docx','rtf','xls','xlsx','pages', 'odt', 'ods',
60 'ppt','pptx','pps', 'odp', 'odg', 'fodp', 'uop', 'fodg',
61 'cab','arj','tar','zip','zipx','sit','sitx','gz','tgz','bz2','ace','arc','pkg','dmg','hqx','jar', 'rar', '7z',
62 'xml','pdf', 'csv', 'txt',
63 'eps', 'svg', 'cdr', 'ai',
64 );
65
66 67 68 69 70
71 public static $apply_restrictions_to_admin = true;
72
73
74 75 76 77 78 79
80 protected static $cache_file_fields = null;
81
82 83 84 85
86 static function find($filename) {
87
88 $filename = ereg_replace('_resampled/[^-]+-','',$filename);
89
90 $parts = explode("/", $filename);
91 $parentID = 0;
92 $item = null;
93
94 foreach($parts as $part) {
95 if($part == "assets" && !$parentID) continue;
96 $SQL_part = Convert::raw2sql($part);
97 $item = DataObject::get_one("File", "\"Name\" = '$SQL_part' AND \"ParentID\" = $parentID");
98 if(!$item) break;
99 $parentID = $item->ID;
100 }
101
102 return $item;
103 }
104
105 function Link($action = null) {
106 return Director::absoluteBaseURL() . $this->RelativeLink($action);
107 }
108
109 function RelativeLink($action = null){
110 return $this->Filename;
111 }
112
113 function TreeTitle() {
114 return $this->Title;
115 }
116
117
118 function BackLinkTrackingCount() {
119 return $this->getUsageCount();
120 }
121
122 123 124 125 126
127 protected function onBeforeDelete() {
128 parent::onBeforeDelete();
129
130 $this->autosetFilename();
131 if($this->Filename && $this->Name && file_exists($this->getFullPath()) && !is_dir($this->getFullPath())) {
132 unlink($this->getFullPath());
133 }
134 }
135
136 protected function onAfterDelete() {
137 parent::onAfterDelete();
138
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
161 }
162
163 164 165 166 167
168 function canView($member = null) {
169 if(!$member) $member = Member::currentUser();
170
171 $results = $this->extend('canView', $member);
172 if($results && is_array($results)) if(!min($results)) return false;
173
174 return true;
175 }
176
177 178 179 180 181 182 183 184
185 function canEdit($member = null) {
186 if(!$member) $member = Member::currentUser();
187
188 $results = $this->extend('canEdit', $member);
189 if($results && is_array($results)) if(!min($results)) return false;
190 if(Permission::checkMember($member, 'CMS_ACCESS_AssetAdmin')) return true;
191 return $results;
192 }
193
194 195 196
197 function canCreate($member = null) {
198 if(!$member) $member = Member::currentUser();
199
200 $results = $this->extend('canCreate', $member);
201 if($results && is_array($results)) if(!min($results)) return false;
202
203 return $this->canEdit($member);
204 }
205
206 207 208
209 function canDelete($member = null) {
210 if(!$member) $member = Member::currentUser();
211
212 $results = $this->extend('canDelete', $member);
213 if($results && is_array($results)) if(!min($results)) return false;
214
215 return $this->canEdit($member);
216 }
217
218 public function appCategory() {
219 $ext = $this->Extension;
220 switch($ext) {
221 case "aif": case "au": case "mid": case "midi": case "mp3": case "ra": case "ram": case "rm":
222 case "mp3": case "wav": case "m4a": case "snd": case "aifc": case "aiff": case "wma": case "apl":
223 case "avr": case "cda": case "mp4": case "ogg":
224 return "audio";
225
226 case "mpeg": case "mpg": case "m1v": case "mp2": case "mpa": case "mpe": case "ifo": case "vob":
227 case "avi": case "wmv": case "asf": case "m2v": case "qt": case "mkv":
228 return "movie";
229
230 case "zip": case "arc": case "rar": case "tar": case "gz": case "tgz": case "bz2": case "dmg": case "jar":
231 case "ace": case "arj": case "bz": case "cab": case "7z":
232 return "zip";
233
234 case "bmp": case "gif": case "jpg": case "jpeg": case "pcx": case "tif": case "png": case "alpha":
235 case "als": case "cel": case "icon": case "ico": case "ps":
236 case 'dmg': case 'ico': case 'tiff':
237 return "image";
238
239 case 'xls': case 'ods': case 'xlsx': case 'csv':
240 return 'xls';
241
242 case 'doc': case 'odt': case 'docx': case 'rtf':
243 return 'doc';
244
245 case 'txt':
246 return 'txt';
247
248 case 'pdf':
249 return 'pdf';
250 }
251 }
252
253 function CMSThumbnail() {
254 $filename = $this->Icon();
255 $text = _t('File.DOWNLOAD','Download');
256 return "<div style=\"text-align:center;width: 100px;padding-top: 15px;\"><a target=\"_blank\" href=\"$this->URL\" title=\"$text: $this->URL\"><img src=\"$filename\" alt=\"$filename\" /></a><br /><br /><a style=\"color: #0074C6;\"target=\"_blank\" href=\"$this->URL\" title=\"$text: $this->URL\">$text</a><br /><em>$this->Size</em></div>";
257 }
258
259 260 261
262 function Icon() {
263 $ext = $this->Extension;
264 if(!Director::fileExists(SAPPHIRE_DIR . "/images/app_icons/{$ext}_32.gif")) {
265 $ext = $this->appCategory();
266 }
267
268 if(!Director::fileExists(SAPPHIRE_DIR . "/images/app_icons/{$ext}_32.gif")) {
269 $ext = "generic";
270 }
271
272 return SAPPHIRE_DIR . "/images/app_icons/{$ext}_32.gif";
273 }
274
275 276 277
278 function onAfterUpload() {
279 $this->extend('onAfterUpload');
280 }
281
282 283 284
285 public function deleteDatabaseOnly() {
286 if(is_numeric($this->ID)) DB::query("DELETE FROM \"File\" WHERE \"ID\" = $this->ID");
287 }
288
289 290 291 292 293
294 protected function onBeforeWrite() {
295 parent::onBeforeWrite();
296
297 if(!$this->Name) $this->Name = "new-" . strtolower($this->class);
298 $this->FileSize = $this->getAbsoluteSize();
299 }
300
301 302 303 304 305 306 307
308 public function collateDescendants($condition, &$collator) {
309 if($children = $this->Children()) {
310 foreach($children as $item) {
311 if(!$condition || eval("return $condition;")) $collator[] = $item;
312 $item->collateDescendants($condition, $collator);
313 }
314 return true;
315 }
316 }
317
318 319 320 321
322 function setName($name) {
323 $oldName = $this->Name;
324
325
326 if(!$name) $name = $this->Title;
327
328
329 $title = $name;
330 $name = Convert::rus2lat($name);
331 $name = ereg_replace(' +','-',trim($name));
332 $name = ereg_replace('[^A-Za-z0-9.+_\-]','',$name);
333
334 while(!empty($name) && ($name[0] == '_' || $name[0] == '.')) {
335 $name = substr($name, 1);
336 }
337
338
339 if(!$name) $name = 'new-folder';
340
341
342 if($oldName && $oldName != $name) {
343 if($dotPos = strrpos($name, '.')) {
344 $base = substr($name,0,$dotPos);
345 $ext = substr($name,$dotPos);
346 } else {
347 $base = $name;
348 $ext = "";
349 }
350 $suffix = 1;
351 while(DataObject::get_one("File", "\"Name\" = '" . addslashes($name) . "' AND \"ParentID\" = " . (int)$this->ParentID)) {
352 $suffix++;
353 $name = "$base-$suffix$ext";
354 }
355 }
356
357 if(!$this->getField('Title')) $this->__set('Title', str_replace(array('-','_'),' ',ereg_replace('\.[^.]+$','',$title)));
358 $this->setField('Name', $name);
359
360
361 if($oldName && $oldName != $this->Name) {
362 $this->resetFilename();
363 } else {
364 $this->autosetFilename();
365 }
366
367
368 return $this->getField('Name');
369 }
370
371 372 373 374
375 protected function resetFilename($renamePhysicalFile = true) {
376 $oldFilename = $this->getField('Filename');
377 $newFilename = $this->getRelativePath();
378
379 if($this->Name && $this->Filename && file_exists(Director::getAbsFile($oldFilename)) && strpos($oldFilename, '//') === false) {
380 if($renamePhysicalFile) {
381 $from = Director::getAbsFile($oldFilename);
382 $to = Director::getAbsFile($newFilename);
383
384
385 if(!file_exists($from)) user_error("Cannot move $oldFilename to $newFilename - $oldFilename doesn't exist", E_USER_WARNING);
386 else if(!file_exists(dirname($to))) user_error("Cannot move $oldFilename to $newFilename - " . dirname($newFilename) . " doesn't exist", E_USER_WARNING);
387 else if(!rename($from, $to)) user_error("Cannot move $oldFilename to $newFilename", E_USER_WARNING);
388
389 else $this->updateLinks($oldFilename, $newFilename);
390
391 } else {
392 $this->updateLinks($oldFilename, $newFilename);
393 }
394 } else {
395
396 if(file_exists(Director::getAbsFile($newFilename))) $this->updateLinks($oldFilename, $newFilename);
397 }
398
399 $this->setField('Filename', $newFilename);
400 }
401
402 403 404
405 protected function autosetFilename() {
406 $this->setField('Filename', $this->getRelativePath());
407 }
408
409 function setField( $field, $value ) {
410 parent::setField( $field, $value );
411 }
412
413 414 415
416 protected function updateLinks($old, $new) {
417 if(class_exists('Subsite')) Subsite::disable_subsite_filter(true);
418
419 if($tracking = $this->BackFileTracking()) {
420 foreach($tracking as $entry) {
421 if (($page = $entry->LinkedObject()) && $page->hasMethod('rewriteFileURL')) {
422 $page->rewriteFileURL($old,$new);
423 }
424 }
425 }
426
427 if(class_exists('Subsite')) Subsite::disable_subsite_filter(false);
428 }
429
430 function setParentID($parentID) {
431 $this->setField('ParentID', $parentID);
432
433 if($this->Name) $this->resetFilename();
434 else $this->autosetFilename();
435
436 return $this->getField('ParentID');
437 }
438
439 440 441 442 443 444
445 function getAbsoluteURL() {
446 return Director::absoluteBaseURL() . $this->getFilename();
447 }
448
449 450 451 452 453 454
455 function getURL() {
456 if(Object::has_extension('SiteTree', 'SiteTreeSubsites')){
457 return Director::absoluteURL('/') . $this->getFilename();
458 }
459 return Director::baseURL() . $this->getFilename();
460 }
461
462 463 464
465 function getLinkedURL() {
466 return "$this->Name";
467 }
468
469 function getFullPath() {
470 $baseFolder = Director::baseFolder();
471
472 if(strpos($this->getFilename(), $baseFolder) === 0) {
473
474 return $this->getFilename();
475 } else {
476
477 return Director::baseFolder() . '/' . $this->getFilename();
478 }
479 }
480
481 482 483 484 485
486 function getRelativePath() {
487 if($this->ParentID) {
488 $p = DataObject::get_by_id('Folder', $this->ParentID);
489 if($p && $p->exists()) return $p->getRelativePath() . $this->getField("Name");
490 else return ASSETS_DIR . "/" . $this->getField("Name");
491 } else if($this->getField("Name")) {
492 return ASSETS_DIR . "/" . $this->getField("Name");
493 } else {
494 return ASSETS_DIR;
495 }
496 }
497
498 function DeleteLink() {
499 return Director::absoluteBaseURL()."admin/assets/removefile/".$this->ID;
500 }
501
502 function getFilename() {
503 if($this->getField('Filename')) {
504 return $this->getField('Filename');
505 } else {
506 return ASSETS_DIR . '/';
507 }
508 }
509
510 function setFilename($val) {
511 $this->setField('Filename', $val);
512 $this->setField('Name', basename($val));
513 }
514
515 516 517 518 519
520 function getExtension() {
521 return self::get_file_extension($this->getField('Filename'));
522 }
523
524 525 526 527 528 529 530
531 public static function get_file_extension($filename) {
532 return strtolower(substr($filename,strrpos($filename,'.')+1));
533 }
534
535 536 537 538 539 540
541 function getFileType() {
542 $types = array(
543 'gif' => 'GIF image - good for diagrams',
544 'jpg' => 'JPEG image - good for photos',
545 'jpeg' => 'JPEG image - good for photos',
546 'png' => 'PNG image - good general-purpose format',
547 'ico' => 'Icon image',
548 'tiff' => 'Tagged image format',
549 'doc' => 'Word document',
550 'xls' => 'Excel spreadsheet',
551 'zip' => 'ZIP compressed file',
552 'gz' => 'GZIP compressed file',
553 'dmg' => 'Apple disk image',
554 'pdf' => 'Adobe Acrobat PDF file',
555 'mp3' => 'MP3 audio file',
556 'wav' => 'WAV audo file',
557 'avi' => 'AVI video file',
558 'mpg' => 'MPEG video file',
559 'mpeg' => 'MPEG video file',
560 'js' => 'Javascript file',
561 'css' => 'CSS file',
562 'html' => 'HTML file',
563 'htm' => 'HTML file'
564 );
565
566 $ext = $this->getExtension();
567
568 return isset($types[$ext]) ? $types[$ext] : 'unknown';
569 }
570
571 572 573
574 function getSize() {
575 $size = $this->getAbsoluteSize();
576
577 return ($size) ? self::format_size($size) : false;
578 }
579
580 public static function format_size($size) {
581 if($size < 1024) return $size . ' bytes';
582
583 if($size < 1024*1024) return round($size/1024) . ' KB';
584 if($size < 1024*1024*10) return (round(($size/1024)/1024*10)/10) . ' MB';
585 if($size < 1024*1024*1024) return round(($size/1024)/1024) . ' MB';
586 return round($size/(1024*1024*1024)*10)/10 . ' GB';
587 }
588
589 590 591 592
593 function getAbsoluteSize(){
594 if ($this->FileSize) {
595 return $this->FileSize;
596 }
597 if(file_exists($this->getFullPath())) {
598 $size = filesize($this->getFullPath());
599 return $size;
600 } else {
601 return 0;
602 }
603 }
604
605 606 607 608 609 610 611 612 613
614 public function instance_get($filter = "", $sort = "", $join = "", $limit="", $containerClass = "DataObjectSet", $having="") {
615 $query = $this->extendedSQL($filter, $sort, $limit, $join, $having);
616 $baseTable = reset($query->from);
617
618 $excludeDbColumns = array('Content');
619
620
621
622 $dataobject_select = array();
623 foreach($query->select as $item) {
624 625 626 627 628 629 630
631 $dataobject_select[] = $item;
632
633 }
634
635 $query->select = $dataobject_select;
636
637 $records = $query->execute();
638 $ret = $this->buildDataObjectSet($records, $containerClass);
639 if($ret) $ret->parseQueryLimit($query);
640
641 return $ret;
642 }
643
644 public function flushCache() {
645 parent::flushCache();
646
647 self::$cache_file_fields = null;
648 }
649
650 651 652 653 654
655 function fieldLabels($includerelations = true) {
656 $labels = parent::fieldLabels($includerelations);
657 $labels['Name'] = _t('File.Name', 'Name');
658 $labels['Title'] = _t('File.Title', 'Title');
659 $labels['Filename'] = _t('File.Filename', 'Filename');
660 $labels['Content'] = _t('File.Content', 'Content');
661 $labels['Sort'] = _t('File.Sort', 'Sort Order');
662
663 return $labels;
664 }
665
666 function validate() {
667 if(File::$apply_restrictions_to_admin || !Permission::check('ADMIN')) {
668 $extension = strtolower(pathinfo($this->Name, PATHINFO_EXTENSION));
669
670 if($extension && !in_array($extension, self::$allowed_extensions)) {
671 $exts = self::$allowed_extensions;
672 sort($exts);
673 $message = sprintf(
674 _t(
675 'File.INVALIDEXTENSION',
676 'Extension %s is not allowed (valid: %s)',
677 PR_MEDIUM,
678 'Argument 1: Invalid extension, Argument 2: Comma-separated list of valid extensions'
679 ),
680 $extension,
681 implode(', ',$exts)
682 );
683 return new ValidationResult(false, $message);
684 }
685 }
686 return new ValidationResult(true);
687 }
688
689 690 691 692 693 694 695 696 697
698 function uploadMetadataFields() {
699 $fields = new FieldSet();
700 $fields->push(new TextField('Title', $this->fieldLabel('Title')));
701 $this->extend('updateUploadMetadataFields', $fields);
702
703 return $fields;
704 }
705
706
707 function getUsageCount() {
708 $count = 0;
709
710 if ($this->hasMethod('BackFileTracking') && ($links = $this->BackFileTracking())) {
711 $count += $links->Count();
712 }
713 return $count;
714 }
715 }
716
717 ?>
718
[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the
Silverstripe Forums to ask development related questions.
-