/ */ class Content_Form_Element_File extends Zend_Form_Element_File implements P4Cms_Content_EnhancedElementInterface, P4Cms_Record_EnhancedElementInterface { const ACTION_KEEP = 'keep'; const ACTION_REMOVE = 'remove'; const ACTION_REPLACE = 'replace'; protected $_contentRecord = null; protected $_existingFileInfo = null; protected $_formData = null; /** * Set information about an existing file. * * Info can be set by passing an array of key/value pairs, or * by passing a string (key) as the first argument and a value * for the second argument to set a specific property. * * If there is an existing file, but nothing is known about * it, pass an empty array. Setting to false or null will indicate * there is no existing file (the default case). * * File info may contain any key/value pairs. The following keys * are used to inform how the file element is rendered: * * - filename * - mimeType * - iconUri * * @param array|string $info information about an existing file * if a string is given, sets the named * key in the file info array. * @param string $value optional - value to set when called * with a string (key) for the first param. * @return P4Cms_Form_Element_ImageFile provides fluent interface. */ public function setExistingFileInfo($info, $value = null) { if (is_string($info)) { $key = $info; $info = $this->_existingFileInfo ?: array(); $info[$key] = $value; } $this->_existingFileInfo = $info; return $this; } /** * Get any available information about an existing file. * * @return array information about an existing file * false if no existing file is set. */ public function getExistingFileInfo() { if ($this->hasExistingFile()) { return $this->_existingFileInfo; } return false; } /** * Determine if this field has an existing file set. * Set existing file info to indicate there is an existing file. * * @return bool true if there is an existing file; false otherwise. */ public function hasExistingFile() { if (is_array($this->_existingFileInfo) || array_key_exists($this->getActionFieldName(), $this->getFormData()) ) { return true; } else { return false; } } /** * Determine if the existing file has been marked for removal. * * @return bool true if the file is marked for removal; false otherwise. */ public function isRemoved() { return ($this->getActionFieldValue() === self::ACTION_REMOVE); } /** * Determine if the existing file has been marked for replacement. * * @return bool true if the file is marked for replacement; false otherwise. */ public function isReplaced() { return ($this->getActionFieldValue() === self::ACTION_REPLACE); } /** * Get the name of the action field (which indicates the disposition * of the existing file). Derived from this file element's name. * * @return string the name of the 'action' field associated with this file input. */ public function getActionFieldName() { return $this->getName() . "-existing-file-action"; } /** * Get the action for the existing file (e.g. keep, remove, replace). * Defaults to 'keep'. * * @return string the value of the existing file action field. */ public function getActionFieldValue() { $data = $this->getFormData(); $action = $this->getActionFieldName(); return array_key_exists($action, $data) ? $data[$action] : self::ACTION_KEEP; } /** * Get the contents of the uploaded file on the server. */ public function getFileContent() { return file_get_contents($this->getFileTempName()); } /** * Get the temporary name of the uploaded file on the server. */ public function getFileTempName() { if (!$this->isUploaded()) { throw new Content_Exception("Cannot get file temp name if file not uploaded."); } $fileInfo = $this->getFileInfo(); return $fileInfo[$this->getName()]['tmp_name']; } /** * Set the form data from which the existing file action will be read. * Normally it is unnecessary to set this as the element will read from * $_POST by default. * * @param array $data the form data to pull the file action from. * @return P4Cms_Form_Element_ImageFile provides fluent interface. */ public function setFormData($data) { $this->_formData = $data; } /** * Get the form data from which the existing file action will be read. * Reads from $_POST directly unless data has been explicitly set via * setFormData(). * * @return array the form data to pull file action from. */ public function getFormData() { return isset($this->_formData) ? $this->_formData : $_POST; } /** * Get the associated content record (if set). * * @return null|P4Cms_Content the associated content record or null if none set. */ public function getContentRecord() { return $this->_contentRecord; } /** * Set the associated content record for this element. * * @param P4Cms_Content $content the associated content record for this element. */ public function setContentRecord($content) { $this->_contentRecord = $content; } /** * Get the default display decorators to use when rendering * content elements of this type. * * @return array decorators configuration array suitable for passing * to element setDecorators(). */ public function getDefaultDisplayDecorators() { return array( array( 'decorator' => 'DisplayFileLink', 'options' => array( 'placement' => Content_Form_Decorator_DisplayFileLink::REPLACE ) ) ); } /** * Retrieve all validators; proxy to adapter * * @return array */ public function getValidators() { $adapter = $this->getTransferAdapter(); $validators = $adapter->getValidators($this->getName()); if (!$validators) { $validators = $adapter->getValidators(); } return $validators; } /** * Validate upload * Overridden to handle content edit's "keep existing file" option. * * @param string $value File, can be optional, give null to validate all files * @param mixed $context optional context * @return bool */ public function isValid($value, $context = null) { if ($this->_validated) { return true; } $isRequired = $this->isRequired(); if ($isRequired && $this->hasExistingFile() && $this->getActionFieldValue() == self::ACTION_KEEP ) { $this->setRequired(false); } $result = parent::isValid($value, $context); $this->setRequired($isRequired); return $result; } /** * Set the file value on the given record. * * File elements require special handling. Three cases: * * - File is flagged for removal, clear the record value and metadata. * - New file is uploaded, set the record value and metadata. * - Existing file is 'kept', do nothing. * * @param P4Cms_Record $record the record to populate * @return P4Cms_Record_EnhancedElementInterface provides fluent interface */ public function populateRecord(P4Cms_Record $record) { $field = $this->getName(); // if file is flagged for removal, clear it. if ($this->isRemoved()) { $record->setValue($field, null); $record->setFieldMetadata($field, null); } // if a new file has been uploaded, store it. if ($this->isUploaded() && ($this->isReplaced() || !$this->hasExistingFile())) { $record->setValueFromFile( $field, $this->getFileTempName(), basename($this->getFileName()), $this->getMimeType() ); } return $this; } /** * Populate the file element from the given record. * If there is an existing file, set file info on the form. * * @param P4Cms_Record $record the record to populate from * @return P4Cms_Record_EnhancedElementInterface provides fluent interface */ public function populateFromRecord(P4Cms_Record $record) { $field = $this->getName(); // nothing to do if record doesn't have a field with this name. if (!$record->hasField($field)) { return $this; } $metadata = $record->getFieldMetadata($field); if (is_array($metadata) && !empty($metadata)) { $this->setExistingFileInfo($metadata); } return $this; } }