07.26
php
saved
The_Cake_Eater
Note
Version 1.4.1 of upload behaviour
Version 1.4.1 of upload behaviour
- <?php
- /**
- * Improved Upload Behaviour
- *
- * This behaviour is based on Chris Partridge's upload behaviour (http://bin.cakephp.org/saved/17539)
- * @author Chris Partridge http://blog.chris-partridge.info/2007/03/16/uploadbehavior-is-here/
- * @author Tane Piper (digitalspaghetti@gmail.com)
- * @author Tijs Teulings
- * @author The_Cake_Eater
- *
- * @link http://www.digitalspaghetti.me.uk
- *
- * @todo fields dir, mimetype, filesize will break if more than one fields are used for upload
- * @todo option to only overwrite files that were referenced by the same field before
- * @todo add naming mode 'modelid' where the file name is model->id + extension ==> is tricky since the id is not available in beforeSave for new records
- *
- * @version 1.4
- *
- * How to use
- * ==========
- * 1) Download this behaviour and place it in your models/behaviours/upload.php
- * 2) Insert the following SQL into your database. This is a basic model you can expand on:
- * CREATE TABLE `images` (
- * `id` int(8) unsigned NOT NULL auto_increment,
- * `filename` varchar(255) default NULL,
- * `dir` varchar(255) default NULL,
- * `mimetype` varchar(255) NULL,
- * `filesize` int(11) unsigned default NULL,
- * `created` datetime default NULL,
- * `modified` datetime default NULL,
- * PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
- * 3) In your model that you want to have the upload behavior work, place the below code. This example is for an Image model:
- *
- * var $actsAs = array('Upload' => array(
- * 'filename' => array(
- * 'dir' => 'files/images',
- * 'overwrite_existing' => false,
- * 'delete_obsolete_files' => true,
- * 'naming' => 'filename',
- * 'naming_if_exists' => 'increment',
- * 'create_directory' => false,
- * 'allowed_mime' => array('image/jpeg', 'image/pjpeg', 'image/gif', 'image/png', 'image/x-png'),
- * 'allowed_ext' => array('.jpg', '.jpeg', '.png', '.gif'),
- * 'thumbsizes' => array(
- * 'small' => array('width'=>100, 'height'=>100),
- * 'medium' => array('width'=>220, 'height'=>220),
- * 'large' => array('width'=>800, 'height'=>600)
- * )
- * )
- * ));
- *
- * The above code will save the uploaded file's name in the 'filename' field in database,
- * it will not overwrite existing files, instead it will create a random filename if
- * it already exists.
- * Allowed Mimetypes and extentions should be pretty explanitory
- * For thumbnails, when the file is uploaded, it will create 3 thumbnail sizes and prepend the name
- * to the thumbfiles (i.e. image_001.jpg will produced thumb.small.image_001.jpg, thumb.medium.image_001.jpg, etc)
- *
- * 4) Create your upload view, make sure it's a multipart/form-data form, and the filename field is of type $form->file
- * 5) Make sure your directory is at least CHMOD 775, also check your php.ini MAX_UPLOAD_SIZE is enough to support the filesizes you are uploading
- *
- * Version History
- * ===============
- * 1.4.1 26/7/2007 The_Cake_Eater http://bin.cakephp.org/saved/22189
- * - replacing files with files having the same name failed
- *
- * 1.4 25/7/2007 The_Cake_Eater http://bin.cakephp.org/saved/22189
- * - fixed the merging of defauilt options with given options
- * + introduced 'naming' option specifying how the name is generatd (filename, random)
- * + introduced 'naming_if_exists' option specifying how alternate names are generated (random, increment)
- * + introduced 'delete_obsolete_files' option specifying if files that are no longer referenced by a field (replaced with a new file)
- * or files referenced by a deleted record are deleted
- * * refactoring of parts of the code
- * - some minor fixes
- * - some performance improvements
- *
- * 1.3 12/7/2007 Tijs Teulings (tijs@automatique.nl) http://bin.cakephp.org/saved/21813
- * - default quality to 'high'
- * - fixed proportional resizing
- * - fixed permissions for both thumbs and originals
- *
- * 1.2 22/6/2007 Tijs Teulings http://bin.cakephp.org/saved/21697
- * * made filename checks case insensitive
- * * removed thumb component dependency
- * - fixes permissions for original file now
- * - fixed handling of file extensions (supports filenames with dots in them now)
- * * rounding new sizes for more reliable results
- *
- * 1.1
- * * Improved Image scaling code
- * - Fixed check to see if file exists and rename file to unique name
- * * Improved model actsAs to allow more thumbnail sizes
- *
- * 1.0
- * + Initial release with thumbnail code.
- */
- uses('folder');
- uses('file');
- class UploadBehavior extends ModelBehavior
- {
- 'delete_obsolete_files' => true,
- 'overwrite_existing' => false,
- 'naming' => 'filename', // random, filename
- 'naming_if_exists' => 'random', // random, increment
- 'create_directory' => true,
- );
- /*
- * temporary registry of obsolete file names, required since obsolete files are identified in beforeSave and beforeDelete
- * but are removed in afterSave and afterDelete
- */
- /*
- * parses the configuration array and sets up the $this->settings property
- */
- {
- $folder = &new Folder;
- // iterate over all upload enabled fields
- foreach($config as $field => $options)
- {
- // Check if given field exists
- if(!$model->hasField($field)) {
- continue;
- }
- // Merge given options with defaults
- $config[$field] = $options;
- // FIXME this looks odd
- // Generate temporary directory if none provided
- $options['dir'] = 'img' . DS . 'uploads' . DS . $model->name;
- }
- // Check if directory exists and create it recursively if required
- if($options['create_directory'] && !$folder->create($options['dir'])) {
- continue;
- }
- }
- // Check if directory is writable
- continue;
- }
- // Check that the given directory does not have a DS on the end
- }
- }
- $this->settings[$model->name] = $config;
- }
- function beforeSave(&$model)
- {
- // return if no fields are configured for file upload
- foreach($this->settings[$model->name] as $field=>$options)
- {
- {
- // Check for upload
- {
- continue;
- }
- // Check for error
- if($model->data[$model->name][$field]['error'] > 0)
- {
- continue;
- }
- // Fix the file name
- $model->data[$model->name][$field]['name'] = $this->__getFilteredFileName($model->data[$model->name][$field]['name']);
- // Check mime type
- {
- continue;
- }
- // Check if file extension is allowed
- {
- $extension = false;
- foreach($options['allowed_ext'] as $allowed_ext)
- {
- if(substr(strtolower($model->data[$model->name][$field]['name']),-strlen($allowed_ext)) == $allowed_ext)
- {
- $extension = $allowed_ext;
- break;
- }
- }
- if(!$extension)
- {
- continue;
- }
- }
- $model->data[$model->name][$field]['name'] = $this->__getTargetFileName($options, $model, $field, $extension);
- $saveAs = $options['dir'] . DS . $model->data[$model->name][$field]['name'];
- // Attempt to move uploaded file
- {
- // if moving fails clear the variables and move on to the next field
- continue;
- }
- // Create thumbnail of uploaded image
- $this->__createThumbs($options, $model, $field, $saveAs);
- // Attempt to set correct file permissions
- {
- // if chmod fails clear the variable and move on to the next field
- continue;
- }
- // Update model data
- // FIXME dir, mimetype, filesize, dir are the same for multiple upload field
- $model->data[$model->name]['dir'] = $options['dir'];
- $model->data[$model->name]['mimetype'] = $model->data[$model->name][$field]['type'];
- $model->data[$model->name]['filesize'] = $model->data[$model->name][$field]['size'];
- $model->data[$model->name][$field] = $model->data[$model->name][$field]['name'];
- if($options['delete_obsolete_files'] && $model->id)
- {
- // retrieve the previous file associated with the current field
- $oldFile = $this->__getFieldValueFromDB($model, $field);
- if($oldFile != $model->data[$model->name][$field])
- {
- // register the file for garbage collection after successful saving of the model
- $key = $model->name.'#'.$model->id;
- $this->__obsoleteFiles[$key][$field] = $this->__getFieldValueFromDB($model, $field);
- }
- }
- }
- }
- }
- function onError(&$model, $error)
- {
- // if an error occured during save operations precautionary unregister files marked as obsolete related to the current model
- if($model->id)
- {
- $key = $model->name.'#'.$model->id;
- }
- }
- function afterSave(&$model, $created)
- {
- // check if any upload fields are defined
- if(!$created) $this->__deleteObsoleteFiles($model);
- }
- function afterDelete(&$model)
- {
- // return if no fields are configured for file upload
- $this->__deleteObsoleteFiles($model);
- }
- function beforeDelete(&$model)
- {
- // return if no fields are configured for file upload
- $fieldValues = $this->__getFieldValuesFromDB($model, $fieldNames);
- foreach($this->settings[$model->name] as $fieldName=>$options)
- {
- if($options['delete_obsolete_files'])
- {
- $dir = $options['dir'];
- $fileName = $fieldValues[$fieldName];
- // register the file for garbage collection after successful saving of the model
- $key = $model->name.'#'.$model->id;
- $this->__obsoleteFiles[$key][$fieldName] = $fileName;
- }
- }
- return true;
- }
- /**
- * creates a thumbnail image
- */
- function __createThumb($name, $filename, $new_w, $new_h)
- {
- $ext = $path_info['extension'];
- {
- $src_img = imagecreatefromjpeg($name);
- }
- {
- $src_img = imagecreatefrompng($name);
- }
- $old_x = imagesx($src_img);
- $old_y = imagesy($src_img);
- $newSizes = $this->__proportionalResize($old_x, $old_y, $new_w, $new_h);
- $thumb_w = $newSizes[0];
- $thumb_h = $newSizes[1];
- $dst_img = imagecreatetruecolor($thumb_w, $thumb_h);
- imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $thumb_w, $thumb_h, $old_x, $old_y);
- {
- imagepng($dst_img, $filename, 0); // no compression
- } else {
- imagejpeg($dst_img, $filename, 100); // 100% quality
- }
- imagedestroy($dst_img);
- imagedestroy($src_img);
- }
- function __createThumbs(&$options, &$model, $fieldName, $saveAs)
- {
- // This is hard-coded to only support JPEG + PNG at this time
- // Code unable to handle other formats
- {
- foreach ($options['thumbsizes'] as $key => $value)
- {
- $this->__createThumb($saveAs, $options['dir'] . DS . 'thumb.' . $key . '.' . $model->data[$model->name][$fieldName]['name'], $value['width'], $value['height']);
- }
- }
- }
- function __deleteObsoleteFiles(&$model)
- {
- // check if obsolete files for the current model exist
- $key = $model->name.'#'.$model->id;
- // get a handle to the obsolete files
- $obsoleteFilesByFieldName = $this->__obsoleteFiles[$key];
- foreach($this->settings[$model->name] as $fieldName=>$options)
- {
- $obsoleteFileForField = $obsoleteFilesByFieldName[$fieldName];
- $dir = $options['dir'];
- foreach ($options['thumbsizes'] as $key => $value)
- {
- }
- }
- }
- function __getFilteredFileName($filename)
- {
- $filtered_filename = '';
- '/\s/', // Whitespace
- '/\&/', // Ampersand
- '/\+/' // Plus
- );
- '_', // Whitespace
- 'and', // Ampersand
- 'plus' // Plus
- );
- for ($i=0;$i<strlen($filename);$i++)
- {
- {
- $filtered_filename .= $current_char;
- }
- }
- return $filtered_filename;
- }
- function __getFieldValueFromDB(&$model, $fieldName)
- {
- return $rs[$model->name][$fieldName];
- }
- function __getFieldValuesFromDB(&$model, $fieldNames)
- {
- return $rs[$model->name];
- }
- function __getTargetFileName(&$options, &$model, $field, $extension)
- {
- // Calculate a new name if a naming mode other than 'filename' has been selected
- switch($options['naming'])
- {
- }
- // Calculate final save path
- $saveAs = $options['dir'] . DS . $fileName;
- // Check if file with the desired name exists already
- {
- // if overwrite is enabled and removing the existing file succeeded, then return the file name directly
- // find a unique filename by appending a counter to the base name
- if($options['naming_if_exists']=='increment')
- {
- $inc=1;
- do
- {
- $fileName = $basename . '_' . $inc . $extension;
- $saveAs = $options['dir'] . DS . $candidate;
- $inc++;
- }
- return $fileName;
- }
- // find a unique file name by using a UID as the base name
- else
- {
- do
- {
- $saveAs = $options['dir'] . DS . $fileName;
- }
- }
- }
- return $fileName;
- }
- function __proportionalResize($old_width, $old_height, $new_width = false, $new_height = false)
- {
- if (($new_width === false) && ($new_height === false)) return false;
- $old_aspect_ratio = $old_width / $old_height;
- if ($new_width === false) {
- $new_width = $new_height * $old_aspect_ratio;
- } elseif ($new_height === false) {
- $new_height = $new_width / $old_aspect_ratio;
- }
- $new_aspect_ratio = $new_width / $new_height;
- if ($new_aspect_ratio == $old_aspect_ratio) {
- // great, done
- } elseif ($new_aspect_ratio < $old_aspect_ratio) {
- // limited by width
- $new_height = $new_width / $old_aspect_ratio;
- } elseif ($new_aspect_ratio > $old_aspect_ratio) {
- // limited by height
- $new_width = $new_height * $old_aspect_ratio;
- }
- }
- }
- ?>
Parsed in 0.716 seconds, using GeSHi 1.0.7.14