Created
February 11, 2013 01:37
-
-
Save awtprod/4751891 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class Photo extends AppModel { | |
var $name = 'Photo'; | |
var $actsAs = array( | |
'Upload.Upload' => array( | |
'image'=> array( | |
'fields' => array( | |
'dir' => 'photo_dir', | |
'customName' => '{!getName}' | |
) | |
) | |
) | |
); | |
public function getName($filename = ''){ | |
return md5($filename); | |
} | |
} | |
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Upload behavior | |
* | |
* Enables users to easily add file uploading and necessary validation rules | |
* | |
* PHP versions 4 and 5 | |
* | |
* Copyright 2010, Jose Diaz-Gonzalez | |
* | |
* Licensed under The MIT License | |
* Redistributions of files must retain the above copyright notice. | |
* | |
* @copyright Copyright 2010, Jose Diaz-Gonzalez | |
* @package upload | |
* @subpackage upload.models.behaviors | |
* @link http://github.com/josegonzalez/upload | |
* @license MIT License (http://www.opensource.org/licenses/mit-license.php) | |
*/ | |
App::uses('Folder', 'Utility'); | |
App::uses('UploadException', 'Upload.Lib/Error/Exception'); | |
class UploadBehavior extends ModelBehavior { | |
public $defaults = array( | |
'rootDir' => null, | |
'pathMethod' => 'flat', | |
'path' => '{ROOT}webroot{DS}files{DS}{model}{DS}{field}{DS}', | |
'fields' => array('dir' => 'dir', 'type' => 'type', 'size' => 'size'), | |
'mimetypes' => array(), | |
'extensions' => array(), | |
'maxSize' => 2097152, | |
'minSize' => 8, | |
'maxHeight' => 0, | |
'minHeight' => 0, | |
'maxWidth' => 0, | |
'minWidth' => 0, | |
'thumbnails' => true, | |
'thumbnailMethod' => 'php', | |
'thumbnailName' => null, | |
'thumbnailPath' => null, | |
'thumbnailPrefixStyle'=> true, | |
'thumbnailQuality' => 75, | |
'thumbnailSizes' => array(), | |
'thumbnailType' => false, | |
'deleteOnUpdate' => false, | |
'mediaThumbnailType'=> 'png', | |
'saveDir' => true, | |
'customName' => true | |
); | |
protected $_imageMimetypes = array( | |
'image/bmp', | |
'image/gif', | |
'image/jpeg', | |
'image/pjpeg', | |
'image/png', | |
'image/vnd.microsoft.icon', | |
'image/x-icon', | |
); | |
protected $_mediaMimetypes = array( | |
'application/pdf', | |
'application/postscript', | |
); | |
protected $_pathMethods = array('flat', 'primaryKey', 'random', 'randomCombined'); | |
protected $_resizeMethods = array('imagick', 'php'); | |
private $__filesToRemove = array(); | |
protected $_removingOnly = array(); | |
/** | |
* Runtime configuration for this behavior | |
* | |
* @var array | |
**/ | |
public $runtime; | |
/** | |
* Initiate Upload behavior | |
* | |
* @param object $model instance of model | |
* @param array $config array of configuration settings. | |
* @return void | |
* @access public | |
*/ | |
public function setup(Model $model, $config = array()) { | |
if (isset($this->settings[$model->alias])) return; | |
$this->settings[$model->alias] = array(); | |
foreach ($config as $field => $options) { | |
$this->_setupField($model, $field, $options); | |
} | |
} | |
/** | |
* Setup a particular upload field | |
* | |
* @param AppModel $model Model instance | |
* @param string $field Name of field being modified | |
* @param array $options array of configuration settings for a field | |
* @return void | |
* @author Jose Diaz-Gonzalez | |
*/ | |
public function _setupField(Model $model, $field, $options) { | |
if (is_int($field)) { | |
$field = $options; | |
$options = array(); | |
} | |
$this->defaults['rootDir'] = ROOT . DS . APP_DIR . DS; | |
if (!isset($this->settings[$model->alias][$field])) { | |
$options = array_merge($this->defaults, (array) $options); | |
// HACK: Remove me in next major version | |
if (!empty($options['thumbsizes'])) { | |
$options['thumbnailSizes'] = $options['thumbsizes']; | |
} | |
if (!empty($options['prefixStyle'])) { | |
$options['thumbnailPrefixStyle'] = $options['prefixStyle']; | |
} | |
// ENDHACK | |
$options['fields'] += $this->defaults['fields']; | |
if ($options['rootDir'] === null) { | |
$options['rootDir'] = $this->defaults['rootDir']; | |
} | |
if ($options['thumbnailName'] === null) { | |
if ($options['thumbnailPrefixStyle']) { | |
$options['thumbnailName'] = '{size}_{filename}'; | |
} else { | |
$options['thumbnailName'] = '{filename}_{size}'; | |
} | |
} | |
if ($options['thumbnailPath'] === null) { | |
$options['thumbnailPath'] = Folder::slashTerm($this->_path($model, $field, array( | |
'isThumbnail' => true, | |
'path' => $options['path'], | |
'rootDir' => $options['rootDir'] | |
))); | |
} else { | |
$options['thumbnailPath'] = Folder::slashTerm($this->_path($model, $field, array( | |
'isThumbnail' => true, | |
'path' => $options['thumbnailPath'], | |
'rootDir' => $options['rootDir'] | |
))); | |
} | |
$options['path'] = Folder::slashTerm($this->_path($model, $field, array( | |
'isThumbnail' => false, | |
'path' => $options['path'], | |
'rootDir' => $options['rootDir'] | |
))); | |
if (!in_array($options['thumbnailMethod'], $this->_resizeMethods)) { | |
$options['thumbnailMethod'] = 'imagick'; | |
} | |
if (!in_array($options['pathMethod'], $this->_pathMethods)) { | |
$options['pathMethod'] = 'flat'; | |
} | |
$options['pathMethod'] = '_getPath' . Inflector::camelize($options['pathMethod']); | |
$options['thumbnailMethod'] = '_resize' . Inflector::camelize($options['thumbnailMethod']); | |
$this->settings[$model->alias][$field] = $options; | |
} | |
} | |
/** | |
* Convenience method for configuring UploadBehavior settings | |
* | |
* @param AppModel $model Model instance | |
* @param string $field Name of field being modified | |
* @param mixed $one A string or an array of data. | |
* @param mixed $two Value in case $one is a string (which then works as the key). | |
* Unused if $one is an associative array, otherwise serves as the values to $one's keys. | |
* @return void | |
*/ | |
public function uploadSettings(Model $model, $field, $one, $two = null) { | |
if (empty($this->settings[$model->alias][$field])) { | |
$this->_setupField($model, $field, array()); | |
} | |
$data = array(); | |
if (is_array($one)) { | |
if (is_array($two)) { | |
$data = array_combine($one, $two); | |
} else { | |
$data = $one; | |
} | |
} else { | |
$data = array($one => $two); | |
} | |
$this->settings[$model->alias][$field] = $data + $this->settings[$model->alias][$field]; | |
} | |
/** | |
* Before save method. Called before all saves | |
* | |
* Handles setup of file uploads | |
* | |
* @param AppModel $model Model instance | |
* @return boolean | |
*/ | |
public function beforeSave(Model $model) { | |
$this->_removingOnly = array(); | |
foreach ($this->settings[$model->alias] as $field => $options) { | |
if (!isset($model->data[$model->alias][$field])) continue; | |
if (!is_array($model->data[$model->alias][$field])) continue; | |
$this->runtime[$model->alias][$field] = $model->data[$model->alias][$field]; | |
$removing = isset($model->data[$model->alias][$field]['remove']); | |
if ($removing || ($this->settings[$model->alias][$field]['deleteOnUpdate'] | |
&& isset($model->data[$model->alias][$field]['name']) | |
&& strlen($model->data[$model->alias][$field]['name']))) { | |
// We're updating the file, remove old versions | |
if (!empty($model->id)) { | |
$data = $model->find('first', array( | |
'conditions' => array("{$model->alias}.{$model->primaryKey}" => $model->id), | |
'contain' => false, | |
'recursive' => -1, | |
)); | |
$this->_prepareFilesForDeletion($model, $field, $data, $options); | |
} | |
if ($removing) { | |
$model->data[$model->alias] = array( | |
$field => null, | |
$options['fields']['type'] => null, | |
$options['fields']['size'] => null, | |
$options['fields']['dir'] => null, | |
); | |
$this->_removingOnly[$field] = true; | |
continue; | |
} else { | |
$model->data[$model->alias][$field] = array( | |
$field => null, | |
$options['fields']['type'] => null, | |
$options['fields']['size'] => null, | |
); | |
} | |
} elseif (!isset($model->data[$model->alias][$field]['name']) | |
|| !strlen($model->data[$model->alias][$field]['name'])) { | |
// if field is empty, don't delete/nullify existing file | |
unset($model->data[$model->alias][$field]); | |
continue; | |
} | |
$model->data[$model->alias] = array_merge($model->data[$model->alias], array( | |
$field => $this->runtime[$model->alias][$field]['name'], | |
$options['fields']['type'] => $this->runtime[$model->alias][$field]['type'], | |
$options['fields']['size'] => $this->runtime[$model->alias][$field]['size'] | |
)); | |
} | |
return true; | |
} | |
public function afterSave(Model $model, $created) { | |
$temp = array($model->alias => array()); | |
foreach ($this->settings[$model->alias] as $field => $options) { | |
if (!in_array($field, array_keys($model->data[$model->alias]))) continue; | |
if (empty($this->runtime[$model->alias][$field])) continue; | |
if (isset($this->_removingOnly[$field])) continue; | |
$tempPath = $this->_getPath($model, $field); | |
$path = $this->settings[$model->alias][$field]['path']; | |
$thumbnailPath = $this->settings[$model->alias][$field]['thumbnailPath']; | |
if (!empty($tempPath)) { | |
$path .= $tempPath . DS; | |
$thumbnailPath .= $tempPath . DS; | |
} | |
$tmp = $this->runtime[$model->alias][$field]['tmp_name']; | |
if ($this->settings[$model->alias][$field]['customName'] != false){ | |
$filePath = $path . $this->_customName($model,$this->settings[$model->alias][$field]['customName'],$model->data[$model->alias][$field]); | |
}else{ | |
$filePath = $path . $this->_sanitizeFilename($model->data[$model->alias][$field]); | |
} | |
if (!$this->handleUploadedFile($model->alias, $field, $tmp, $filePath)) { | |
$model->invalidate($field, 'Unable to move the uploaded file to '.$filePath); | |
throw new UploadException('Unable to upload file'); | |
} | |
$this->_createThumbnails($model, $field, $path, $thumbnailPath); | |
if ($model->hasField($options['fields']['dir'])) { | |
if ($created && $options['pathMethod'] == '_getPathFlat') { | |
} else if ($options['saveDir']) { | |
$temp[$model->alias][$options['fields']['dir']] = "'{$tempPath}'"; | |
} | |
} | |
} | |
if (!empty($temp[$model->alias])) { | |
$model->updateAll($temp[$model->alias], array( | |
$model->alias.'.'.$model->primaryKey => $model->id | |
)); | |
} | |
if (empty($this->__filesToRemove[$model->alias])) return true; | |
foreach ($this->__filesToRemove[$model->alias] as $file) { | |
$result[] = $this->unlink($file); | |
} | |
return $result; | |
} | |
public function handleUploadedFile($modelAlias, $field, $tmp, $filePath) { | |
return is_uploaded_file($tmp) && @move_uploaded_file($tmp, $filePath); | |
} | |
public function unlink($file) { | |
return @unlink($file); | |
} | |
public function beforeDelete(Model $model, $cascade = true) { | |
$data = $model->find('first', array( | |
'conditions' => array("{$model->alias}.{$model->primaryKey}" => $model->id), | |
'contain' => false, | |
'recursive' => -1, | |
)); | |
foreach ($this->settings[$model->alias] as $field => $options) { | |
$this->_prepareFilesForDeletion($model, $field, $data, $options); | |
} | |
return true; | |
} | |
public function afterDelete(Model $model) { | |
$result = array(); | |
if (!empty($this->__filesToRemove[$model->alias])) { | |
foreach ($this->__filesToRemove[$model->alias] as $file) { | |
$result[] = $this->unlink($file); | |
} | |
} | |
return $result; | |
} | |
/** | |
* Verify that the uploaded file has been moved to the | |
* destination successfully. This rule is special that it | |
* is invalidated in afterSave(). Therefore it is possible | |
* for save() to return true and this rule to fail. | |
* | |
* @param Object $model | |
* @return boolean Always true | |
* @access public | |
*/ | |
public function moveUploadedFile(Model $model) { | |
return true; | |
} | |
/** | |
* Check that the file does not exceed the max | |
* file size specified by PHP | |
* | |
* @param Object $model | |
* @param mixed $check Value to check | |
* @return boolean Success | |
* @access public | |
*/ | |
public function isUnderPhpSizeLimit(Model $model, $check) { | |
$field = $this->_getField($check); | |
if (!empty($check[$field]['remove'])) { | |
return true; | |
} | |
return $check[$field]['error'] !== UPLOAD_ERR_INI_SIZE; | |
} | |
/** | |
* Check that the file does not exceed the max | |
* file size specified in the HTML Form | |
* | |
* @param Object $model | |
* @param mixed $check Value to check | |
* @return boolean Success | |
* @access public | |
*/ | |
public function isUnderFormSizeLimit(Model $model, $check) { | |
$field = $this->_getField($check); | |
if (!empty($check[$field]['remove'])) { | |
return true; | |
} | |
return $check[$field]['error'] !== UPLOAD_ERR_FORM_SIZE; | |
} | |
/** | |
* Check that the file was completely uploaded | |
* | |
* @param Object $model | |
* @param mixed $check Value to check | |
* @return boolean Success | |
* @access public | |
*/ | |
public function isCompletedUpload(Model $model, $check) { | |
$field = $this->_getField($check); | |
if (!empty($check[$field]['remove'])) { | |
return true; | |
} | |
return $check[$field]['error'] !== UPLOAD_ERR_PARTIAL; | |
} | |
/** | |
* Check that a file was uploaded | |
* | |
* @param Object $model | |
* @param mixed $check Value to check | |
* @return boolean Success | |
* @access public | |
*/ | |
public function isFileUpload(Model $model, $check) { | |
$field = $this->_getField($check); | |
if (!empty($check[$field]['remove'])) { | |
return true; | |
} | |
return $check[$field]['error'] !== UPLOAD_ERR_NO_FILE; | |
} | |
/** | |
* Check that the PHP temporary directory is missing | |
* | |
* @param Object $model | |
* @param mixed $check Value to check | |
* @return boolean Success | |
* @access public | |
*/ | |
public function tempDirExists(Model $model, $check, $requireUpload = true) { | |
$field = $this->_getField($check); | |
if (!empty($check[$field]['remove'])) { | |
return true; | |
} | |
// Allow circumvention of this rule if uploads is not required | |
if (!$requireUpload && $check[$field]['error'] === UPLOAD_ERR_NO_FILE) { | |
return true; | |
} | |
return $check[$field]['error'] !== UPLOAD_ERR_NO_TMP_DIR; | |
} | |
/** | |
* Check that the file was successfully written to the server | |
* | |
* @param Object $model | |
* @param mixed $check Value to check | |
* @return boolean Success | |
* @access public | |
*/ | |
public function isSuccessfulWrite(Model $model, $check, $requireUpload = true) { | |
$field = $this->_getField($check); | |
if (!empty($check[$field]['remove'])) { | |
return true; | |
} | |
// Allow circumvention of this rule if uploads is not required | |
if (!$requireUpload && $check[$field]['error'] === UPLOAD_ERR_NO_FILE) { | |
return true; | |
} | |
return $check[$field]['error'] !== UPLOAD_ERR_CANT_WRITE; | |
} | |
/** | |
* Check that a PHP extension did not cause an error | |
* | |
* @param Object $model | |
* @param mixed $check Value to check | |
* @return boolean Success | |
* @access public | |
*/ | |
public function noPhpExtensionErrors(Model $model, $check, $requireUpload = true) { | |
$field = $this->_getField($check); | |
if (!empty($check[$field]['remove'])) { | |
return true; | |
} | |
// Allow circumvention of this rule if uploads is not required | |
if (!$requireUpload && $check[$field]['error'] === UPLOAD_ERR_NO_FILE) { | |
return true; | |
} | |
return $check[$field]['error'] !== UPLOAD_ERR_EXTENSION; | |
} | |
/** | |
* Check that the file is of a valid mimetype | |
* | |
* @param Object $model | |
* @param mixed $check Value to check | |
* @param array $mimetypes file mimetypes to allow | |
* @return boolean Success | |
* @access public | |
*/ | |
public function isValidMimeType(Model $model, $check, $mimetypes = array(), $requireUpload = true) { | |
$field = $this->_getField($check); | |
if (!empty($check[$field]['remove'])) { | |
return true; | |
} | |
// Allow circumvention of this rule if uploads is not required | |
if (!$requireUpload && $check[$field]['error'] === UPLOAD_ERR_NO_FILE) { | |
return true; | |
} | |
// Non-file uploads also mean the mimetype is invalid | |
if (!isset($check[$field]['type']) || !strlen($check[$field]['type'])) { | |
return false; | |
} | |
// Sometimes the user passes in a string instead of an array | |
if (is_string($mimetypes)) { | |
$mimetypes = array($mimetypes); | |
} | |
foreach ($mimetypes as $key => $value) { | |
if (!is_int($key)) { | |
$mimetypes = $this->settings[$model->alias][$field]['mimetypes']; | |
break; | |
} | |
} | |
if (empty($mimetypes)) $mimetypes = $this->settings[$model->alias][$field]['mimetypes']; | |
return in_array($check[$field]['type'], $mimetypes); | |
} | |
/** | |
* Check that the upload directory is writable | |
* | |
* | |
* @param Object $model | |
* @param mixed $check Value to check | |
* @param string $path Full upload path | |
* @return boolean Success | |
* @access public | |
*/ | |
public function isWritable(Model $model, $check, $requireUpload = true) { | |
$field = $this->_getField($check); | |
if (!empty($check[$field]['remove'])) { | |
return true; | |
} | |
// Allow circumvention of this rule if uploads is not required | |
if (!$requireUpload && $check[$field]['error'] === UPLOAD_ERR_NO_FILE) { | |
return true; | |
} | |
return is_writable($this->settings[$model->alias][$field]['path']); | |
} | |
/** | |
* Check that the upload directory exists | |
* | |
* @param Object $model | |
* @param mixed $check Value to check | |
* @param string $path Full upload path | |
* @return boolean Success | |
* @access public | |
*/ | |
public function isValidDir(Model $model, $check, $requireUpload = true) { | |
$field = $this->_getField($check); | |
if (!empty($check[$field]['remove'])) { | |
return true; | |
} | |
// Allow circumvention of this rule if uploads is not required | |
if (!$requireUpload && $check[$field]['error'] === UPLOAD_ERR_NO_FILE) { | |
return true; | |
} | |
return is_dir($this->settings[$model->alias][$field]['path']); | |
} | |
/** | |
* Check that the file is below the maximum file upload size | |
* | |
* @param Object $model | |
* @param mixed $check Value to check | |
* @param int $size Maximum file size | |
* @return boolean Success | |
* @access public | |
*/ | |
public function isBelowMaxSize(Model $model, $check, $size = null, $requireUpload = true) { | |
$field = $this->_getField($check); | |
if (!empty($check[$field]['remove'])) { | |
return true; | |
} | |
// Allow circumvention of this rule if uploads is not required | |
if (!$requireUpload && $check[$field]['error'] === UPLOAD_ERR_NO_FILE) { | |
return true; | |
} | |
// Non-file uploads also mean the size is too small | |
if (!isset($check[$field]['size']) || !strlen($check[$field]['size'])) { | |
return false; | |
} | |
if (!$size) $size = $this->settings[$model->alias][$field]['maxSize']; | |
return $check[$field]['size'] <= $size; | |
} | |
/** | |
* Check that the file is above the minimum file upload size | |
* | |
* @param Object $model | |
* @param mixed $check Value to check | |
* @param int $size Minimum file size | |
* @return boolean Success | |
* @access public | |
*/ | |
public function isAboveMinSize(Model $model, $check, $size = null, $requireUpload = true) { | |
$field = $this->_getField($check); | |
if (!empty($check[$field]['remove'])) { | |
return true; | |
} | |
// Allow circumvention of this rule if uploads is not required | |
if (!$requireUpload && $check[$field]['error'] === UPLOAD_ERR_NO_FILE) { | |
return true; | |
} | |
// Non-file uploads also mean the size is too small | |
if (!isset($check[$field]['size']) || !strlen($check[$field]['size'])) { | |
return false; | |
} | |
if (!$size) $size = $this->settings[$model->alias][$field]['minSize']; | |
return $check[$field]['size'] >= $size; | |
} | |
/** | |
* Check that the file has a valid extension | |
* | |
* @param Object $model | |
* @param mixed $check Value to check | |
* @param array $extensions file extenstions to allow | |
* @return boolean Success | |
* @access public | |
*/ | |
public function isValidExtension(Model $model, $check, $extensions = array(), $requireUpload = true) { | |
$field = $this->_getField($check); | |
if (!empty($check[$field]['remove'])) { | |
return true; | |
} | |
// Allow circumvention of this rule if uploads is not required | |
if (!$requireUpload && $check[$field]['error'] === UPLOAD_ERR_NO_FILE) { | |
return true; | |
} | |
// Non-file uploads also mean the extension is invalid | |
if (!isset($check[$field]['name']) || !strlen($check[$field]['name'])) { | |
return false; | |
} | |
// Sometimes the user passes in a string instead of an array | |
if (is_string($extensions)) { | |
$extensions = array($extensions); | |
} | |
// Sometimes a user does not specify any extensions in the validation rule | |
foreach ($extensions as $key => $value) { | |
if (!is_int($key)) { | |
$extensions = $this->settings[$model->alias][$field]['extensions']; | |
break; | |
} | |
} | |
if (empty($extensions)) $extensions = $this->settings[$model->alias][$field]['extensions']; | |
$pathInfo = $this->_pathinfo($check[$field]['name']); | |
$extensions = array_map('strtolower', $extensions); | |
return in_array(strtolower($pathInfo['extension']), $extensions); | |
} | |
/** | |
* Check that the file is above the minimum height requirement | |
* | |
* @param Object $model | |
* @param mixed $check Value to check | |
* @param int $height Height of Image | |
* @return boolean Success | |
* @access public | |
*/ | |
public function isAboveMinHeight(Model $model, $check, $height = null, $requireUpload = true) { | |
$field = $this->_getField($check); | |
if (!empty($check[$field]['remove'])) { | |
return true; | |
} | |
// Allow circumvention of this rule if uploads is not required | |
if (!$requireUpload && $check[$field]['error'] === UPLOAD_ERR_NO_FILE) { | |
return true; | |
} | |
// Non-file uploads also mean the height is too big | |
if (!isset($check[$field]['tmp_name']) || !strlen($check[$field]['tmp_name'])) { | |
return false; | |
} | |
if (!$height) $height = $this->settings[$model->alias][$field]['minHeight']; | |
list($imgWidth, $imgHeight) = getimagesize($check[$field]['tmp_name']); | |
return $height > 0 && $imgHeight >= $height; | |
} | |
/** | |
* Check that the file is below the maximum height requirement | |
* | |
* @param Object $model | |
* @param mixed $check Value to check | |
* @param int $height Height of Image | |
* @return boolean Success | |
* @access public | |
*/ | |
public function isBelowMaxHeight(Model $model, $check, $height = null, $requireUpload = true) { | |
$field = $this->_getField($check); | |
if (!empty($check[$field]['remove'])) { | |
return true; | |
} | |
// Allow circumvention of this rule if uploads is not required | |
if (!$requireUpload && $check[$field]['error'] === UPLOAD_ERR_NO_FILE) { | |
return true; | |
} | |
// Non-file uploads also mean the height is too big | |
if (!isset($check[$field]['tmp_name']) || !strlen($check[$field]['tmp_name'])) { | |
return false; | |
} | |
if (!$height) $height = $this->settings[$model->alias][$field]['maxHeight']; | |
list($imgWidth, $imgHeight) = getimagesize($check[$field]['tmp_name']); | |
return $height > 0 && $imgHeight <= $height; | |
} | |
/** | |
* Check that the file is above the minimum width requirement | |
* | |
* @param Object $model | |
* @param mixed $check Value to check | |
* @param int $width Width of Image | |
* @return boolean Success | |
* @access public | |
*/ | |
public function isAboveMinWidth(Model $model, $check, $width = null, $requireUpload = true) { | |
$field = $this->_getField($check); | |
if (!empty($check[$field]['remove'])) { | |
return true; | |
} | |
// Allow circumvention of this rule if uploads is not required | |
if (!$requireUpload && $check[$field]['error'] === UPLOAD_ERR_NO_FILE) { | |
return true; | |
} | |
// Non-file uploads also mean the height is too big | |
if (!isset($check[$field]['tmp_name']) || !strlen($check[$field]['tmp_name'])) { | |
return false; | |
} | |
if (!$width) $width = $this->settings[$model->alias][$field]['minWidth']; | |
list($imgWidth, $imgHeight) = getimagesize($check[$field]['tmp_name']); | |
return $width > 0 && $imgWidth >= $width; | |
} | |
/** | |
* Check that the file is below the maximum width requirement | |
* | |
* @param Object $model | |
* @param mixed $check Value to check | |
* @param int $width Width of Image | |
* @return boolean Success | |
* @access public | |
*/ | |
public function isBelowMaxWidth(Model $model, $check, $width = null, $requireUpload = true) { | |
$field = $this->_getField($check); | |
if (!empty($check[$field]['remove'])) { | |
return true; | |
} | |
// Allow circumvention of this rule if uploads is not required | |
if (!$requireUpload && $check[$field]['error'] === UPLOAD_ERR_NO_FILE) { | |
return true; | |
} | |
// Non-file uploads also mean the height is too big | |
if (!isset($check[$field]['tmp_name']) || !strlen($check[$field]['tmp_name'])) { | |
return false; | |
} | |
if (!$width) $width = $this->settings[$model->alias][$field]['maxWidth']; | |
list($imgWidth, $imgHeight) = getimagesize($check[$field]['tmp_name']); | |
return $width > 0 && $imgWidth <= $width; | |
} | |
public function _resizeImagick(Model $model, $field, $path, $size, $geometry, $thumbnailPath) { | |
$srcFile = $path . $model->data[$model->alias][$field]; | |
$pathInfo = $this->_pathinfo($srcFile); | |
$thumbnailType = $imageFormat = $this->settings[$model->alias][$field]['thumbnailType']; | |
$isMedia = $this->_isMedia($model, $this->runtime[$model->alias][$field]['type']); | |
$image = new imagick(); | |
if ($isMedia) { | |
$image->setResolution(300, 300); | |
$srcFile = $srcFile.'[0]'; | |
} | |
$image->readImage($srcFile); | |
$height = $image->getImageHeight(); | |
$width = $image->getImageWidth(); | |
if (preg_match('/^\\[[\\d]+x[\\d]+\\]$/', $geometry)) { | |
// resize with banding | |
list($destW, $destH) = explode('x', substr($geometry, 1, strlen($geometry)-2)); | |
$image->thumbnailImage($destW, $destH, true); | |
$imageGeometry = $image->getImageGeometry(); | |
$x = ($destW - $imageGeometry['width']) / 2; | |
$y = ($destH - $imageGeometry['height']) / 2; | |
$image->setGravity(Imagick::GRAVITY_CENTER); | |
$image->extentImage($destW, $destH, $x, $y); | |
} elseif (preg_match('/^[\\d]+x[\\d]+$/', $geometry)) { | |
// cropped resize (best fit) | |
list($destW, $destH) = explode('x', $geometry); | |
$image->cropThumbnailImage($destW, $destH); | |
} elseif (preg_match('/^[\\d]+w$/', $geometry)) { | |
// calculate heigh according to aspect ratio | |
$image->thumbnailImage((int)$geometry-1, 0); | |
} elseif (preg_match('/^[\\d]+h$/', $geometry)) { | |
// calculate width according to aspect ratio | |
$image->thumbnailImage(0, (int)$geometry-1); | |
} elseif (preg_match('/^[\\d]+l$/', $geometry)) { | |
// calculate shortest side according to aspect ratio | |
$destW = 0; | |
$destH = 0; | |
$destW = ($width > $height) ? (int)$geometry-1 : 0; | |
$destH = ($width > $height) ? 0 : (int)$geometry-1; | |
$imagickVersion = phpversion('imagick'); | |
$image->thumbnailImage($destW, $destH, !($imagickVersion[0] == 3)); | |
} | |
if ($isMedia) { | |
$thumbnailType = $imageFormat = $this->settings[$model->alias][$field]['mediaThumbnailType']; | |
} | |
if (!$thumbnailType || !is_string($thumbnailType)) { | |
try { | |
$thumbnailType = $imageFormat = $image->getImageFormat(); | |
// Fix file casing | |
while (true) { | |
$ext = false; | |
$pieces = explode('.', $srcFile); | |
if (count($pieces) > 1) { | |
$ext = end($pieces); | |
} | |
if (!$ext || !strlen($ext)) { | |
break; | |
} | |
$low = array( | |
'ext' => strtolower($ext), | |
'thumbnailType' => strtolower($thumbnailType), | |
); | |
if ($low['ext'] == 'jpg' && $low['thumbnailType'] == 'jpeg') { | |
$thumbnailType = $ext; | |
break; | |
} | |
if ($low['ext'] == $low['thumbnailType']) { | |
$thumbnailType = $ext; | |
} | |
break; | |
} | |
} catch (Exception $e) {$this->log($e->getMessage(), 'upload'); | |
$thumbnailType = $imageFormat = 'png'; | |
} | |
} | |
$fileName = str_replace( | |
array('{size}', '{filename}', '{primaryKey}'), | |
array($size, $pathInfo['filename'], $model->id), | |
$this->settings[$model->alias][$field]['thumbnailName'] | |
); | |
$destFile = "{$thumbnailPath}{$fileName}.{$thumbnailType}"; | |
$image->setImageCompressionQuality($this->settings[$model->alias][$field]['thumbnailQuality']); | |
$image->setImageFormat($imageFormat); | |
if (!$image->writeImage($destFile)) { | |
return false; | |
} | |
$image->clear(); | |
$image->destroy(); | |
return true; | |
} | |
public function _resizePhp(Model $model, $field, $path, $size, $geometry, $thumbnailPath) { | |
$srcFile = $path . $model->data[$model->alias][$field]; | |
$pathInfo = $this->_pathinfo($srcFile); | |
$thumbnailType = $this->settings[$model->alias][$field]['thumbnailType']; | |
if (!$thumbnailType || !is_string($thumbnailType)) { | |
$thumbnailType = $pathInfo['extension']; | |
} | |
if (!$thumbnailType) { | |
$thumbnailType = 'png'; | |
} | |
$fileName = str_replace( | |
array('{size}', '{filename}', '{primaryKey}'), | |
array($size, $pathInfo['filename'], $model->id), | |
$this->settings[$model->alias][$field]['thumbnailName'] | |
); | |
$destFile = "{$thumbnailPath}{$fileName}.{$thumbnailType}"; | |
copy($srcFile, $destFile); | |
$src = null; | |
$createHandler = null; | |
$outputHandler = null; | |
switch (strtolower($pathInfo['extension'])) { | |
case 'gif': | |
$createHandler = 'imagecreatefromgif'; | |
break; | |
case 'jpg': | |
case 'jpeg': | |
$createHandler = 'imagecreatefromjpeg'; | |
break; | |
case 'png': | |
$createHandler = 'imagecreatefrompng'; | |
break; | |
default: | |
return false; | |
} | |
$supportsThumbnailQuality = false; | |
switch (strtolower($thumbnailType)) { | |
case 'gif': | |
$outputHandler = 'imagegif'; | |
break; | |
case 'jpg': | |
case 'jpeg': | |
$outputHandler = 'imagejpeg'; | |
$supportsThumbnailQuality = true; | |
break; | |
case 'png': | |
$outputHandler = 'imagepng'; | |
$supportsThumbnailQuality = true; | |
break; | |
default: | |
return false; | |
} | |
if ($src = $createHandler($destFile)) { | |
$srcW = imagesx($src); | |
$srcH = imagesy($src); | |
// determine destination dimensions and resize mode from provided geometry | |
if (preg_match('/^\\[[\\d]+x[\\d]+\\]$/', $geometry)) { | |
// resize with banding | |
list($destW, $destH) = explode('x', substr($geometry, 1, strlen($geometry)-2)); | |
$resizeMode = 'band'; | |
} elseif (preg_match('/^[\\d]+x[\\d]+$/', $geometry)) { | |
// cropped resize (best fit) | |
list($destW, $destH) = explode('x', $geometry); | |
$resizeMode = 'best'; | |
} elseif (preg_match('/^[\\d]+w$/', $geometry)) { | |
// calculate heigh according to aspect ratio | |
$destW = (int)$geometry-1; | |
$resizeMode = false; | |
} elseif (preg_match('/^[\\d]+h$/', $geometry)) { | |
// calculate width according to aspect ratio | |
$destH = (int)$geometry-1; | |
$resizeMode = false; | |
} elseif (preg_match('/^[\\d]+l$/', $geometry)) { | |
// calculate shortest side according to aspect ratio | |
if ($srcW > $srcH) $destW = (int)$geometry-1; | |
else $destH = (int)$geometry-1; | |
$resizeMode = false; | |
} | |
if (!isset($destW)) $destW = ($destH/$srcH) * $srcW; | |
if (!isset($destH)) $destH = ($destW/$srcW) * $srcH; | |
// determine resize dimensions from appropriate resize mode and ratio | |
if ($resizeMode == 'best') { | |
// "best fit" mode | |
if ($srcW > $srcH) { | |
if ($srcH/$destH > $srcW/$destW) $ratio = $destW/$srcW; | |
else $ratio = $destH/$srcH; | |
} else { | |
if ($srcH/$destH < $srcW/$destW) $ratio = $destH/$srcH; | |
else $ratio = $destW/$srcW; | |
} | |
$resizeW = $srcW*$ratio; | |
$resizeH = $srcH*$ratio; | |
} else if ($resizeMode == 'band') { | |
// "banding" mode | |
if ($srcW > $srcH) $ratio = $destW/$srcW; | |
else $ratio = $destH/$srcH; | |
$resizeW = $srcW*$ratio; | |
$resizeH = $srcH*$ratio; | |
} else { | |
// no resize ratio | |
$resizeW = $destW; | |
$resizeH = $destH; | |
} | |
$img = imagecreatetruecolor($destW, $destH); | |
imagealphablending($img, false); | |
imagesavealpha($img, true); | |
imagefill($img, 0, 0, imagecolorallocate($img, 255, 255, 255)); | |
imagecopyresampled($img, $src, ($destW-$resizeW)/2, ($destH-$resizeH)/2, 0, 0, $resizeW, $resizeH, $srcW, $srcH); | |
if ($supportsThumbnailQuality) { | |
$outputHandler($img, $destFile, $this->settings[$model->alias][$field]['thumbnailQuality']); | |
} else { | |
$outputHandler($img, $destFile); | |
} | |
return true; | |
} | |
return false; | |
} | |
public function _getPath(Model $model, $field) { | |
$path = $this->settings[$model->alias][$field]['path']; | |
$pathMethod = $this->settings[$model->alias][$field]['pathMethod']; | |
if (method_exists($this, $pathMethod)) { | |
return $this->$pathMethod($model, $field, $path); | |
} | |
return $this->_getPathPrimaryKey($model, $field, $path); | |
} | |
public function _getPathFlat(Model $model, $field, $path) { | |
$destDir = $path; | |
$this->_mkPath($destDir); | |
return ''; | |
} | |
public function _getPathPrimaryKey(Model $model, $field, $path) { | |
$destDir = $path . $model->id . DIRECTORY_SEPARATOR; | |
$this->_mkPath($destDir); | |
return $model->id; | |
} | |
public function _getPathRandom(Model $model, $field, $path) { | |
$endPath = null; | |
$decrement = 0; | |
$string = crc32($field . microtime()); | |
for ($i = 0; $i < 3; $i++) { | |
$decrement = $decrement - 2; | |
$endPath .= sprintf("%02d" . DIRECTORY_SEPARATOR, substr('000000' . $string, $decrement, 2)); | |
} | |
$destDir = $path . $endPath; | |
$this->_mkPath($destDir); | |
return substr($endPath, 0, -1); | |
} | |
public function _getPathRandomCombined(Model $model, $field, $path) { | |
$endPath = null; | |
$decrement = 0; | |
$string = crc32($field . microtime() . $model->id); | |
for ($i = 0; $i < 3; $i++) { | |
$decrement = $decrement - 2; | |
$endPath .= sprintf("%02d" . DIRECTORY_SEPARATOR, substr('000000' . $string, $decrement, 2)); | |
} | |
$destDir = $path . $endPath; | |
$this->_mkPath($destDir); | |
return substr($endPath, 0, -1); | |
} | |
public function _mkPath($destDir) { | |
if (!file_exists($destDir)) { | |
@mkdir($destDir, 0777, true); | |
@chmod($destDir, 0777); | |
} | |
return true; | |
} | |
/** | |
* Returns a path based on settings configuration | |
* | |
* @return string | |
**/ | |
public function _path(Model $model, $fieldName, $options = array()) { | |
$defaults = array( | |
'isThumbnail' => true, | |
'path' => '{ROOT}webroot{DS}files{DS}{model}{DS}{field}{DS}', | |
'rootDir' => $this->defaults['rootDir'], | |
); | |
$options = array_merge($defaults, $options); | |
foreach ($options as $key => $value) { | |
if ($value === null) { | |
$options[$key] = $defaults[$key]; | |
} | |
} | |
if (!$options['isThumbnail']) { | |
$options['path'] = str_replace(array('{size}', '{geometry}'), '', $options['path']); | |
} | |
$replacements = array( | |
'{ROOT}' => $options['rootDir'], | |
'{primaryKey}' => $model->id, | |
'{model}' => Inflector::underscore($model->alias), | |
'{field}' => $fieldName, | |
'{time}' => time(), | |
'{microtime}' => microtime(), | |
'{DS}' => DIRECTORY_SEPARATOR, | |
'//' => DIRECTORY_SEPARATOR, | |
'/' => DIRECTORY_SEPARATOR, | |
'\\' => DIRECTORY_SEPARATOR, | |
); | |
$newPath = Folder::slashTerm(str_replace( | |
array_keys($replacements), | |
array_values($replacements), | |
$options['path'] | |
)); | |
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { | |
if (!preg_match('/^([a-zA-Z]:\\\|\\\\)/', $newPath)) { | |
$newPath = $options['rootDir'] . $newPath; | |
} | |
} elseif ($newPath[0] !== DIRECTORY_SEPARATOR) { | |
$newPath = $options['rootDir'] . $newPath; | |
} | |
$pastPath = $newPath; | |
while (true) { | |
$pastPath = $newPath; | |
$newPath = str_replace(array( | |
'//', | |
'\\', | |
DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR | |
), DIRECTORY_SEPARATOR, $newPath); | |
if ($pastPath == $newPath) { | |
break; | |
} | |
} | |
return $newPath; | |
} | |
public function _pathThumbnail(Model $model, $field, $params = array()) { | |
return str_replace( | |
array('{size}', '{geometry}'), | |
array($params['size'], $params['geometry']), | |
$params['thumbnailPath'] | |
); | |
} | |
public function _createThumbnails(Model $model, $field, $path, $thumbnailPath) { | |
$isImage = $this->_isImage($model, $this->runtime[$model->alias][$field]['type']); | |
$isMedia = $this->_isMedia($model, $this->runtime[$model->alias][$field]['type']); | |
$createThumbnails = $this->settings[$model->alias][$field]['thumbnails']; | |
$hasThumbnails = !empty($this->settings[$model->alias][$field]['thumbnailSizes']); | |
if (($isImage || $isMedia) && $createThumbnails && $hasThumbnails) { | |
$method = $this->settings[$model->alias][$field]['thumbnailMethod']; | |
foreach ($this->settings[$model->alias][$field]['thumbnailSizes'] as $size => $geometry) { | |
$thumbnailPathSized = $this->_pathThumbnail($model, $field, compact( | |
'geometry', 'size', 'thumbnailPath' | |
)); | |
$this->_mkPath($thumbnailPathSized); | |
$valid = false; | |
if (method_exists($model, $method)) { | |
$valid = $model->$method($model, $field, $path, $size, $geometry, $thumbnailPathSized); | |
} elseif (method_exists($this, $method)) { | |
$valid = $this->$method($model, $field, $path, $size, $geometry, $thumbnailPathSized); | |
} else { | |
throw new Exception("Invalid thumbnailMethod %s", $method); | |
} | |
if (!$valid) { | |
$model->invalidate($field, 'resizeFail'); | |
} | |
} | |
} | |
} | |
public function _isImage(Model $model, $mimetype) { | |
return in_array($mimetype, $this->_imageMimetypes); | |
} | |
public function _isMedia(Model $model, $mimetype) { | |
return in_array($mimetype, $this->_mediaMimetypes); | |
} | |
public function _getMimeType($filePath) { | |
if (class_exists('finfo')) { | |
$finfo = new finfo(defined('FILEINFO_MIME_TYPE') ? FILEINFO_MIME_TYPE : FILEINFO_MIME); | |
return $finfo->file($filePath); | |
} | |
if (function_exists('exif_imagetype') && function_exists('image_type_to_mime_type')) { | |
$mimetype = image_type_to_mime_type(exif_imagetype($filePath)); | |
if ($mimetype !== false) { | |
return $mimetype; | |
} | |
} | |
if (function_exists('mime_content_type')) { | |
return mime_content_type($filePath); | |
} | |
return 'application/octet-stream'; | |
} | |
public function _prepareFilesForDeletion(Model $model, $field, $data, $options) { | |
if (!strlen($data[$model->alias][$field])) return $this->__filesToRemove; | |
$dir = $data[$model->alias][$options['fields']['dir']]; | |
$filePathDir = $this->settings[$model->alias][$field]['path'] . $dir . DS; | |
$filePath = $filePathDir.$data[$model->alias][$field]; | |
$pathInfo = $this->_pathinfo($filePath); | |
if (!isset($this->__filesToRemove[$model->alias])) { | |
$this->__filesToRemove[$model->alias] = array(); | |
} | |
$this->__filesToRemove[$model->alias][] = $filePath; | |
$createThumbnails = $options['thumbnails']; | |
$hasThumbnails = !empty($options['thumbnailSizes']); | |
if (!$createThumbnails || !$hasThumbnails) { | |
return $this->__filesToRemove; | |
} | |
$DS = DIRECTORY_SEPARATOR; | |
$mimeType = $this->_getMimeType($filePath); | |
$isMedia = $this->_isMedia($model, $mimeType); | |
$isImagickResize = $options['thumbnailMethod'] == 'imagick'; | |
$thumbnailType = $options['thumbnailType']; | |
if ($isImagickResize) { | |
if ($isMedia) { | |
$thumbnailType = $options['mediaThumbnailType']; | |
} | |
if (!$thumbnailType || !is_string($thumbnailType)) { | |
try { | |
$srcFile = $filePath; | |
$image = new imagick(); | |
if ($isMedia) { | |
$image->setResolution(300, 300); | |
$srcFile = $srcFile.'[0]'; | |
} | |
$image->readImage($srcFile); | |
$thumbnailType = $image->getImageFormat(); | |
} catch (Exception $e) { | |
$thumbnailType = 'png'; | |
} | |
} | |
} else { | |
if (!$thumbnailType || !is_string($thumbnailType)) { | |
$thumbnailType = $pathInfo['extension']; | |
} | |
if (!$thumbnailType) { | |
$thumbnailType = 'png'; | |
} | |
} | |
foreach ($options['thumbnailSizes'] as $size => $geometry) { | |
$fileName = str_replace( | |
array('{size}', '{filename}', '{primaryKey}', '{time}', '{microtime}'), | |
array($size, $pathInfo['filename'], $model->id, time(), microtime()), | |
$options['thumbnailName'] | |
); | |
$thumbnailPath = $options['thumbnailPath']; | |
$thumbnailPath = $this->_pathThumbnail($model, $field, compact( | |
'geometry', 'size', 'thumbnailPath' | |
)); | |
$thumbnailFilePath = "{$thumbnailPath}{$dir}{$DS}{$fileName}.{$thumbnailType}"; | |
$this->__filesToRemove[$model->alias][] = $thumbnailFilePath; | |
} | |
return $this->__filesToRemove; | |
} | |
public function _getField($check) { | |
$field_keys = array_keys($check); | |
return array_pop($field_keys); | |
} | |
public function _pathinfo($filename) { | |
$pathInfo = pathinfo($filename); | |
if (!isset($pathInfo['extension']) || !strlen($pathInfo['extension'])) { | |
$pathInfo['extension'] = ''; | |
} | |
// PHP < 5.2.0 doesn't include 'filename' key in pathinfo. Let's try to fix this. | |
if (empty($pathInfo['filename'])) { | |
$pathInfo['filename'] = basename($pathInfo['basename'], '.' . $pathInfo['extension']); | |
} | |
return $pathInfo; | |
} | |
public function _customName(Model $model, $customName ,$filename ){ | |
$filename = $this->_pathinfo($filename); | |
$customName = str_replace('{#NAME}',$filename['filename'],$customName); | |
preg_match_all("/(\{.*?})/", $customName, $matches); | |
if ($matches){ | |
foreach ($matches[0] as $row){ | |
$cm = substr($row,1,-1); | |
if ($cm[0] == '!'){ | |
$cm = substr($cm,1); | |
$customName = str_replace($row,$model->$cm($filename['filename']),$customName); | |
}else{ | |
$customName = str_replace($row,$model->$cm,$customName); | |
} | |
} | |
} | |
return $this->_sanitizeFilename("{$customName}.{$filename['extension']}"); | |
} | |
/** | |
* Make a filename safe to use in any function. (Accents, spaces, special chars...) | |
* The iconv function must be activated. | |
* | |
* @param string $fileName The filename to sanitize (with or without extension) | |
* @param string $defaultIfEmpty The default string returned for a non valid filename (only special chars or separators) | |
* @param string $separator The default separator | |
* @param boolean $lowerCase Tells if the string must converted to lower case | |
* | |
* @author COil <https://github.com/COil> | |
* @see http://stackoverflow.com/questions/2668854/sanitizing-strings-to-make-them-url-and-filename-safe | |
* | |
* @return string | |
*/ | |
public function _sanitizeFilename($fileName, $defaultIfEmpty = 'default', $separator = '_', $lowerCase = true) | |
{ | |
// Gather file informations and store its extension | |
// use _pathinfo instead pathinfo | |
$fileInfos = $this->_pathinfo($fileName); | |
$fileExt = array_key_exists('extension', $fileInfos) ? '.'. strtolower($fileInfos['extension']) : ''; | |
// Removes accents | |
$fileName = @iconv('UTF-8', 'us-ascii//TRANSLIT', $fileInfos['filename']); | |
// Removes all characters that are not separators, letters, numbers, dots or whitespaces | |
$fileName = preg_replace("/[^ a-zA-Z". preg_quote($separator). "\d\.\s]/", '', $lowerCase ? strtolower($fileName) : $fileName); | |
// Replaces all successive separators into a single one | |
$fileName = preg_replace('!['. preg_quote($separator).'\s]+!u', $separator, $fileName); | |
// Trim beginning and ending seperators | |
$fileName = trim($fileName, $separator); | |
// If empty use the default string | |
if (empty($fileName)) { | |
$fileName = $defaultIfEmpty; | |
} | |
return $fileName. $fileExt; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment