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

Packages

  • auth
  • Booking
  • cart
    • shipping
    • steppedcheckout
  • Catalog
  • 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  * Manages uploads via HTML forms processed by PHP,
  4  * uploads to Silverstripe's default upload directory,
  5  * and either creates a new or uses an existing File-object
  6  * for syncing with the database.
  7  * 
  8  * @package sapphire
  9  * @subpackage filesystem
 10  * 
 11  * @todo Allow for non-database uploads
 12  */
 13 class Upload extends Controller {
 14     
 15     /**
 16      * A File object
 17      * @var File
 18      */
 19     protected $file;
 20     
 21     /**
 22      * An instance of Upload_Validator
 23      * @var Upload_Validator
 24      */
 25     protected $validator;
 26     
 27     /**
 28      * Information about the temporary file produced
 29      * by the PHP-runtime.
 30      *
 31      * @var array
 32      */
 33     protected $tmpFile;
 34     
 35     /**
 36      * Processing errors that can be evaluated,
 37      * e.g. by Form-validation.
 38      *
 39      * @var array
 40      */
 41     protected $errors = array();
 42     
 43     /**
 44      * A foldername relative to /assets,
 45      * where all uploaded files are stored by default.
 46      *
 47      * @var string
 48      */
 49     public static $uploads_folder = "Uploads"; 
 50     
 51     public function __construct() {
 52         parent::__construct();
 53         $this->validator = new Upload_Validator();
 54     }
 55     
 56     /**
 57      * Get current validator
 58      * 
 59      * @return object $validator
 60      */
 61     public function getValidator() {
 62         return $this->validator;
 63     }
 64     
 65     /**
 66      * Set a different instance than {@link Upload_Validator}
 67      * for this upload session.
 68      * 
 69      * @param object $validator
 70      */
 71     public function setValidator($validator) {
 72         $this->validator = $validator;
 73     }
 74     
 75     /**
 76      * Save an file passed from a form post into this object.
 77      * 
 78      * @param $tmpFile array Indexed array that PHP generated for every file it uploads.
 79      * @param $folderPath string Folder path relative to /assets
 80      * @return Boolean|string Either success or error-message.
 81      */
 82     function load($tmpFile, $folderPath = false) {
 83         $this->clearErrors();
 84         
 85         if(!$folderPath) $folderPath = self::$uploads_folder;
 86         
 87         if(!$this->file) $this->file = new File();
 88         
 89         if(!is_array($tmpFile)) {
 90             user_error("Upload::load() Not passed an array.  Most likely, the form hasn't got the right enctype", E_USER_ERROR);
 91         }
 92         
 93         if(!$tmpFile['size']) {
 94             $this->errors[] = _t('File.NOFILESIZE', 'Filesize is zero bytes.');
 95             return false;
 96         }
 97         
 98         $valid = $this->validate($tmpFile);
 99         if(!$valid) return false;
100         
101         // @TODO This puts a HUGE limitation on files especially when lots
102         // have been uploaded.
103         $base = Director::baseFolder();
104         $parentFolder = Folder::findOrMake($folderPath);
105 
106         // Create a folder for uploading.
107         if(!file_exists(ASSETS_PATH)){
108             mkdir(ASSETS_PATH, Filesystem::$folder_create_mask);
109         }
110         if(!file_exists(ASSETS_PATH . "/" . $folderPath)){
111             mkdir(ASSETS_PATH . "/" . $folderPath, Filesystem::$folder_create_mask);
112         }
113 
114         // Generate default filename
115         $fileName = Convert::rus2lat($tmpFile['name']);
116         $fileName = ereg_replace('^[_.-]+', '',$fileName);
117         $fileName = ereg_replace('[+\ ]+', '-',$fileName);
118         $fileName = ereg_replace('[^A-Za-z0-9._-]+','',$fileName);
119         $fileName = ereg_replace('-+', '-',$fileName);
120         $fileName = basename($fileName);
121 
122         $relativeFilePath = ASSETS_DIR . "/" . $folderPath . "/$fileName";
123         
124         // if filename already exists, version the filename (e.g. test.gif to test1.gif)
125         while(file_exists("$base/$relativeFilePath")) {
126             $i = isset($i) ? ($i+1) : 2;
127             $oldFilePath = $relativeFilePath;
128             // make sure archives retain valid extensions
129             if(substr($relativeFilePath, strlen($relativeFilePath) - strlen('.tar.gz')) == '.tar.gz' ||
130                 substr($relativeFilePath, strlen($relativeFilePath) - strlen('.tar.bz2')) == '.tar.bz2') {
131                     $relativeFilePath = ereg_replace('[0-9]*(\.tar\.[^.]+$)',$i . '\\1', $relativeFilePath);
132             } else if (strpos($relativeFilePath, '.') !== false) {
133                 $relativeFilePath = ereg_replace('[0-9]*(\.[^.]+$)',$i . '\\1', $relativeFilePath);
134             } else if (strpos($relativeFilePath, '_') !== false) {
135                 $relativeFilePath = ereg_replace('_([^_]+$)', '_'.$i, $relativeFilePath);
136             } else {
137                 $relativeFilePath .= "_$i";
138             }
139             if($oldFilePath == $relativeFilePath && $i > 2) user_error("Couldn't fix $relativeFilePath with $i tries", E_USER_ERROR);
140         }
141 
142         if(file_exists($tmpFile['tmp_name']) && move_uploaded_file($tmpFile['tmp_name'], "$base/$relativeFilePath")) {
143             $this->file->ParentID = $parentFolder->ID;
144             // This is to prevent it from trying to rename the file
145             $this->file->Name = basename($relativeFilePath);
146             $this->file->Title = basename($tmpFile['name']);
147             $this->file->write();
148             return true;
149         } else {
150             $this->errors[] = _t('File.NOFILESIZE', 'Filesize is zero bytes.');
151             return false;
152         }
153     }
154     
155     /**
156      * Load temporary PHP-upload into File-object.
157      *
158      * @param array $tmpFile
159      * @param File $file
160      * @return Boolean
161      */
162     public function loadIntoFile($tmpFile, $file, $folderPath = false) {
163         $this->file = $file;
164         return $this->load($tmpFile, $folderPath);
165     }
166     
167     /**
168      * Container for all validation on the file
169      * (e.g. size and extension restrictions).
170      * Is NOT connected to the {Validator} classes,
171      * please have a look at {FileField->validate()}
172      * for an example implementation of external validation.
173      *
174      * @param array $tmpFile
175      * @return boolean
176      */
177     public function validate($tmpFile) {
178         $validator = $this->validator;
179         $validator->setTmpFile($tmpFile);
180         $isValid = $validator->validate();
181         if($validator->getErrors()) {
182             $this->errors = array_merge($this->errors, $validator->getErrors());
183         }
184         return $isValid;
185     }
186     
187     /**
188      * Get file-object, either generated from {load()},
189      * or manually set.
190      *
191      * @return File
192      */
193     public function getFile() {
194         return $this->file;
195     }
196     
197     /**
198      * Set a file-object (similiar to {loadIntoFile()})
199      *
200      * @param File $file
201      */
202     public function setFile($file) {
203         $this->file = $file;
204     }
205     
206     /**
207      * Get maximum file size for all or specified file extension.
208      * 
209      * @deprecated 2.5 Please use Upload_Validator::getAllowedMaxFileSize() instead
210      * 
211      * @param string $ext
212      * @return int Filesize in bytes
213      */
214     public function getAllowedMaxFileSize($ext = null) {
215         user_error('Upload::getAllowedMaxFileSize() is deprecated. Please use Upload_Validator::getAllowedMaxFileSize() instead', E_USER_NOTICE);
216         return $this->validator->getAllowedMaxFileSize($ext);
217     }
218     
219     /**
220      * Set filesize maximums (in bytes).
221      * Automatically converts extensions to lowercase
222      * for easier matching.
223      * 
224      * Example: 
225      * <code>
226      * array('*' => 200, 'jpg' => 1000)
227      * </code>
228      *
229      * @deprecated 2.5 Please use Upload_Validator::setAllowedMaxFileSize() instead
230      *
231      * @param array|int $rules
232      */
233     public function setAllowedMaxFileSize($rules) {
234         user_error('Upload::setAllowedMaxFileSize() is deprecated. Please use Upload_Validator::setAllowedMaxFileSize() instead', E_USER_NOTICE);
235         $this->validator->setAllowedMaxFileSize($rules);
236     }
237     
238     /**
239      * @deprecated 2.5 Please use Upload_Validator::getAllowedExtensions() instead
240      * @return array
241      */
242     public function getAllowedExtensions() {
243         user_error('Upload::getAllowedExtensions() is deprecated. Please use Upload_Validator::getAllowedExtensions() instead', E_USER_NOTICE);
244         return $this->validator->getAllowedExtensions();
245     }
246     
247     /**
248      * @deprecated 2.5 Please use Upload_Validator::setAllowedExtensions() instead
249      * @param array $rules
250      */
251     public function setAllowedExtensions($rules) {
252         user_error('Upload::setAllowedExtensions() is deprecated. Please use Upload_Validator::setAllowedExtensions() instead', E_USER_NOTICE);
253         $this->validator->setAllowedExtensions($rules);
254     }
255     
256     /**
257      * Determines if the bytesize of an uploaded
258      * file is valid - can be defined on an
259      * extension-by-extension basis in {$allowedMaxFileSize}
260      * 
261      * @deprecated 2.5 Please use Upload_Validator::isValidExtension() instead
262      *
263      * @param array $tmpFile
264      * @return boolean
265      */
266     public function isValidSize($tmpFile) {
267         user_error('Upload::isValidSize() is deprecated. Please use Upload_Validator::isValidSize() instead', E_USER_NOTICE);
268         $validator = new Upload_Validator();
269         $validator->setTmpFile($tmpFile);
270         return $validator->isValidSize();
271     }
272     
273     /**
274      * Determines if the temporary file has a valid extension
275      * 
276      * @deprecated 2.5 Please use Upload_Validator::isValidExtension() instead
277      * 
278      * @param array $tmpFile
279      * @return boolean
280      */
281     public function isValidExtension($tmpFile) {
282         user_error('Upload::isValidExtension() is deprecated. Please use Upload_Validator::isValidExtension() instead', E_USER_NOTICE);
283         $validator = new Upload_Validator();
284         $validator->setTmpFile($tmpFile);
285         return $validator->isValidExtension();
286     }
287     
288     /**
289      * Clear out all errors (mostly set by {loadUploaded()})
290      */
291     public function clearErrors() {
292         $this->errors = array();
293     }
294     
295     /**
296      * Determines wether previous operations caused an error.
297      * 
298      * @return boolean
299      */
300     public function isError() {
301         return (count($this->errors));      
302     }
303     
304     /**
305      * Return all errors that occurred while processing so far
306      * (mostly set by {loadUploaded()})
307      *
308      * @return array
309      */
310     public function getErrors() {
311         return $this->errors;       
312     }
313     
314 }
315 
316 /**
317  * @package sapphire
318  * @subpackage filesystem
319  */
320 class Upload_Validator {
321 
322     /**
323      * Information about the temporary file produced
324      * by the PHP-runtime.
325      *
326      * @var array
327      */
328     protected $tmpFile;
329 
330     protected $errors = array();
331 
332     /**
333      * Restrict filesize for either all filetypes
334      * or a specific extension, with extension-name
335      * as array-key and the size-restriction in bytes as array-value.
336      * 
337      * @var array 
338      */
339     public $allowedMaxFileSize = array();
340 
341     /**
342      * @var array Collection of extensions. 
343      * Extension-names are treated case-insensitive.
344      * 
345      * Example:
346      * <code>
347      *  array("jpg","GIF")
348      * </code>
349      */
350     public $allowedExtensions = array();
351 
352     /**
353      * Return all errors that occurred while validating
354      * the temporary file.
355      *
356      * @return array
357      */
358     public function getErrors() {
359         return $this->errors;       
360     }
361 
362     /**
363      * Set information about temporary file produced by PHP.
364      * @param array $tmpFile
365      */
366     public function setTmpFile($tmpFile) {
367         $this->tmpFile = $tmpFile;
368     }
369 
370     /**
371      * Get maximum file size for all or specified file extension.
372      *
373      * @param string $ext
374      * @return int Filesize in bytes
375      */
376     public function getAllowedMaxFileSize($ext = null) {
377         $ext = strtolower($ext);
378         if(isset($ext) && isset($this->allowedMaxFileSize[$ext])) {
379             return $this->allowedMaxFileSize[$ext];   
380         } else {
381             return (isset($this->allowedMaxFileSize['*'])) ? $this->allowedMaxFileSize['*'] : false;
382         }
383     }
384     
385     /**
386      * Set filesize maximums (in bytes).
387      * Automatically converts extensions to lowercase
388      * for easier matching.
389      * 
390      * Example: 
391      * <code>
392      * array('*' => 200, 'jpg' => 1000)
393      * </code>
394      *
395      * @param array|int $rules
396      */
397     public function setAllowedMaxFileSize($rules) {
398         if(is_array($rules) && count($rules)) {
399             // make sure all extensions are lowercase
400             $rules = array_change_key_case($rules, CASE_LOWER);
401             $this->allowedMaxFileSize = $rules;
402         } elseif((int) $rules > 0) {
403             $this->allowedMaxFileSize['*'] = (int)$rules;
404         }
405     }
406     
407     /**
408      * @return array
409      */
410     public function getAllowedExtensions() {
411         return $this->allowedExtensions;
412     }
413     
414     /**
415      * @param array $rules
416      */
417     public function setAllowedExtensions($rules) {
418         if(!is_array($rules)) return false;
419         
420         // make sure all rules are lowercase
421         foreach($rules as &$rule) $rule = strtolower($rule);
422         
423         $this->allowedExtensions = $rules;
424     }
425     
426     /**
427      * Determines if the bytesize of an uploaded
428      * file is valid - can be defined on an
429      * extension-by-extension basis in {$allowedMaxFileSize}
430      *
431      * @return boolean
432      */
433     public function isValidSize() {
434         $pathInfo = pathinfo($this->tmpFile['name']);
435         $extension = isset($pathInfo['extension']) ? strtolower($pathInfo['extension']) : null;
436         $maxSize = $this->getAllowedMaxFileSize($extension);
437         return (!$this->tmpFile['size'] || !$maxSize || (int) $this->tmpFile['size'] < $maxSize);
438     }
439     
440     /**
441      * Determines if the temporary file has a valid extension
442      * An empty string in the validation map indicates files without an extension.
443      * @return boolean
444      */
445     public function isValidExtension() {
446         $pathInfo = pathinfo($this->tmpFile['name']);
447         
448         // Special case for filenames without an extension
449         if(!isset($pathInfo['extension'])) {
450             return in_array('', $this->allowedExtensions, true);
451         } else {
452             return (!count($this->allowedExtensions) || in_array(strtolower($pathInfo['extension']), $this->allowedExtensions));
453         }
454     }   
455     
456     /**
457      * Run through the rules for this validator checking against
458      * the temporary file set by {@link setTmpFile()} to see if
459      * the file is deemed valid or not.
460      * 
461      * @return boolean
462      */
463     public function validate() {
464         // we don't validate for empty upload fields yet
465         if(!isset($this->tmpFile['name']) || empty($this->tmpFile['name'])) return true;
466 
467                 if(isset($this->tmpFile['tmp_name']) && !ini_get("open_basedir") && !is_uploaded_file($this->tmpFile['tmp_name']) && !SapphireTest::is_running_test()) { 
468             $this->errors[] = _t('File.NOVALIDUPLOAD', 'File is not a valid upload');
469             return false;
470         }
471 
472         $pathInfo = pathinfo($this->tmpFile['name']);
473         // filesize validation
474         if(!$this->isValidSize()) {
475             $ext = (isset($pathInfo['extension'])) ? $pathInfo['extension'] : '';
476             $arg = File::format_size($this->getAllowedMaxFileSize($ext));
477             $this->errors[] = sprintf(
478                 _t(
479                     'File.TOOLARGE', 
480                     'Filesize is too large, maximum %s allowed.',
481                     PR_MEDIUM,
482                     'Argument 1: Filesize (e.g. 1MB)'
483                 ),
484                 $arg
485             );
486             return false;
487         }
488 
489         // extension validation
490         if(!$this->isValidExtension()) {
491             $this->errors[] = sprintf(
492                 _t(
493                     'File.INVALIDEXTENSION', 
494                     'Extension %s is not allowed (valid: %s)',
495                     PR_MEDIUM,
496                     'Argument 1: Comma-separated list of valid extensions'
497                 ),
498                 $pathInfo['extension'],
499                 implode(',', $this->allowedExtensions)
500             );
501             return false;
502         }
503         
504         return true;
505     }
506 
507 }
[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.1 API Docs API documentation generated by ApiGen 2.8.0