Created
January 21, 2011 00:05
-
-
Save kevindew/788986 to your computer and use it in GitHub Desktop.
widgets/validators for sfPluploadPlugin
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 | |
// an example within a sfForm configure method | |
class HomepageContentForm extends NavBarPageContentForm | |
{ | |
public function configure() | |
{ | |
// ... | |
$this->setWidget( | |
'contact_us_image_filename', | |
new sfWidgetFormInputPersistentFileEditablePlupload( | |
array( | |
'file_dir' => | |
$this->getObject()->getImagesDirPublic('default'), | |
'plupload_options' | |
=> array( | |
'max_file_size' => '2MB', | |
'filters' => array( | |
array( | |
'title' => 'Image Files', | |
'extensions' => 'jpg,gif,png' | |
) | |
) | |
), | |
'is_image' => true, | |
'label' => 'Contact Us Image', | |
'plupload_upload_path' | |
=> sfContext::getInstance()->getController() | |
->genUrl( | |
'@sfPlupload?form=' . get_class($this) | |
. '&validator=contact_us_image_plupload' | |
. '&sf_format=json' | |
), | |
'with_delete' => false, | |
'minify_js_method' | |
=> array('sfCombineUtility', 'minifyInlineJs') | |
) | |
) | |
); | |
$this->setValidator( | |
'contact_us_image_filename', | |
new sfValidatorPersistentFile( | |
array( | |
'required' => true, | |
'mime_types' => 'web_images', | |
'path' => $this->getObject()->getImagesDirSystem('original'), | |
'post_save_processing_function' | |
=> array( | |
$this->getObject(), | |
'processContactUsImageTransformations' | |
), | |
'form' => $this, | |
'field_name' => 'contact_us_image_filename' | |
) | |
) | |
); | |
$this->setValidator( | |
'contact_us_image_plupload', | |
new sfValidatorPreUploadedFile( | |
array( | |
'required' => false, | |
'mime_types' => 'web_images', | |
'path' => $this->getObject()->getImagesDirSystem('original'), | |
'post_save_processing_function' | |
=> array( | |
$this->getObject(), | |
'processContactUsImageTransformations' | |
) | |
) | |
) | |
); | |
// ... | |
} | |
} |
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 | |
/** | |
* Validator for a file that persists between form submissions | |
* | |
* @package rbUtilityPlugin | |
* @subpackage validator | |
* @author Kevin Dew <kev@redbullet.co.uk> | |
* @version SVN $Id: sfValidatorPersistentFile.class.php 185 2010-12-09 21:29:43Z kevin $ | |
*/ | |
class sfValidatorPersistentFile extends sfValidatorUploadedFileAbstract | |
{ | |
/** | |
* Configures the current validator. | |
* | |
* Available options: | |
* | |
* * form: A form object associated with this validator | |
* (to have it's tainted values updated) | |
* * field_name: The field name of the form field | |
* * file_validator_class: The class to validate the file upload | |
* (can't extend sfValidatorFile for this | |
* validator as otherwise the form processing | |
* on sfFormDoctrine tries to save it as a file) | |
* (Optional) | |
* * file_validator_options: Options for the file validator (Optional) | |
* * file_validator_messages: Messages for the file validator (Optional) | |
* | |
* @param array $options An array of options | |
* @param array $messages An array of error messages | |
* | |
* @see parent | |
*/ | |
protected function configure($options = array(), $messages = array()) | |
{ | |
if (!ini_get('file_uploads')) | |
{ | |
throw new LogicException( | |
sprintf( | |
'Unable to use a file validator as "file_uploads" is disabled in ' | |
. 'your php.ini file (%s)', get_cfg_var('cfg_file_path') | |
) | |
); | |
} | |
parent::configure($options, $messages); | |
$this->addRequiredOption('form'); | |
$this->addRequiredOption('field_name'); | |
$this->addOption('check_existing_file', false); | |
$this->addOption('existing_file_path', null); | |
$this->addMessage('partial', 'The uploaded file was only partially uploaded.'); | |
$this->addMessage('no_tmp_dir', 'Missing a temporary folder.'); | |
$this->addMessage('cant_write', 'Failed to write file to disk.'); | |
$this->addMessage('extension', 'File upload stopped by extension.'); | |
} | |
/** | |
* @see parent | |
* @todo This doesn't currently delete any files, reason for this is we don't | |
* want to delete a file on a failed form processing and then have the | |
* user not save it leaving the old filename in storage | |
*/ | |
protected function doClean($value) | |
{ | |
if (!is_array($value)) | |
{ | |
throw new Exception('Array expected as value'); | |
} | |
// whether user has specified to delete | |
$toDelete = isset($value['delete']) && $value['delete']; | |
$form = $this->getOption('form'); | |
if (!$form instanceof sfForm) | |
{ | |
throw new Exception('Form must be an instance of sfForm'); | |
} | |
elseif (!method_exists($form, 'setTaintedValue')) | |
{ | |
throw new Exception('Form must have a set tainted value method'); | |
} | |
$filename = isset($value['file']) ? $value['file'] : ''; | |
if ( | |
isset($value['upload']) | |
&& | |
!( | |
isset($value['upload']['error']) | |
&& | |
UPLOAD_ERR_NO_FILE === $value['upload']['error'] | |
) | |
) | |
{ | |
// process the file sent | |
$validatedFile = $this->validateUploadedFile($value['upload']); | |
$filename = $this->processUpload($validatedFile); | |
$toDelete = false; | |
} | |
else if ( | |
!$toDelete && $filename && $this->getOption('check_existing_file') | |
) | |
{ | |
// check existing file | |
$this->checkExistingFile( | |
($this->getOption('existing_file_path') | |
? $this->getOption('existing_file_path') | |
: $this->getOption('path') | |
) . $filename | |
); | |
} | |
if ((!$filename || $toDelete) && $this->getOption('required')) | |
{ | |
throw new sfValidatorError($this, 'required'); | |
} | |
$return = $filename; | |
if ($toDelete) | |
{ | |
$return = array( | |
'file' => $filename, | |
'delete' => true | |
); | |
} | |
$form->setTaintedValue($this->getOption('field_name'), $return); | |
return !$toDelete ? $filename : ''; | |
} | |
protected function validateUploadedFile($upload) | |
{ | |
if (!is_array($upload) || !isset($upload['tmp_name'])) | |
{ | |
throw new sfValidatorError( | |
$this, 'invalid', array('value' => (string) $upload) | |
); | |
} | |
if (!isset($upload['name'])) | |
{ | |
$upload['name'] = ''; | |
} | |
if (!isset($upload['error'])) | |
{ | |
$upload['error'] = UPLOAD_ERR_OK; | |
} | |
if (!isset($upload['size'])) | |
{ | |
$upload['size'] = filesize($upload['tmp_name']); | |
} | |
if (!isset($upload['type'])) | |
{ | |
$upload['type'] = 'application/octet-stream'; | |
} | |
switch ($upload['error']) | |
{ | |
case UPLOAD_ERR_INI_SIZE: | |
$max = ini_get('upload_max_filesize'); | |
if ($this->getOption('max_size')) | |
{ | |
$max = min($max, $this->getOption('max_size')); | |
} | |
throw new sfValidatorError( | |
$this, | |
'max_size', | |
array('max_size' => $max, 'size' => (int) $upload['size']) | |
); | |
case UPLOAD_ERR_FORM_SIZE: | |
throw new sfValidatorError( | |
$this, | |
'max_size', | |
array('max_size' => 0, 'size' => (int) $upload['size']) | |
); | |
case UPLOAD_ERR_PARTIAL: | |
throw new sfValidatorError($this, 'partial'); | |
case UPLOAD_ERR_NO_TMP_DIR: | |
throw new sfValidatorError($this, 'no_tmp_dir'); | |
case UPLOAD_ERR_CANT_WRITE: | |
throw new sfValidatorError($this, 'cant_write'); | |
case UPLOAD_ERR_EXTENSION: | |
throw new sfValidatorError($this, 'extension'); | |
} | |
// check file size | |
if ( | |
$this->hasOption('max_size') | |
&& | |
$this->getOption('max_size') < (int) $upload['size'] | |
) | |
{ | |
throw new sfValidatorError( | |
$this, | |
'max_size', | |
array( | |
'max_size' => $this->getOption('max_size'), | |
'size' => (int) $upload['size'] | |
) | |
); | |
} | |
$mimeType = $this->getMimeType( | |
(string) $upload['tmp_name'], (string) $upload['type'] | |
); | |
$mimeTypes = $this->getMimeTypes(); | |
if (!$this->checkMimeType($mimeType, $mimeTypes)) | |
{ | |
throw new sfValidatorError( | |
$this, | |
'mime_types', | |
array('mime_types' => $mimeTypes, 'mime_type' => $mimeType) | |
); | |
} | |
$class = $this->getOption('validated_file_class'); | |
return new $class( | |
$upload['name'], | |
$mimeType, | |
$upload['tmp_name'], | |
$upload['size'], | |
$this->getOption('path') | |
); | |
} | |
protected function checkExistingFile($filePath) | |
{ | |
if (!file_exists($filePath)) | |
{ | |
throw new sfValidatorError( | |
$this, | |
'file_not_found' | |
); | |
} | |
$mimeType = $this->getMimeType($filePath, 'application/octet-stream'); | |
$mimeTypes = $this->getMimeTypes(); | |
if (!$this->checkMimeType($mimeType, $mimeTypes)) | |
{ | |
throw new sfValidatorError( | |
$this, | |
'mime_types', | |
array('mime_types' => $mimeTypes, 'mime_type' => $mimeType) | |
); | |
} | |
} | |
/** | |
* @see sfValidatorBase | |
*/ | |
protected function isEmpty($value) | |
{ | |
$noUploadedFile = | |
isset($value['upload']['error']) | |
&& | |
UPLOAD_ERR_NO_FILE === $value['upload']['error'] | |
; | |
$emptyFile = !isset($value['file']) || !$value['file']; | |
$fileToBeDeleted = isset($value['delete']) && $value['delete']; | |
return | |
!is_array($value) | |
|| | |
( | |
$noUploadedFile | |
&& | |
($emptyFile || $fileToBeDeleted) | |
) | |
; | |
} | |
} |
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 | |
/** | |
* Validator for a file that has been uploaded | |
* | |
* @package rbUtilityPlugin | |
* @subpackage validator | |
* @author Kevin Dew <kev@redbullet.co.uk> | |
* @version SVN $Id: sfValidatorPreUploadedFile.class.php 206 2010-12-15 10:52:42Z kevin $ | |
*/ | |
class sfValidatorPreUploadedFile extends sfValidatorUploadedFileAbstract | |
{ | |
/** | |
* Configures the current validator. | |
* | |
* Available options: | |
* | |
* * return_with_filename: Return the validated result as an array with | |
* filename and the original filename | |
* | |
* @param array $options An array of options | |
* @param array $messages An array of error messages | |
* | |
* @see parent | |
*/ | |
protected function configure($options = array(), $messages = array()) | |
{ | |
parent::configure($options, $messages); | |
$this->addOption('return_with_name', false); | |
} | |
/** | |
* @see parent | |
*/ | |
protected function doClean($value) | |
{ | |
if (!is_array($value)) | |
{ | |
$value['file'] = $value; | |
} | |
if (!isset($value['name'])) | |
{ | |
$value['name'] = ''; | |
} | |
if (!isset($value['type'])) | |
{ | |
$value['type'] = 'application/octet-stream'; | |
} | |
$value['size'] = sprintf('%u', filesize($value['file'])); | |
// check file exists | |
if (!file_exists($value['file'])) | |
{ | |
throw new sfValidatorError( | |
$this, | |
'file_not_found' | |
); | |
} | |
// check mime type | |
$mimeType = $this->getMimeType($value['file'], $value['type']); | |
$mimeTypes = $this->getMimeTypes(); | |
if (!$this->checkMimeType($mimeType, $mimeTypes)) | |
{ | |
throw new sfValidatorError( | |
$this, | |
'mime_types', | |
array('mime_types' => $mimeTypes, 'mime_type' => $mimeType) | |
); | |
} | |
// check max size | |
// check file size | |
if ( | |
$this->hasOption('max_size') | |
&& | |
$this->getOption('max_size') < (int) $value['size'] | |
) | |
{ | |
throw new sfValidatorError( | |
$this, | |
'max_size', | |
array( | |
'max_size' => $this->getOption('max_size'), | |
'size' => (int) $value['size'] | |
) | |
); | |
} | |
$class = $this->getOption('validated_file_class'); | |
$validatedFile = new $class( | |
$value['name'], | |
$mimeType, | |
$value['file'], | |
$value['size'], | |
$this->getOption('path') | |
); | |
return $this->getOption('return_with_name') | |
? array( | |
'file' => $this->processUpload($validatedFile), | |
'name' => $value['name'] | |
) | |
: $this->processUpload($validatedFile) | |
; | |
} | |
} |
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 | |
/** | |
* Validator for a uploaded file | |
* | |
* Uses code from sfValidatorFile | |
* | |
* @package rbUtilityPlugin | |
* @subpackage validator | |
* @author Kevin Dew <kev@redbullet.co.uk> | |
* @version SVN $Id: sfValidatorUploadedFileAbstract.class.php 185 2010-12-09 21:29:43Z kevin $ | |
*/ | |
abstract class sfValidatorUploadedFileAbstract extends sfValidatorBase | |
{ | |
/** | |
* Configures the current validator. | |
* | |
* Available options: | |
* | |
* * generate_filename_function: | |
* A function to be called by call_user_func | |
* to generate the files filename (Optional) | |
* * generate_filename_function_args: | |
* An array of extra arguments for the above | |
* function (Optional) | |
* * post_save_processing_function: | |
* A function to be called by call_user_func | |
* to process the uploaded file (Optional) | |
* * post_save_processing_function_args: | |
* An array of extra arguments for the above | |
* function (Optional) | |
* | |
* @param array $options An array of options | |
* @param array $messages An array of error messages | |
* | |
* @see parent | |
*/ | |
protected function configure($options = array(), $messages = array()) | |
{ | |
parent::configure($options, $messages); | |
$this->addOption('generate_filename_function', null); | |
$this->addOption('generate_filename_function_args', array()); | |
$this->addOption('post_save_processing_function', null); | |
$this->addOption('post_save_processing_function_args', array()); | |
$this->addOption('max_size'); | |
$this->addOption('mime_types'); | |
$this->addOption('mime_type_guessers', array( | |
array($this, 'guessFromFileinfo'), | |
array($this, 'guessFromMimeContentType'), | |
array($this, 'guessFromFileBinary'), | |
)); | |
$this->addOption('mime_categories', array( | |
'web_images' => array( | |
'image/jpeg', | |
'image/pjpeg', | |
'image/png', | |
'image/x-png', | |
'image/gif', | |
))); | |
$this->addOption('validated_file_class', 'sfValidatedFile'); | |
$this->addOption('path', null); | |
$this->addMessage( | |
'max_size', 'File is too large (maximum is %max_size% bytes).' | |
); | |
$this->addMessage('mime_types', 'Invalid mime type (%mime_type%).'); | |
$this->addMessage( | |
'file_not_found', 'File was not found' | |
); | |
} | |
protected function generateFilename($originalFilename) | |
{ | |
$generatedFilename = null; | |
if ($this->getOption('generate_filename_function')) | |
{ | |
if (!is_callable($this->getOption('generate_filename_function'))) | |
{ | |
throw new Exception('Generate filename function is not callable'); | |
} | |
$generatedFilename = call_user_func_array( | |
$this->getOption('generate_filename_function'), | |
array_merge( | |
array($originalFilename), | |
$this->getOption('generate_filename_function_args') | |
) | |
); | |
} | |
return $generatedFilename; | |
} | |
protected function postSaveProcessing($filePath) | |
{ | |
if ($this->getOption('post_save_processing_function')) | |
{ | |
if (!is_callable($this->getOption('post_save_processing_function'))) | |
{ | |
throw new Exception('Post save processing function is not callable'); | |
} | |
call_user_func_array( | |
$this->getOption('post_save_processing_function'), | |
array_merge( | |
array($filePath), | |
$this->getOption('post_save_processing_function_args') | |
) | |
); | |
} | |
} | |
/** | |
* Returns the mime type of a file. | |
* | |
* This methods call each mime_type_guessers option callables to | |
* guess the mime type. | |
* | |
* This method always returns a lower-cased string as mime types are case-insensitive | |
* as per the RFC 2616 (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7). | |
* | |
* @param string $file The absolute path of a file | |
* @param string $fallback The default mime type to return if not guessable | |
* | |
* @return string The mime type of the file (fallback is returned if not guessable) | |
*/ | |
protected function getMimeType($file, $fallback) | |
{ | |
foreach ($this->getOption('mime_type_guessers') as $method) | |
{ | |
$type = call_user_func($method, $file); | |
if (null !== $type && $type !== false) | |
{ | |
return strtolower($type); | |
} | |
} | |
return strtolower($fallback); | |
} | |
/** | |
* Guess the file mime type with PECL Fileinfo extension | |
* | |
* @param string $file The absolute path of a file | |
* | |
* @return string The mime type of the file (null if not guessable) | |
*/ | |
protected function guessFromFileinfo($file) | |
{ | |
if (!function_exists('finfo_open') || !is_readable($file)) | |
{ | |
return null; | |
} | |
if (!$finfo = new finfo(FILEINFO_MIME)) | |
{ | |
return null; | |
} | |
$type = $finfo->file($file); | |
// remove charset (added as of PHP 5.3) | |
if (false !== $pos = strpos($type, ';')) | |
{ | |
$type = substr($type, 0, $pos); | |
} | |
return $type; | |
} | |
/** | |
* Guess the file mime type with mime_content_type function (deprecated) | |
* | |
* @param string $file The absolute path of a file | |
* | |
* @return string The mime type of the file (null if not guessable) | |
*/ | |
protected function guessFromMimeContentType($file) | |
{ | |
if (!function_exists('mime_content_type') || !is_readable($file)) | |
{ | |
return null; | |
} | |
return mime_content_type($file); | |
} | |
/** | |
* Guess the file mime type with the file binary (only available on *nix) | |
* | |
* @param string $file The absolute path of a file | |
* | |
* @return string The mime type of the file (null if not guessable) | |
*/ | |
protected function guessFromFileBinary($file) | |
{ | |
ob_start(); | |
//need to use --mime instead of -i. see #6641 | |
passthru(sprintf('file -b --mime %s 2>/dev/null', escapeshellarg($file)), $return); | |
if ($return > 0) | |
{ | |
ob_end_clean(); | |
return null; | |
} | |
$type = trim(ob_get_clean()); | |
if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-]+)#i', $type, $match)) | |
{ | |
// it's not a type, but an error message | |
return null; | |
} | |
return $match[1]; | |
} | |
protected function getMimeTypesFromCategory($category) | |
{ | |
$categories = $this->getOption('mime_categories'); | |
if (!isset($categories[$category])) | |
{ | |
throw new InvalidArgumentException( | |
sprintf('Invalid mime type category "%s".', $category) | |
); | |
} | |
return $categories[$category]; | |
} | |
protected function getMimeTypes() | |
{ | |
if ($this->hasOption('mime_types')) | |
{ | |
$mimeTypes = is_array($this->getOption('mime_types')) | |
? $this->getOption('mime_types') | |
: $this->getMimeTypesFromCategory($this->getOption('mime_types') | |
); | |
return $mimeTypes; | |
} | |
return array(); | |
} | |
protected function checkMimeType($mimeType, $mimeTypes) | |
{ | |
if ($mimeTypes) | |
{ | |
return in_array($mimeType, array_map('strtolower', $mimeTypes)); | |
} | |
return true; | |
} | |
/** | |
* Saves the file and performs any actions specified for it | |
* | |
* @param array $upload an array of data for a file upload | |
* @return string | |
*/ | |
protected function processUpload(sfValidatedFile $validatedFile) | |
{ | |
$filePath = rtrim($this->getOption('path'), '/'); | |
$generatedFilename = $this->generateFilename( | |
isset($upload['name']) ? $upload['name'] : null | |
); | |
$filename = $validatedFile->save($generatedFilename); | |
$this->postSaveProcessing($filePath . '/' . $filename); | |
return $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 | |
/** | |
* Widget for a file that persists between form submissions | |
* | |
* @package rbUtilityPlugin | |
* @subpackage widget | |
* @author Kevin Dew <kev@redbullet.co.uk> | |
* @version SVN $Id: sfWidgetFormInputPersistentFileEditable.class.php 206 2010-12-15 10:52:42Z kevin $ | |
*/ | |
class sfWidgetFormInputPersistentFileEditable extends sfWidgetFormInputFile | |
{ | |
protected | |
$_containerId, | |
$_hiddenFieldName, | |
$_hiddenFieldId, | |
$_file, | |
$_toDelete, | |
$_deleteId | |
; | |
/** | |
* Constructor. | |
* | |
* Available options: | |
* | |
* * file_dir: The directory where the file is (required) | |
* * is_image: Whether the file is a displayable image | |
* * with_delete: Whether to add a delete checkbox or not | |
* * delete_label: The delete label used by the template | |
* * template: The HTML template to use to render this widget when in edit mode | |
* The available placeholders are: | |
* * %input% (the image upload widget) | |
* * %delete% (the delete checkbox) | |
* * %delete_label% (the delete label text) | |
* * %file% (the file tag) | |
* | |
* @param array $options An array of options | |
* @param array $attributes An array of default HTML attributes | |
* | |
* @see sfWidgetFormInputFile | |
*/ | |
protected function configure($options = array(), $attributes = array()) | |
{ | |
parent::configure($options, $attributes); | |
$this->addRequiredOption('file_dir'); | |
$this->addOption('is_image', false); | |
$this->addOption('with_delete', true); | |
$this->addOption('delete_label', 'Remove'); | |
$this->addOption( | |
'template', | |
'<div id="%container_id%" class="editable-file-container">' | |
. '<div class="file-placeholder">%file_template%</div> ' | |
. '%input_template%' | |
. '%delete_template%' | |
. '</div> %javascript%' | |
); | |
$this->addOption('to_delete_class', 'delete'); | |
$this->addOption( | |
'file_template', | |
'<div class="file-upload file-container%to_delete_class%">' | |
. '<a href="%file_url%" target="_blank" class="file">%file_name%</a>' | |
. '</div>' | |
); | |
$this->addOption( | |
'image_template', | |
'<div class="file-upload image-container%to_delete_class%">' | |
. '<img src="%file_url%" alt="" />' | |
. '</div>' | |
); | |
$this->addOption( | |
'input_template', | |
'<div class="input-container">' | |
. '%file_input% %hidden_input%' | |
. '</div>' | |
); | |
$this->addOption( | |
'delete_template', | |
'<div class="delete-container">' | |
. '%delete_input% %delete_label%' | |
. '</div>' | |
); | |
$this->addOption('minify_js_method', null); | |
$this->addOption('minify_js_method_options', array()); | |
$this->addOption( | |
'javascript', | |
<<<EOF | |
(function($) { | |
$(document).ready(function() { | |
$("#%delete_id%").click(function() { | |
if (this.checked) { | |
$("#%container_id%").find(".file").addClass("%to_delete_class%"); | |
} else { | |
$("#%container_id%").find(".file").removeClass("%to_delete_class%"); | |
} | |
}); | |
}); | |
})(jQuery); | |
EOF | |
); | |
} | |
/** | |
* Renders the widget. | |
* | |
* @param string $name The element name | |
* @param string $value The value displayed in this widget | |
* @param array $attributes An array of HTML attributes to be merged with the default HTML attributes | |
* @param array $errors An array of errors for the field | |
* | |
* @return string An HTML tag string | |
* | |
* @see sfWidgetForm | |
*/ | |
public function render($name, $value = null, $attributes = array(), $errors = array()) | |
{ | |
$this->_containerId = $this->generateId($name . '[container]'); | |
$this->_hiddenFieldName = $name . '[file]'; | |
$this->_hiddenFieldId = $this->generateId($this->_hiddenFieldName); | |
$this->_file = $value; | |
$this->_toDelete = false; | |
$this->_deleteId = $this->generateId($name . '[delete]'); | |
if (is_array($value)) | |
{ | |
$this->_file = isset($value['file']) ? $value['file'] : ''; | |
$this->_toDelete = isset($value['delete']) && $value['delete']; | |
} | |
return strtr( | |
$this->getOption('template'), | |
array( | |
'%container_id%' => | |
$this->_containerId, | |
'%file_template%' => | |
$this->buildFileTemplate($name, $value, $attributes, $errors), | |
'%input_template%' => | |
$this->buildInputTemplate($name, $value, $attributes, $errors), | |
'%delete_template%' => | |
$this->buildDeleteTemplate($name, $value, $attributes, $errors), | |
'%javascript%' => | |
$this->buildJavascript($name, $value, $attributes, $errors) | |
) | |
); | |
} | |
protected function buildFileTemplate($name, $value, $attributes, $errors) | |
{ | |
if (!$this->_file) | |
{ | |
return ''; | |
} | |
$filePath = ''; | |
if ($this->getOption('file_dir')) | |
{ | |
$filePath = rtrim($this->getOption('file_dir'), '/'); | |
} | |
$filePath .= '/' . $this->_file; | |
$deleteClass = | |
($this->getOption('to_delete_class') ? ' ' : '') | |
. $this->getOption('to_delete_class') | |
; | |
$replacements = array( | |
'%file_name%' => $this->_file, | |
'%file_url%' => $filePath, | |
'%to_delete_class%' => $this->_toDelete ? $deleteClass : '' | |
); | |
if ($this->getOption('is_image')) | |
{ | |
$template = strtr($this->getOption('image_template'), $replacements); | |
} | |
else | |
{ | |
$template = strtr($this->getOption('file_template'), $replacements); | |
} | |
return $template; | |
} | |
protected function buildInputTemplate($name, $value, $attributes, $errors) | |
{ | |
$fileInput = $this->renderTag( | |
'input', | |
array('type' => 'file', 'name' => $name . '[upload]') | |
); | |
$hiddenInput = ''; | |
if ($this->_file) | |
{ | |
$hiddenInput = $this->renderTag( | |
'input', | |
array( | |
'type' => 'hidden', | |
'name' => $this->_hiddenFieldName, | |
'id' => $this->_hiddenFieldId, | |
'value' => $this->_file | |
) | |
); | |
} | |
return strtr( | |
$this->getOption('input_template'), | |
array( | |
'%file_input%' => $fileInput, | |
'%hidden_input%' => $hiddenInput | |
) | |
); | |
} | |
protected function buildDeleteTemplate($name, $value, $attributes, $errors) | |
{ | |
if (!$this->getOption('with_delete') || !$this->_file) | |
{ | |
return ''; | |
} | |
$deleteInput = $this->renderTag( | |
'input', | |
array_merge( | |
array('type' => 'checkbox', 'name' => $name . '[delete]'), | |
$attributes, | |
array('checked' => $this->_toDelete) | |
) | |
); | |
$deleteLabel = $this->renderContentTag( | |
'label', | |
$this->translate($this->getOption('delete_label')), | |
array_merge(array('for' => $this->generateId($name . '[delete]'))) | |
); | |
return strtr( | |
$this->getOption('delete_template'), | |
array( | |
'%delete_input%' => $deleteInput, | |
'%delete_label%' => $deleteLabel | |
) | |
); | |
} | |
protected function buildJavascript($name, $value, $attributes, $errors) | |
{ | |
if (!$this->getOption('javascript')) | |
{ | |
return ''; | |
} | |
$javascript = strtr( | |
$this->getOption('javascript'), | |
array( | |
'%delete_id%' => addcslashes($this->_deleteId, '"'), | |
'%container_id%' => addcslashes($this->_containerId, '"'), | |
'%to_delete_class%' => addcslashes($this->getOption('to_delete_class'), '"'), | |
) | |
); | |
return sprintf(<<<EOF | |
<script type="text/javascript"> | |
//<![CDATA[ | |
%s | |
//]]> | |
</script> | |
EOF | |
, | |
$this->minifyJavascript($javascript) | |
); | |
} | |
/** | |
* @see parent | |
* @return array | |
*/ | |
public function getStylesheets() | |
{ | |
return array( | |
'/rbUtilityPlugin/css/input-persistent-file.css' => 'screen' | |
); | |
} | |
protected function minifyJavascript($javascript) | |
{ | |
if ( | |
$this->getOption('minify_js_method') | |
&& | |
is_callable($this->getOption('minify_js_method')) | |
) | |
{ | |
$javascript = call_user_func( | |
$this->getOption('minify_js_method'), | |
$javascript, | |
$this->getOption('minify_js_method_options') | |
); | |
} | |
return $javascript; | |
} | |
} |
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 | |
/** | |
* Widget for a file that persists between form submissions and uses Plupload | |
* as the mechanism for uploading | |
* | |
* Note default options use both jQuery and jQuery UI | |
* | |
* @package rbUtilityPlugin | |
* @subpackage widget | |
* @author Kevin Dew <kev@redbullet.co.uk> | |
* @version SVN $Id: sfWidgetFormInputPersistentFileEditablePlupload.class.php 218 2010-12-21 01:00:15Z kevin $ | |
*/ | |
class sfWidgetFormInputPersistentFileEditablePlupload | |
extends sfWidgetFormInputPersistentFileEditable | |
{ | |
protected | |
$_containerId, | |
$_uploaderId, | |
$_hiddenFieldName, | |
$_hiddenFieldId, | |
$_file, | |
$_toDelete, | |
$_pluploadOptions, | |
$_deleteId, | |
$_pluploadDefaultOptions | |
; | |
/** | |
* There are a lot of options! | |
* | |
* Required | |
* * plupload_upload_path - the path that files will be uploaded to | |
* | |
* Optional | |
* * plupload_tempate - html for the plupload controls | |
* * uploader_text - text within the uploader control | |
* * uploading_text - text that appears while file is uploading | |
* * ul_error_class - class for the list holding errors | |
* * li_error_class - class for the list item within it | |
* * plupload_500_error_message - error message for when plupload is returned | |
* a 500 error | |
* * plupload_file_size_error_message | |
* * plupload_extension_error_message | |
* * delete_active_class - a class to give the delete container when it is in | |
* use so javascript can determine whether to hide it or not | |
* * plupload_options - options for plupload itself, this is merged with | |
* the default options specified in this class and | |
* app_rbUtilityPlugin_plupload_options in a app.yml | |
* * plupload_path_to_js - path to the javascript files for plupload | |
* * plupload_js_base - base javascript for this widget (glues other parts in | |
* it) | |
* * delete_js - js for handling the delete checkbox | |
* * plupload_js_pre_init - all of these are various js methods | |
* * plupload_js_files_added | |
* * plupload_js_upload_progress | |
* * plupload_js_error | |
* * plupload_js_file_uploaded | |
* * plupload_js_reset_uploader | |
* * plupload_js_handle_error | |
* * plupload_js_handle_success | |
* * plupload_js_hide_uploader | |
* * plupload_js_reset_errors | |
* * plupload_js_post_init | |
* | |
* @see parent | |
* @param array $options | |
* @param array $attributes | |
*/ | |
protected function configure($options = array(), $attributes = array()) | |
{ | |
parent::configure($options, $attributes); | |
// default options for plupload | |
$this->_pluploadDefaultOptions = $this->getDefaultPluploadOptions(); | |
$this->addRequiredOption('plupload_upload_path'); | |
$this->addOption( | |
'template', | |
'<div id="%container_id%" class="editable-file-container plupload-editable">' | |
. '<div class="file-placeholder">%file_template%</div> ' | |
. '%plupload_template% ' | |
. '%input_template%' | |
. '%delete_template%' | |
. '<div class="errors"></div>' | |
. '</div> %javascript%' | |
); | |
$this->addOption( | |
'plupload_template', | |
'<div class="plupload-controls-container">' | |
. '<div class="progress-container"></div>' | |
. '<div class="uploader-container" id="%plupload_container_id%">' | |
. '<button class="uploader" id="%uploader_id%">%uploader_text%</button>' | |
. '</div>' | |
. '</div>' | |
); | |
$this->addOption('uploader_text', 'Upload'); | |
$this->addOption('uploading_text', 'Uploading %filename%'); | |
$this->addOption('ul_error_class', 'form-errors sf-error'); | |
$this->addOption('li_error_class', 'error'); | |
$this->addOption( | |
'plupload_500_error_message', 'File could not be uploaded' | |
); | |
$this->addOption( | |
'plupload_file_size_error_message', 'File size is too big' | |
); | |
$this->addOption( | |
'plupload_extension_error_message', 'File is incorrect type' | |
); | |
$this->addOption( | |
'delete_template', | |
'<div class="delete-container%delete_active_class%">' | |
. '%delete_input% %delete_label%' | |
. '</div>' | |
); | |
$this->addOption('delete_active_class', 'active'); | |
$this->addOption('plupload_options', array()); | |
$this->addOption( | |
'plupload_path_to_js', '/rbUtilityPlugin/js/plupload-1.3.0' | |
); | |
$this->addOption('plupload_js_base', <<<EOF | |
(function($) { | |
$(document).ready(function() { | |
%delete_js% | |
%plupload_js_pre_init% | |
var container = $("#%container_id%"); | |
$("#%uploader_id%").click(function() { return false; }); | |
container.find(".input-container").hide(); | |
container.find(".delete-container:not(.active)").hide(); | |
if (!$("#%hidden_field_id%").length) { | |
$("<input type=\"hidden\" name=\"%hidden_field_name%\" " | |
+ "id=\"%hidden_field_id%\" />" | |
).appendTo(container.find(".input-container").first()); | |
} | |
var uploader = new plupload.Uploader(%uploader_json%); | |
uploader.init(); | |
uploader.bind("FilesAdded", function(up, files) { | |
%plupload_js_files_added% | |
}); | |
uploader.bind("UploadProgress", function(up, file) { | |
%plupload_js_upload_progress% | |
}); | |
uploader.bind("Error", function(up, err) { | |
%plupload_js_error% | |
}); | |
uploader.bind("FileUploaded", function(up, file, response) { | |
%plupload_js_file_uploaded% | |
}); | |
function resetUploader(up) { | |
%plupload_js_reset_uploader% | |
} | |
function handleError(up, message) { | |
%plupload_js_handle_error% | |
} | |
function handleSuccess(up, file) { | |
%plupload_js_handle_success% | |
} | |
function hideUploader(up) { | |
%plupload_js_hide_uploader% | |
} | |
function resetErrors(up) { | |
%plupload_js_reset_errors% | |
} | |
}); | |
})(jQuery); | |
EOF | |
); | |
$this->addOption('delete_js', <<<EOF | |
$("#%delete_id%").click(function() { | |
if (this.checked) { | |
$("#%container_id%").find(".file").addClass("%to_delete_class%"); | |
} else { | |
$("#%container_id%").find(".file").removeClass("%to_delete_class%"); | |
} | |
}); | |
EOF | |
); | |
$this->addOption('plupload_js_pre_init', ''); | |
$this->addOption('plupload_js_files_added', <<<EOF | |
resetErrors(); | |
resetUploader(up); | |
var filename = files[0].name; | |
var progress = container.find(".progress-container").get(0); | |
$(progress).append( | |
"<div class=\"progress\"><div class=\"message\">%uploading_text%" | |
+ "</div><div class=\"bar\"></div></div>" | |
); | |
$(progress).find(".bar").progressbar({value: 0}); | |
hideUploader(up); | |
up.start(); | |
up.refresh(); | |
EOF | |
); | |
$this->addOption('plupload_js_upload_progress', <<<EOF | |
container.find(".progress-container .bar").progressbar( | |
"value", file.percent | |
); | |
EOF | |
); | |
$this->addOption('plupload_js_error', <<<EOF | |
var message = ""; | |
switch (err.code) { | |
case plupload.FILE_SIZE_ERROR: | |
message = "%plupload_file_size_error_message%"; | |
break; | |
case plupload.FILE_EXTENSION_ERROR: | |
message = "%plupload_extension_error_message%"; | |
break; | |
} | |
return handleError(up, message); | |
EOF | |
); | |
$this->addOption('plupload_js_file_uploaded', <<<EOF | |
try { | |
var json = $.parseJSON(response.response); | |
} catch (err) { | |
return handleError(up, ""); | |
} | |
if (json.status == "error") { | |
return handleError(up, json.message); | |
} | |
if (json.status != "complete") { | |
return; | |
} | |
handleSuccess(up, json.file); | |
EOF | |
); | |
$this->addOption('plupload_js_reset_uploader', <<<EOF | |
container.find(".progress-container").empty(); | |
container.find(".uploader-container button.uploader").show(); | |
up.refresh(); | |
EOF | |
); | |
$this->addOption('plupload_js_handle_error', <<<EOF | |
if (!message) { | |
message = "%plupload_500_error_message%"; | |
} | |
resetErrors(up); | |
var ul = $("<ul class=\"%ul_error_class%\" />").append( | |
$("<li class=\"%li_error_class%\" />").text(message) | |
); | |
container.find(".errors").append(ul); | |
resetUploader(up); | |
EOF | |
); | |
$this->addOption('plupload_js_handle_success', <<<EOF | |
container.find(".file-placeholder .file").remove(); | |
var template = "%file_template%"; | |
template = template.replace(/%file_url%/g, "%path%" + "/" + file); | |
template = template.replace(/%file_name%/g, file); | |
container.find(".file-placeholder").append(template); | |
if ($("#%hidden_field_id%").length) { | |
$("#%hidden_field_id%").val(file); | |
} else { | |
$("<input type=\"hidden\" name=\"%hidden_field_name%\" " | |
+ "id=\"%hidden_field_id%\" />" | |
).val(file).appendTo(container.find(".input-container").first()); | |
} | |
container.find(".delete-container").show(); | |
resetUploader(up); | |
EOF | |
); | |
$this->addOption('plupload_js_hide_uploader', <<<EOF | |
container.find(".uploader-container button.uploader").hide(); | |
up.refresh(); | |
EOF | |
); | |
$this->addOption('plupload_js_reset_errors', <<<EOF | |
container.find(".errors").empty(); | |
EOF | |
); | |
$this->addOption('plupload_js_post_init', ''); | |
} | |
/** | |
* Renders the widget. | |
* | |
* @param string $name The element name | |
* @param string $value The value displayed in this widget | |
* @param array $attributes An array of HTML attributes to be merged with the default HTML attributes | |
* @param array $errors An array of errors for the field | |
* | |
* @return string An HTML tag string | |
* | |
* @see sfWidgetForm | |
*/ | |
public function render($name, $value = null, $attributes = array(), $errors = array()) | |
{ | |
$this->_containerId = $this->generateId($name . '[container]'); | |
$this->_pluploadContainerId = | |
$this->generateId($name . '[plupload_container]') | |
; | |
$this->_uploaderId = $this->generateId($name . '[uploader]'); | |
$this->_deleteId = $this->generateId($name . '[delete]'); | |
$this->_hiddenFieldName = $name . '[file]'; | |
$this->_hiddenFieldId = $this->generateId($this->_hiddenFieldName); | |
// make plupload options | |
$this->_pluploadOptions = array_merge( | |
$this->getPluploadOptions(), | |
array( | |
'url' => $this->getOption('plupload_upload_path'), | |
'browse_button' => $this->_uploaderId, | |
'container' => $this->_pluploadContainerId | |
) | |
); | |
$this->_file = $value; | |
$this->_toDelete = false; | |
if (is_array($value)) | |
{ | |
$this->_file = isset($value['file']) ? $value['file'] : ''; | |
$this->_toDelete = isset($value['delete']) && $value['delete']; | |
} | |
return strtr( | |
$this->getOption('template'), | |
array( | |
'%container_id%' => | |
$this->_containerId, | |
'%file_template%' => | |
$this->buildFileTemplate($name, $value, $attributes, $errors), | |
'%plupload_template%' => | |
$this->buildPluploadTemplate($name, $value, $attributes, $errors), | |
'%input_template%' => | |
$this->buildInputTemplate($name, $value, $attributes, $errors), | |
'%delete_template%' => | |
$this->buildDeleteTemplate($name, $value, $attributes, $errors), | |
'%javascript%' => | |
$this->buildJavascript($name, $value, $attributes, $errors), | |
) | |
); | |
} | |
/** | |
* Build the template for the plupload part of the widget | |
* | |
* @param string $name | |
* @param string $value | |
* @param array $attributes | |
* @param array $errors | |
* @return string | |
*/ | |
protected function buildPluploadTemplate($name, $value, $attributes, $errors) | |
{ | |
return strtr( | |
$this->getOption('plupload_template'), | |
array( | |
'%plupload_container_id%' => $this->_pluploadContainerId, | |
'%uploader_id%' => $this->_uploaderId, | |
'%uploader_text%' => $this->getOption('uploader_text') | |
) | |
); | |
} | |
/** | |
* Build the template for the delete part of the widget | |
* | |
* @param string $name | |
* @param string $value | |
* @param array $attributes | |
* @param array $errors | |
* @return string | |
*/ | |
protected function buildDeleteTemplate($name, $value, $attributes, $errors) | |
{ | |
if (!$this->getOption('with_delete')) | |
{ | |
return ''; | |
} | |
$deleteInput = $this->renderTag( | |
'input', | |
array_merge( | |
array('type' => 'checkbox', 'name' => $name . '[delete]'), | |
$attributes, | |
array('checked' => $this->_toDelete) | |
) | |
); | |
$deleteLabel = $this->renderContentTag( | |
'label', | |
$this->translate($this->getOption('delete_label')), | |
array_merge(array('for' => $this->generateId($name . '[delete]'))) | |
); | |
return strtr( | |
$this->getOption('delete_template'), | |
array( | |
'%delete_input%' => $deleteInput, | |
'%delete_label%' => $deleteLabel, | |
'%delete_active_class%' => ($this->_file | |
? ' ' . $this->getOption('delete_active_class') | |
: '' | |
) | |
) | |
); | |
} | |
/** | |
* Build the javascript for this widget | |
* | |
* @param string $name | |
* @param string $value | |
* @param array $attributes | |
* @param array $errors | |
* @return string | |
*/ | |
protected function buildJavascript($name, $value, $attributes, $errors) | |
{ | |
// build full js for translatation | |
$javascript = strtr( | |
$this->getOption('plupload_js_base'), | |
array( | |
'%delete_js%' => $this->getOption('delete_js'), | |
'%plupload_js_pre_init%' => $this->getOption('plupload_js_pre_init'), | |
'%plupload_js_files_added%' => $this->getOption('plupload_js_files_added'), | |
'%plupload_js_upload_progress%' => $this->getOption('plupload_js_upload_progress'), | |
'%plupload_js_error%' => $this->getOption('plupload_js_error'), | |
'%plupload_js_file_uploaded%' => $this->getOption('plupload_js_file_uploaded'), | |
'%plupload_js_reset_uploader%' => $this->getOption('plupload_js_reset_uploader'), | |
'%plupload_js_handle_error%' => $this->getOption('plupload_js_handle_error'), | |
'%plupload_js_handle_success%' => $this->getOption('plupload_js_handle_success'), | |
'%plupload_js_hide_uploader%' => $this->getOption('plupload_js_hide_uploader'), | |
'%plupload_js_reset_errors%' => $this->getOption('plupload_js_reset_errors'), | |
'%plupload_js_post_init%' => $this->getOption('plupload_js_post_init'), | |
) | |
); | |
$fileTemplate = $this->getOption('is_image') | |
? $this->getOption('image_template') | |
: $this->getOption('file_template') | |
; | |
$uploadingText = strtr( | |
addcslashes($this->getOption('uploading_text'), '"'), | |
array( | |
'%filename%' => '" + filename + "' | |
) | |
); | |
// add replacement values | |
$javascript = strtr( | |
$javascript, | |
array( | |
'%uploader_id%' => addcslashes($this->_uploaderId, '"'), | |
'%container_id%' => addcslashes($this->_containerId, '"'), | |
'%delete_id%' => addcslashes($this->_deleteId, '"'), | |
'%uploader_json%' => json_encode($this->_pluploadOptions), | |
'%file_template%' => addcslashes( | |
strtr($fileTemplate, array('%to_delete_class%' => '')) | |
, '"' | |
), | |
'%path%' => addcslashes(rtrim($this->getOption('file_dir'), '/'), '"'), | |
'%hidden_field_name%' => addcslashes($this->_hiddenFieldName, '"'), | |
'%hidden_field_id%' => addcslashes($this->_hiddenFieldId, '"'), | |
'%ul_error_class%' => | |
addcslashes($this->getOption('ul_error_class', ''), '"'), | |
'%li_error_class%' => | |
addcslashes($this->getOption('li_error_class', ''), '"'), | |
'%plupload_500_error_message%' => | |
addcslashes($this->getOption('plupload_500_error_message', ''), '"'), | |
'%plupload_file_size_error_message%' => | |
addcslashes( | |
$this->getOption('plupload_file_size_error_message'), '"' | |
), | |
'%plupload_extension_error_message%' => | |
addcslashes( | |
$this->getOption('plupload_extension_error_message'), '"' | |
), | |
'%to_delete_class%' => addcslashes($this->getOption('to_delete_class'), '"'), | |
'%uploading_text%' => $uploadingText | |
) | |
); | |
return sprintf(<<<EOF | |
<script type="text/javascript"> | |
//<![CDATA[ | |
%s | |
//]]> | |
</script> | |
EOF | |
, | |
$this->minifyJavascript($javascript) | |
); | |
} | |
/** | |
* Get the javascripts for this widget | |
* | |
* @see parent | |
* @return array | |
*/ | |
public function getJavaScripts() | |
{ | |
$options = $this->getPluploadOptions(); | |
$javascripts = array(); | |
if ( | |
isset($options['runtimes']) | |
&& | |
strpos($options['runtimes'], 'gears') !== false | |
) | |
{ | |
$javascripts[] = | |
$this->getOption('plupload_path_to_js') . '/gears_init.js' | |
; | |
} | |
$javascripts[] = | |
$this->getOption('plupload_path_to_js') . '/plupload.full.min.js' | |
; | |
return array_merge( | |
parent::getJavaScripts(), | |
$javascripts | |
); | |
} | |
/** | |
* Build and return plupload options | |
* | |
* @return array | |
*/ | |
protected function getPluploadOptions() | |
{ | |
return array_merge( | |
array( | |
'flash_swf_url' => | |
$this->getOption('plupload_path_to_js') . '/plupload.flash.swf', | |
'silverlight_xap_url' => | |
$this->getOption('plupload_path_to_js') . '/plupload.silverlight.xap' | |
), | |
$this->_pluploadDefaultOptions, | |
sfConfig::get('app_rbUtilityPlugin_plupload_options', array()), | |
$this->getOption('plupload_options') | |
); | |
} | |
/** | |
* Get the default options for plupload | |
* | |
* @return array | |
*/ | |
public function getDefaultPluploadOptions() | |
{ | |
return array( | |
'runtimes' => 'gears,flash,html5,silverlight', | |
'chunk_size' => '1MB', | |
'multi_selection' => false, | |
'unique_names' => true | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment