Webylon 3.2 API Docs
  • Package
  • Class
  • Tree
  • Deprecated
  • Download
Version: current
  • 3.2
  • 3.1

Packages

  • 1c
    • exchange
      • catalog
  • auth
  • Booking
  • building
    • company
  • cart
    • shipping
    • steppedcheckout
  • Catalog
    • monument
  • cms
    • assets
    • batchaction
    • batchactions
    • bulkloading
    • comments
    • content
    • core
    • export
    • newsletter
    • publishers
    • reports
    • security
    • tasks
  • Dashboard
  • DataObjectManager
  • event
  • faq
  • forms
    • actions
    • core
    • fields-basic
    • fields-dataless
    • fields-datetime
    • fields-files
    • fields-formatted
    • fields-formattedinput
    • fields-relational
    • fields-structural
    • transformations
    • validators
  • googlesitemaps
  • guestbook
  • installer
  • newsletter
  • None
  • photo
    • gallery
  • PHP
  • polls
  • recaptcha
  • sapphire
    • api
    • bulkloading
    • control
    • core
    • cron
    • dev
    • email
    • fields-formattedinput
    • filesystem
    • formatters
    • forms
    • i18n
    • integration
    • misc
    • model
    • parsers
    • search
    • security
    • tasks
    • testing
    • tools
    • validation
    • view
    • widgets
  • seo
    • open
      • graph
  • sfDateTimePlugin
  • spamprotection
  • stealth
    • captha
  • subsites
  • userform
    • pagetypes
  • userforms
  • webylon
  • widgets

Classes

  • Archive
  • File
  • Filesystem
  • FlushGeneratedImagesTask
  • FLV
  • Folder
  • Folder_UnusedAssetsField
  • GD
  • Image
  • Image_Cached
  • MediawebPage_Image
  • MP3
  • SaveFileSizeTask
  • TarballArchive
  • Upload
  • Upload_Validator
  • VideoFile
  1 <?php
  2 /**
  3  * This class handles the representation of a File within Sapphire
  4  * Note: The files are stored in the assets/ directory, but sapphire
  5  * looks at the db object to gather information about a file such as URL
  6  * It then uses this for all processing functions (like image manipulation).
  7  * 
  8  * @package sapphire
  9  * @subpackage filesystem
 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", // hide File/Folder from CMS, to avoid lags, when there are a lot
 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      * @see Upload->allowedExtensions
 52      * @var array
 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      * If this is true, then restrictions set in $allowed_max_file_size and
 68      * $allowed_extensions will be applied to users with admin privileges as
 69      * well.
 70      */
 71     public static $apply_restrictions_to_admin = true;
 72 
 73     
 74     /**
 75      * Cached result of a "SHOW FIELDS" call
 76      * in instance_get() for performance reasons.
 77      *
 78      * @var array
 79      */
 80     protected static $cache_file_fields = null;
 81     
 82     /**
 83      * Find a File object by the given filename.
 84      * @return mixed null if not found, File object of found file
 85      */
 86     static function find($filename) {
 87         // Get the base file if $filename points to a resampled file
 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     // Used by AssetTableField
118     function BackLinkTrackingCount() {
119         return $this->getUsageCount();
120     }
121 
122     /**
123      * Event handler called before deleting from the database.
124      * You can overload this to clean up or otherwise process data before delete this
125      * record.  Don't forget to call parent::onBeforeDelete(), though!
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          /* do it in FileDataObjectTrackingDecorator.php
140         if($tracking = $this->BackFileTracking()) {
141             foreach($tracking as $entry) {
142                 if (($page = $entry->LinkedObject()) && $page->is_a('SiteTree')) {
143                     $origStage = Versioned::current_stage();
144 
145                     // This will syncLinkTracking on draft
146                     Versioned::reading_stage('Stage');
147                     if (!$page->IsDeletedFromStage) {
148                         $page->write();
149                     }                   
150 
151                     // This will syncLinkTracking on published
152                     Versioned::reading_stage('Live');
153                     if ($page->ExistsOnLive) {
154                         $page->write();
155                     }
156                 }
157             }
158             Versioned::reading_stage($origStage);
159         }
160         */
161     }
162     
163     /**
164      * @todo Enforce on filesystem URL level via mod_rewrite
165      * 
166      * @return boolean
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      * Returns true if the following conditions are met:
179      * - CMS_ACCESS_AssetAdmin
180      * 
181      * @todo Decouple from CMS view access
182      * 
183      * @return boolean
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      * @return boolean
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      * @return boolean
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      * Return the URL of an icon for the file type
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      * Should be called after the file was uploaded 
277      */ 
278     function onAfterUpload() {
279         $this->extend('onAfterUpload');
280     }
281 
282     /**
283      * Delete the database record (recursively for folders) without touching the filesystem
284      */
285     public function deleteDatabaseOnly() {
286         if(is_numeric($this->ID)) DB::query("DELETE FROM \"File\" WHERE \"ID\" = $this->ID");
287     }
288 
289     /**
290      * Event handler called before deleting from the database.
291      * You can overload this to clean up or otherwise process data before delete this
292      * record.  Don't forget to call parent::onBeforeWrite(), though!
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      * Collate selected descendants of this page.
303      * $condition will be evaluated on each descendant, and if it is succeeds, that item will be added
304      * to the $collator array.
305      * @param condition The PHP condition to be evaluated.  The page will be called $item
306      * @param collator An array, passed by reference, to collect all of the matching descendants.
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      * Setter function for Name.
320      * Automatically sets a default title.
321      */
322     function setName($name) {
323         $oldName = $this->Name;
324 
325         // It can't be blank
326         if(!$name) $name = $this->Title;
327 
328         // Fix illegal characters
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         // We might have just turned it blank, so check again.
339         if(!$name) $name = 'new-folder';
340 
341         // If it's changed, check for duplicates
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      * Change a filename, moving the file if appropriate.
373      * @param $renamePhysicalFile Set this to false if you don't want to rename the physical file. Used when calling resetFilename() on the children of a folder.
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                 // Error checking
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             // If the old file doesn't exist, maybe it's already been renamed.
396             if(file_exists(Director::getAbsFile($newFilename))) $this->updateLinks($oldFilename, $newFilename);
397         }
398 
399         $this->setField('Filename', $newFilename);
400     }
401 
402     /**
403      * Set the Filename field without manipulating the filesystem.
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      * Rewrite links to the $old file to now point to the $new file
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      * Gets the absolute URL accessible through the web.
441      * 
442      * @uses Director::absoluteBaseURL()
443      * @return string
444      */
445     function getAbsoluteURL() {
446         return Director::absoluteBaseURL() . $this->getFilename();
447     }
448     
449     /**
450      * Gets the relative URL accessible through the web.
451      * 
452      * @uses Director::baseURL()
453      * @return string
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      * Return the last 50 characters of the URL
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             // if path is absolute already, just return
474             return $this->getFilename();
475         } else {
476             // otherwise assume silverstripe-basefolder
477             return Director::baseFolder() . '/' . $this->getFilename();
478         }
479     }
480 
481     /**
482      * Returns 
483      * 
484      * @return String
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      * FIXME This overrides getExtension() in DataObject, but it does something completely different.
517      * This should be renamed to getFileExtension(), but has not been yet as it may break
518      * legacy code.
519      */
520     function getExtension() {
521         return self::get_file_extension($this->getField('Filename'));
522     }
523     
524     /**
525      * Gets the extension of a filepath or filename,
526      * by stripping away everything before the last "dot".
527      *
528      * @param string $filename
529      * @return string
530      */
531     public static function get_file_extension($filename) {
532         return strtolower(substr($filename,strrpos($filename,'.')+1));
533     }
534     
535     /**
536      * Return the type of file for the given extension
537      * on the current file name.
538      *
539      * @return string
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      * Returns the size of the file type in an appropriate format.
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 //      if($size < 1024*10) return (round($size/1024*10)/10). ' KB';
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      * Return file size in bytes.
591      * @return int
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      * We've overridden the DataObject::get function for File so that the very large content field
607      * is excluded!
608      *
609      * @todo Admittedly this is a bit of a hack; but we need a way of ensuring that large
610      * TEXT fields don't stuff things up for the rest of us.  Perhaps a separate search table would
611      * be a better way of approaching this?
612      * @deprecated alternative_instance_get()
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         // Work out which columns we're actually going to select
621         // In short, we select everything except File.Content
622         $dataobject_select = array();
623         foreach($query->select as $item) {
624             /*
625             if($item == "\"File\".*") {
626                 $fileColumns = DB::query("SHOW FIELDS IN \"File\"")->column();
627                 $columnsToAdd = array_diff($fileColumns, $excludeDbColumns);
628                 foreach($columnsToAdd as $otherItem) $dataobject_select[] = '"File".' . $otherItem;
629             } else {
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      * @param boolean $includerelations a boolean value to indicate if the labels returned include relation fields
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      * Allow custom fields for uploads in {@link AssetAdmin}.
691      * Similar to {@link getCMSFields()}, but a more restricted
692      * set of fields which can be reliably set on any file type.
693      * 
694      * Needs to be enabled through {@link AssetAdmin::$metadata_upload_enabled}
695      * 
696      * @return FieldSet
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     // Quantity of pages on which this file is used
707     function getUsageCount() {
708         $count = 0;
709         // from FileDataObjectTrackingDecorator
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. -
Webylon 3.2 API Docs API documentation generated by ApiGen 2.8.0