API Refactor UploadField, FileField and AssetField into traits Uploadable and FileUploadable

This commit is contained in:
Damian Mooyman 2016-11-04 15:40:34 +13:00
parent 8e4ed776d8
commit 7cba50e3a5
7 changed files with 2601 additions and 2558 deletions

View File

@ -105,7 +105,7 @@ class Upload extends Controller
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
$this->validator = Injector::inst()->create('SilverStripe\\Assets\\Upload_Validator'); $this->validator = Upload_Validator::create();
$this->replaceFile = self::config()->replaceFile; $this->replaceFile = self::config()->replaceFile;
} }
@ -167,7 +167,7 @@ class Upload extends Controller
{ {
// Validate filename // Validate filename
$filename = $this->getValidFilename($tmpFile, $folderPath); $filename = $this->getValidFilename($tmpFile, $folderPath);
if (!$filename) { if(!$filename) {
return false; return false;
} }
@ -196,14 +196,14 @@ class Upload extends Controller
// Validate filename // Validate filename
$filename = $this->getValidFilename($tmpFile, $folderPath); $filename = $this->getValidFilename($tmpFile, $folderPath);
if (!$filename) { if(!$filename) {
return false; return false;
} }
$filename = $this->resolveExistingFile($filename); $filename = $this->resolveExistingFile($filename);
// Save changes to underlying record (if it's a DataObject) // Save changes to underlying record (if it's a DataObject)
$this->storeTempFile($tmpFile, $filename, $this->file); $this->storeTempFile($tmpFile, $filename, $this->file);
if ($this->file instanceof DataObject) { if($this->file instanceof DataObject) {
$this->file->write(); $this->file->write();
} }
@ -244,7 +244,7 @@ class Upload extends Controller
*/ */
protected function getValidFilename($tmpFile, $folderPath = null) protected function getValidFilename($tmpFile, $folderPath = null)
{ {
if (!is_array($tmpFile)) { if(!is_array($tmpFile)) {
throw new InvalidArgumentException( throw new InvalidArgumentException(
"Upload::load() Not passed an array. Most likely, the form hasn't got the right enctype" "Upload::load() Not passed an array. Most likely, the form hasn't got the right enctype"
); );
@ -253,18 +253,18 @@ class Upload extends Controller
// Validate // Validate
$this->clearErrors(); $this->clearErrors();
$valid = $this->validate($tmpFile); $valid = $this->validate($tmpFile);
if (!$valid) { if(!$valid) {
return false; return false;
} }
// Clean filename // Clean filename
if (!$folderPath) { if(!$folderPath) {
$folderPath = $this->config()->uploads_folder; $folderPath = $this->config()->uploads_folder;
} }
$nameFilter = FileNameFilter::create(); $nameFilter = FileNameFilter::create();
$file = $nameFilter->filter($tmpFile['name']); $file = $nameFilter->filter($tmpFile['name']);
$filename = basename($file); $filename = basename($file);
if ($folderPath) { if($folderPath) {
$filename = File::join_paths($folderPath, $filename); $filename = File::join_paths($folderPath, $filename);
} }
return $filename; return $filename;
@ -283,7 +283,7 @@ class Upload extends Controller
protected function resolveExistingFile($filename) protected function resolveExistingFile($filename)
{ {
// Create a new file record (or try to retrieve an existing one) // Create a new file record (or try to retrieve an existing one)
if (!$this->file) { if(!$this->file) {
$fileClass = File::get_class_for_file_extension( $fileClass = File::get_class_for_file_extension(
File::get_file_extension($filename) File::get_file_extension($filename)
); );
@ -291,7 +291,7 @@ class Upload extends Controller
} }
// Skip this step if not writing File dataobjects // Skip this step if not writing File dataobjects
if (! ($this->file instanceof File)) { if(! ($this->file instanceof File) ) {
return $filename; return $filename;
} }
@ -299,9 +299,9 @@ class Upload extends Controller
$existing = File::find($filename); $existing = File::find($filename);
// If replacing (or no file exists) confirm this filename is safe // If replacing (or no file exists) confirm this filename is safe
if ($this->replaceFile || !$existing) { if($this->replaceFile || !$existing) {
// If replacing files, make sure to update the OwnerID // If replacing files, make sure to update the OwnerID
if (!$this->file->ID && $this->replaceFile && $existing) { if(!$this->file->ID && $this->replaceFile && $existing) {
$this->file = $existing; $this->file = $existing;
$this->file->OwnerID = Member::currentUserID(); $this->file->OwnerID = Member::currentUserID();
} }
@ -311,8 +311,8 @@ class Upload extends Controller
// if filename already exists, version the filename (e.g. test.gif to test-v2.gif, test-v2.gif to test-v3.gif) // if filename already exists, version the filename (e.g. test.gif to test-v2.gif, test-v2.gif to test-v3.gif)
$renamer = $this->getNameGenerator($filename); $renamer = $this->getNameGenerator($filename);
foreach ($renamer as $newName) { foreach($renamer as $newName) {
if (!File::find($newName)) { if(!File::find($newName)) {
return $newName; return $newName;
} }
} }
@ -353,7 +353,7 @@ class Upload extends Controller
$validator = $this->validator; $validator = $this->validator;
$validator->setTmpFile($tmpFile); $validator->setTmpFile($tmpFile);
$isValid = $validator->validate(); $isValid = $validator->validate();
if ($validator->getErrors()) { if($validator->getErrors()) {
$this->errors = array_merge($this->errors, $validator->getErrors()); $this->errors = array_merge($this->errors, $validator->getErrors());
} }
return $isValid; return $isValid;

View File

@ -3,10 +3,12 @@
namespace SilverStripe\Assets; namespace SilverStripe\Assets;
use SilverStripe\Core\Config\Config; use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injectable;
use SilverStripe\Dev\SapphireTest; use SilverStripe\Dev\SapphireTest;
class Upload_Validator class Upload_Validator
{ {
use Injectable;
/** /**
* Contains a list of the max file sizes shared by * Contains a list of the max file sizes shared by

View File

@ -14,7 +14,6 @@ use SilverStripe\Core\Convert;
use SilverStripe\Core\Injector\Injector; use SilverStripe\Core\Injector\Injector;
use SilverStripe\Control\HTTPRequest; use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse; use SilverStripe\Control\HTTPResponse;
use SilverStripe\View\Requirements;
use Exception; use Exception;
/** /**
@ -27,8 +26,9 @@ use Exception;
* - Files can't be edited once uploaded. * - Files can't be edited once uploaded.
* - Attached files can only be removed, not deleted. * - Attached files can only be removed, not deleted.
*/ */
class AssetField extends FileField class AssetField extends FormField
{ {
use Uploadable;
/** /**
* @var array * @var array
@ -165,6 +165,7 @@ class AssetField extends FileField
$this->ufConfig = array_merge($this->ufConfig, self::config()->defaultConfig); $this->ufConfig = array_merge($this->ufConfig, self::config()->defaultConfig);
$this->constructUploadable();
parent::__construct($name, $title); parent::__construct($name, $title);
// AssetField always uses rename replacement method // AssetField always uses rename replacement method
@ -208,11 +209,11 @@ class AssetField extends FileField
*/ */
public function canPreviewFolder() public function canPreviewFolder()
{ {
if (!$this->isActive()) { if(!$this->isActive()) {
return false; return false;
} }
$can = $this->getConfig('canPreviewFolder'); $can = $this->getConfig('canPreviewFolder');
if (is_bool($can)) { if(is_bool($can)) {
return $can; return $can;
} }
return Permission::check($can); return Permission::check($can);
@ -282,14 +283,14 @@ class AssetField extends FileField
public function setValue($value, $record = null) public function setValue($value, $record = null)
{ {
// Extract value from underlying record // Extract value from underlying record
if (empty($value) && $this->getName() && $record instanceof DataObject) { if(empty($value) && $this->getName() && $record instanceof DataObject) {
$name = $this->getName(); $name = $this->getName();
$value = $record->$name; $value = $record->$name;
} }
// Convert asset container to tuple value // Convert asset container to tuple value
if ($value instanceof AssetContainer) { if($value instanceof AssetContainer) {
if ($value->exists()) { if($value->exists()) {
$value = array( $value = array(
'Filename' => $value->getFilename(), 'Filename' => $value->getFilename(),
'Hash' => $value->getHash(), 'Hash' => $value->getHash(),
@ -304,9 +305,9 @@ class AssetField extends FileField
// trigger a single or multiple file submission. Note that this may be // trigger a single or multiple file submission. Note that this may be
// included in addition to re-submitted File IDs as above, so these // included in addition to re-submitted File IDs as above, so these
// should be added to the list instead of operated on independently. // should be added to the list instead of operated on independently.
if ($uploadedFile = $this->extractUploadedFileData($value)) { if($uploadedFile = $this->extractUploadedFileData($value)) {
$value = $this->saveTemporaryFile($uploadedFile, $error); $value = $this->saveTemporaryFile($uploadedFile, $error);
if (!$value) { if(!$value) {
throw new ValidationException($error); throw new ValidationException($error);
} }
} }
@ -325,11 +326,11 @@ class AssetField extends FileField
{ {
// Check required relation details are available // Check required relation details are available
$name = $this->getName(); $name = $this->getName();
if (!$name) { if(!$name) {
return $this; return $this;
} }
$value = $this->Value(); $value = $this->Value();
foreach (array('Filename', 'Hash', 'Variant') as $part) { foreach(array('Filename', 'Hash', 'Variant') as $part) {
$partValue = isset($value[$part]) $partValue = isset($value[$part])
? $value[$part] ? $value[$part]
: null; : null;
@ -363,7 +364,7 @@ class AssetField extends FileField
*/ */
public function getConfig($key) public function getConfig($key)
{ {
if (isset($this->ufConfig[$key])) { if(isset($this->ufConfig[$key])) {
return $this->ufConfig[$key]; return $this->ufConfig[$key];
} }
} }
@ -396,11 +397,11 @@ class AssetField extends FileField
*/ */
public function canUpload() public function canUpload()
{ {
if (!$this->isActive()) { if(!$this->isActive()) {
return false; return false;
} }
$can = $this->getConfig('canUpload'); $can = $this->getConfig('canUpload');
if (is_bool($can)) { if(is_bool($can)) {
return $can; return $can;
} }
return Permission::check($can); return Permission::check($can);
@ -519,10 +520,10 @@ class AssetField extends FileField
public function extraClass() public function extraClass()
{ {
if ($this->isDisabled()) { if($this->isDisabled()) {
$this->addExtraClass('disabled'); $this->addExtraClass('disabled');
} }
if ($this->isReadonly()) { if($this->isReadonly()) {
$this->addExtraClass('readonly'); $this->addExtraClass('readonly');
} }
@ -582,7 +583,7 @@ class AssetField extends FileField
$value = $this->Value(); $value = $this->Value();
// If there is no file then quit // If there is no file then quit
if (!$value) { if(!$value) {
return true; return true;
} }
@ -603,8 +604,8 @@ class AssetField extends FileField
$this->getUpload()->validate($tmpFile); $this->getUpload()->validate($tmpFile);
// Check all errors // Check all errors
if ($errors = $this->getUpload()->getErrors()) { if($errors = $this->getUpload()->getErrors()) {
foreach ($errors as $error) { foreach($errors as $error) {
$validator->validationError($name, $error, "validation"); $validator->validationError($name, $error, "validation");
} }
return false; return false;
@ -625,19 +626,19 @@ class AssetField extends FileField
// <input name='{$Name}[Upload]' /> for multiple file uploads // <input name='{$Name}[Upload]' /> for multiple file uploads
// Skip empty file // Skip empty file
if (empty($postVars['tmp_name'])) { if(empty($postVars['tmp_name'])) {
return null; return null;
} }
// Return single level array for posted file // Return single level array for posted file
/** @skipUpgrade */ /** @skipUpgrade */
if (empty($postVars['tmp_name']['Upload'])) { if(empty($postVars['tmp_name']['Upload'])) {
return $postVars; return $postVars;
} }
// Extract posted feedback value // Extract posted feedback value
$tmpFile = array(); $tmpFile = array();
foreach (array('name', 'type', 'tmp_name', 'error', 'size') as $field) { foreach(array('name', 'type', 'tmp_name', 'error', 'size') as $field) {
/** @skipUpgrade */ /** @skipUpgrade */
$tmpFile[$field] = $postVars[$field]['Upload']; $tmpFile[$field] = $postVars[$field]['Upload'];
} }
@ -660,7 +661,7 @@ class AssetField extends FileField
return null; return null;
} }
if ($tmpFile['error']) { if($tmpFile['error']) {
$error = $tmpFile['error']; $error = $tmpFile['error'];
return null; return null;
} }
@ -726,7 +727,7 @@ class AssetField extends FileField
*/ */
public function upload(HTTPRequest $request) public function upload(HTTPRequest $request)
{ {
if ($this->isDisabled() || $this->isReadonly() || !$this->canUpload()) { if($this->isDisabled() || $this->isReadonly() || !$this->canUpload()) {
return $this->httpError(403); return $this->httpError(403);
} }
@ -734,7 +735,7 @@ class AssetField extends FileField
$token = $this $token = $this
->getForm() ->getForm()
->getSecurityToken(); ->getSecurityToken();
if (!$token->checkRequest($request)) { if(!$token->checkRequest($request)) {
return $this->httpError(400); return $this->httpError(400);
} }
@ -744,14 +745,14 @@ class AssetField extends FileField
// Extract uploaded files from Form data // Extract uploaded files from Form data
$uploadedFile = $this->extractUploadedFileData($postVars); $uploadedFile = $this->extractUploadedFileData($postVars);
if (!$uploadedFile) { if(!$uploadedFile) {
return $this->httpError(400); return $this->httpError(400);
} }
// Save the temporary files into a File objects // Save the temporary files into a File objects
// and save data/error on a per file basis // and save data/error on a per file basis
$result = $this->saveTemporaryFile($uploadedFile, $error); $result = $this->saveTemporaryFile($uploadedFile, $error);
if (empty($result)) { if(empty($result)) {
$return = array('error' => $error); $return = array('error' => $error);
} else { } else {
$return = $this->encodeAssetAttributes($result['Filename'], $result['Hash'], $result['Variant']); $return = $this->encodeAssetAttributes($result['Filename'], $result['Hash'], $result['Variant']);
@ -792,7 +793,7 @@ class AssetField extends FileField
// Check record and name // Check record and name
$name = $this->getName(); $name = $this->getName();
$record = $this->getRecord(); $record = $this->getRecord();
if (empty($name) || empty($record)) { if(empty($name) || empty($record)) {
return $default; return $default;
} else { } else {
$class = $record->getRelationClass($name); $class = $record->getRelationClass($name);
@ -807,4 +808,11 @@ class AssetField extends FileField
{ {
return Injector::inst()->get('AssetStore'); return Injector::inst()->get('AssetStore');
} }
public function getAttributes() {
return array_merge(
parent::getAttributes(),
['type' => 'file']
);
}
} }

View File

@ -2,10 +2,8 @@
namespace SilverStripe\Forms; namespace SilverStripe\Forms;
use SilverStripe\Assets\Upload_Validator;
use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DataObjectInterface; use SilverStripe\ORM\DataObjectInterface;
use SilverStripe\Assets\Upload;
use SilverStripe\Assets\File; use SilverStripe\Assets\File;
use SilverStripe\Core\Object; use SilverStripe\Core\Object;
@ -48,8 +46,8 @@ use SilverStripe\Core\Object;
* } * }
* </code> * </code>
*/ */
class FileField extends FormField class FileField extends FormField {
{ use Uploadable;
/** /**
* Flag to automatically determine and save a has_one-relationship * Flag to automatically determine and save a has_one-relationship
@ -59,24 +57,7 @@ class FileField extends FormField
* *
* @var boolean * @var boolean
*/ */
public $relationAutoSetting = true; protected $relationAutoSetting = true;
/**
* Upload object (needed for validation
* and actually moving the temporary file
* created by PHP).
*
* @var Upload
*/
protected $upload;
/**
* Partial filesystem path relative to /assets directory.
* Defaults to Upload::$uploads_folder.
*
* @var string
*/
protected $folderName = false;
/** /**
* Create a new file field. * Create a new file field.
@ -85,9 +66,8 @@ class FileField extends FormField
* @param string $title The field label. * @param string $title The field label.
* @param int $value The value of the field. * @param int $value The value of the field.
*/ */
public function __construct($name, $title = null, $value = null) public function __construct($name, $title = null, $value = null) {
{ $this->constructUploadable();
$this->upload = Upload::create();
parent::__construct($name, $title, $value); parent::__construct($name, $title, $value);
} }
@ -95,8 +75,7 @@ class FileField extends FormField
* @param array $properties * @param array $properties
* @return string * @return string
*/ */
public function Field($properties = array()) public function Field($properties = array()) {
{
$properties = array_merge($properties, array( $properties = array_merge($properties, array(
'MaxFileSize' => $this->getValidator()->getAllowedMaxFileSize() 'MaxFileSize' => $this->getValidator()->getAllowedMaxFileSize()
)); ));
@ -104,8 +83,7 @@ class FileField extends FormField
return parent::Field($properties); return parent::Field($properties);
} }
public function getAttributes() public function getAttributes() {
{
return array_merge( return array_merge(
parent::getAttributes(), parent::getAttributes(),
array('type' => 'file') array('type' => 'file')
@ -115,9 +93,8 @@ class FileField extends FormField
/** /**
* @param DataObject|DataObjectInterface $record * @param DataObject|DataObjectInterface $record
*/ */
public function saveInto(DataObjectInterface $record) public function saveInto(DataObjectInterface $record) {
{ if(!isset($_FILES[$this->name])) {
if (!isset($_FILES[$this->name])) {
return; return;
} }
@ -126,17 +103,17 @@ class FileField extends FormField
); );
/** @var File $file */ /** @var File $file */
if ($this->relationAutoSetting) { if($this->relationAutoSetting) {
// assume that the file is connected via a has-one // assume that the file is connected via a has-one
$objectClass = DataObject::getSchema()->hasOneComponent(get_class($record), $this->name); $objectClass = DataObject::getSchema()->hasOneComponent(get_class($record), $this->name);
if ($objectClass === File::class || empty($objectClass)) { if($objectClass === File::class || empty($objectClass)) {
// Create object of the appropriate file class // Create object of the appropriate file class
$file = Object::create($fileClass); $file = Object::create($fileClass);
} else { } else {
// try to create a file matching the relation // try to create a file matching the relation
$file = Object::create($objectClass); $file = Object::create($objectClass);
} }
} elseif ($record instanceof File) { } else if($record instanceof File) {
$file = $record; $file = $record;
} else { } else {
$file = Object::create($fileClass); $file = Object::create($fileClass);
@ -144,11 +121,11 @@ class FileField extends FormField
$this->upload->loadIntoFile($_FILES[$this->name], $file, $this->getFolderName()); $this->upload->loadIntoFile($_FILES[$this->name], $file, $this->getFolderName());
if ($this->upload->isError()) { if($this->upload->isError()) {
return; return;
} }
if ($this->relationAutoSetting) { if($this->relationAutoSetting) {
if (empty($objectClass)) { if (empty($objectClass)) {
return; return;
} }
@ -159,73 +136,21 @@ class FileField extends FormField
} }
} }
public function Value() public function Value() {
{
return isset($_FILES[$this->getName()]) ? $_FILES[$this->getName()] : null; return isset($_FILES[$this->getName()]) ? $_FILES[$this->getName()] : null;
} }
/** public function validate($validator) {
* Get custom validator for this field if(!isset($_FILES[$this->name])) return true;
*
* @return Upload_Validator
*/
public function getValidator()
{
return $this->upload->getValidator();
}
/**
* Set custom validator for this field
*
* @param Upload_Validator $validator
* @return $this Self reference
*/
public function setValidator($validator)
{
$this->upload->setValidator($validator);
return $this;
}
/**
* Sets the upload folder name
*
* @param string $folderName
* @return FileField Self reference
*/
public function setFolderName($folderName)
{
$this->folderName = $folderName;
return $this;
}
/**
* Gets the upload folder name
*
* @return string
*/
public function getFolderName()
{
return ($this->folderName !== false)
? $this->folderName
: Upload::config()->uploads_folder;
}
public function validate($validator)
{
if (!isset($_FILES[$this->name])) {
return true;
}
$tmpFile = $_FILES[$this->name]; $tmpFile = $_FILES[$this->name];
$valid = $this->upload->validate($tmpFile); $valid = $this->upload->validate($tmpFile);
if (!$valid) { if(!$valid) {
$errors = $this->upload->getErrors(); $errors = $this->upload->getErrors();
if ($errors) { if($errors) foreach($errors as $error) {
foreach ($errors as $error) {
$validator->validationError($this->name, $error, "validation"); $validator->validationError($this->name, $error, "validation");
} }
}
return false; return false;
} }
@ -233,67 +158,23 @@ class FileField extends FormField
} }
/** /**
* Retrieves the Upload handler * Set if relation can be automatically assigned to the underlying dataobject
* *
* @return Upload * @param bool $auto
* @return $this
*/ */
public function getUpload() public function setRelationAutoSetting($auto) {
{ $this->relationAutoSetting = $auto;
return $this->upload;
}
/**
* Sets the upload handler
*
* @param Upload $upload
* @return FileField Self reference
*/
public function setUpload(Upload $upload)
{
$this->upload = $upload;
return $this; return $this;
} }
/** /**
* Limit allowed file extensions. Empty by default, allowing all extensions. * Check if relation can be automatically assigned to the underlying dataobject
* To allow files without an extension, use an empty string.
* See {@link File::$allowed_extensions} to get a good standard set of
* extensions that are typically not harmful in a webserver context.
* See {@link setAllowedMaxFileSize()} to limit file size by extension.
* *
* @param array $rules List of extensions * @return bool
* @return $this
*/ */
public function setAllowedExtensions($rules) public function getRelationAutoSetting() {
{ return $this->relationAutoSetting;
$this->getValidator()->setAllowedExtensions($rules);
return $this;
} }
/**
* Limit allowed file extensions by specifying categories of file types.
* These may be 'image', 'image/supported', 'audio', 'video', 'archive', 'flash', or 'document'
* See {@link File::$allowed_extensions} for details of allowed extensions
* for each of these categories
*
* @param string $category Category name
* @param string,... $categories Additional category names
* @return $this
*/
public function setAllowedFileCategories($category)
{
$extensions = File::get_category_extensions(func_get_args());
return $this->setAllowedExtensions($extensions);
}
/**
* Returns list of extensions allowed by this field, or an empty array
* if there is no restriction
*
* @return array
*/
public function getAllowedExtensions()
{
return $this->getValidator()->getAllowedExtensions();
}
} }

View File

@ -0,0 +1,399 @@
<?php
namespace SilverStripe\Forms;
use Exception;
use InvalidArgumentException;
use SilverStripe\Assets\File;
use SilverStripe\Assets\Storage\AssetContainer;
use SilverStripe\Core\Object;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DataObjectInterface;
use SilverStripe\ORM\RelationList;
use SilverStripe\ORM\SS_List;
use SilverStripe\ORM\UnsavedRelationList;
use SilverStripe\ORM\ValidationException;
/**
* Provides operations for reading and writing uploaded files to/from
* {@see File} dataobject instances.
* Allows writing to a parent record with the following relation types:
* - has_one
* - has_many
* - many_many
* Additionally supports writing directly to the File table not attached
* to any parent record.
*
* @mixin FormField
*/
trait FileUploadable {
use Uploadable;
/**
* Flag to automatically determine and save a has_one-relationship
* on the saved record (e.g. a "Player" has_one "PlayerImage" would
* trigger saving the ID of newly created file into "PlayerImageID"
* on the record).
*
* @var boolean
*/
public $relationAutoSetting = true;
/**
* Parent data record. Will be infered from parent form or controller if blank.
*
* @var DataObject
*/
protected $record;
/**
* Items loaded into this field. May be a RelationList, or any other SS_List
*
* @var SS_List
*/
protected $items;
protected function constructFileUploadable() {
$this->constructUploadable();
}
/**
* Force a record to be used as "Parent" for uploaded Files (eg a Page with a has_one to File)
*
* @param DataObject $record
* @return $this
*/
public function setRecord($record) {
$this->record = $record;
return $this;
}
/**
* Get the record to use as "Parent" for uploaded Files (eg a Page with a has_one to File) If none is set, it will
* use Form->getRecord() or Form->Controller()->data()
*
* @return DataObject
*/
public function getRecord() {
if ($this->record) {
return $this->record;
}
if (!$this->getForm()) {
return null;
}
// Get record from form
$record = $this->getForm()->getRecord();
if ($record && ($record instanceof DataObject)) {
$this->record = $record;
return $record;
}
// Get record from controller
$controller = $this->getForm()->getController();
if ($controller
&& $controller->hasMethod('data')
&& ($record = $controller->data())
&& ($record instanceof DataObject)
) {
$this->record = $record;
return $record;
}
return null;
}
/**
* Loads the related record values into this field. This can be uploaded
* in one of three ways:
*
* - By passing in a list of file IDs in the $value parameter (an array with a single
* key 'Files', with the value being the actual array of IDs).
* - By passing in an explicit list of File objects in the $record parameter, and
* leaving $value blank.
* - By passing in a dataobject in the $record parameter, from which file objects
* will be extracting using the field name as the relation field.
*
* Each of these methods will update both the items (list of File objects) and the
* field value (list of file ID values).
*
* @param array $value Array of submitted form data, if submitting from a form
* @param array|DataObject|SS_List $record Full source record, either as a DataObject,
* SS_List of items, or an array of submitted form data
* @return $this Self reference
* @throws ValidationException
*/
public function setValue($value, $record = null) {
// If we're not passed a value directly, we can attempt to infer the field
// value from the second parameter by inspecting its relations
$items = new ArrayList();
// Determine format of presented data
if(empty($value) && $record) {
// If a record is given as a second parameter, but no submitted values,
// then we should inspect this instead for the form values
if(($record instanceof DataObject) && $record->hasMethod($this->getName())) {
// If given a dataobject use reflection to extract details
$data = $record->{$this->getName()}();
if($data instanceof DataObject) {
// If has_one, add sole item to default list
$items->push($data);
} elseif($data instanceof SS_List) {
// For many_many and has_many relations we can use the relation list directly
$items = $data;
}
} elseif($record instanceof SS_List) {
// If directly passing a list then save the items directly
$items = $record;
}
} elseif(!empty($value['Files'])) {
// If value is given as an array (such as a posted form), extract File IDs from this
$class = $this->getRelationAutosetClass();
$items = DataObject::get($class)->byIDs($value['Files']);
}
// If javascript is disabled, direct file upload (non-html5 style) can
// trigger a single or multiple file submission. Note that this may be
// included in addition to re-submitted File IDs as above, so these
// should be added to the list instead of operated on independently.
if($uploadedFiles = $this->extractUploadedFileData($value)) {
foreach($uploadedFiles as $tempFile) {
$file = $this->saveTemporaryFile($tempFile, $error);
if($file) {
$items->add($file);
} else {
throw new ValidationException($error);
}
}
}
// Filter items by what's allowed to be viewed
$filteredItems = new ArrayList();
$fileIDs = array();
foreach($items as $file) {
if($file->exists() && $file->canView()) {
$filteredItems->push($file);
$fileIDs[] = $file->ID;
}
}
// Filter and cache updated item list
$this->items = $filteredItems;
// Same format as posted form values for this field. Also ensures that
// $this->setValue($this->getValue()); is non-destructive
$value = $fileIDs ? array('Files' => $fileIDs) : null;
// Set value using parent
parent::setValue($value, $record);
return $this;
}
/**
* Sets the items assigned to this field as an SS_List of File objects.
* Calling setItems will also update the value of this field, as well as
* updating the internal list of File items.
*
* @param SS_List $items
* @return $this self reference
*/
public function setItems(SS_List $items) {
return $this->setValue(null, $items);
}
/**
* Retrieves the current list of files
*
* @return SS_List|File[]
*/
public function getItems() {
return $this->items ? $this->items : new ArrayList();
}
/**
* Retrieves the list of selected file IDs
*
* @return array
*/
public function getItemIDs() {
$value = $this->Value();
return empty($value['Files']) ? array() : $value['Files'];
}
public function Value() {
// Re-override FileField Value to use data value
return $this->dataValue();
}
/**
* @param DataObject|DataObjectInterface $record
* @return $this
*/
public function saveInto(DataObjectInterface $record) {
// Check required relation details are available
$fieldname = $this->getName();
if(!$fieldname) {
return $this;
}
// Get details to save
$idList = $this->getItemIDs();
// Check type of relation
$relation = $record->hasMethod($fieldname) ? $record->$fieldname() : null;
if($relation && ($relation instanceof RelationList || $relation instanceof UnsavedRelationList)) {
// has_many or many_many
$relation->setByIDList($idList);
} elseif($class = DataObject::getSchema()->hasOneComponent(get_class($record), $fieldname)) {
// Assign has_one ID
$id = $idList ? reset($idList) : 0;
$record->{"{$fieldname}ID"} = $id;
// Polymorphic asignment
if ($class === DataObject::class) {
$file = $id ? File::get()->byID($id) : null;
$fileClass = $file ? get_class($file) : File::class;
$record->{"{$fieldname}Class"} = $id ? $fileClass : null;
}
}
return $this;
}
/**
* Loads the temporary file data into a File object
*
* @param array $tmpFile Temporary file data
* @param string $error Error message
* @return AssetContainer File object, or null if error
*/
protected function saveTemporaryFile($tmpFile, &$error = null) {
// Determine container object
$error = null;
$fileObject = null;
if (empty($tmpFile)) {
$error = _t('UploadField.FIELDNOTSET', 'File information not found');
return null;
}
if($tmpFile['error']) {
$error = $tmpFile['error'];
return null;
}
// Search for relations that can hold the uploaded files, but don't fallback
// to default if there is no automatic relation
if ($relationClass = $this->getRelationAutosetClass(null)) {
// Allow File to be subclassed
if($relationClass === File::class && isset($tmpFile['name'])) {
$relationClass = File::get_class_for_file_extension(
File::get_file_extension($tmpFile['name'])
);
}
// Create new object explicitly. Otherwise rely on Upload::load to choose the class.
$fileObject = Object::create($relationClass);
if(! ($fileObject instanceof DataObject) || !($fileObject instanceof AssetContainer)) {
throw new InvalidArgumentException("Invalid asset container $relationClass");
}
}
// Get the uploaded file into a new file object.
try {
$this->getUpload()->loadIntoFile($tmpFile, $fileObject, $this->getFolderName());
} catch (Exception $e) {
// we shouldn't get an error here, but just in case
$error = $e->getMessage();
return null;
}
// Check if upload field has an error
if ($this->getUpload()->isError()) {
$error = implode(' ' . PHP_EOL, $this->getUpload()->getErrors());
return null;
}
// return file
return $this->getUpload()->getFile();
}
/**
* Gets the foreign class that needs to be created, or 'File' as default if there
* is no relationship, or it cannot be determined.
*
* @param string $default Default value to return if no value could be calculated
* @return string Foreign class name.
*/
public function getRelationAutosetClass($default = File::class) {
// Don't autodetermine relation if no relationship between parent record
if(!$this->getRelationAutoSetting()) {
return $default;
}
// Check record and name
$name = $this->getName();
$record = $this->getRecord();
if(empty($name) || empty($record)) {
return $default;
} else {
$class = $record->getRelationClass($name);
return empty($class) ? $default : $class;
}
}
/**
* Set if relation can be automatically assigned to the underlying dataobject
*
* @param bool $auto
* @return $this
*/
public function setRelationAutoSetting($auto) {
$this->relationAutoSetting = $auto;
return $this;
}
/**
* Check if relation can be automatically assigned to the underlying dataobject
*
* @return bool
*/
public function getRelationAutoSetting() {
return $this->relationAutoSetting;
}
/**
* Given an array of post variables, extract all temporary file data into an array
*
* @param array $postVars Array of posted form data
* @return array List of temporary file data
*/
protected function extractUploadedFileData($postVars) {
// Note: Format of posted file parameters in php is a feature of using
// <input name='{$Name}[Uploads][]' /> for multiple file uploads
$tmpFiles = array();
if( !empty($postVars['tmp_name'])
&& is_array($postVars['tmp_name'])
&& !empty($postVars['tmp_name']['Uploads'])
) {
for($i = 0; $i < count($postVars['tmp_name']['Uploads']); $i++) {
// Skip if "empty" file
if(empty($postVars['tmp_name']['Uploads'][$i])) {
continue;
}
$tmpFile = array();
foreach(array('name', 'type', 'tmp_name', 'error', 'size') as $field) {
$tmpFile[$field] = $postVars[$field]['Uploads'][$i];
}
$tmpFiles[] = $tmpFile;
}
} elseif(!empty($postVars['tmp_name'])) {
// Fallback to allow single file uploads (method used by AssetUploadField)
$tmpFiles[] = $postVars;
}
return $tmpFiles;
}
}

View File

@ -14,11 +14,7 @@ use SilverStripe\ORM\SS_List;
use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\ArrayList; use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\ValidationException; use SilverStripe\ORM\ValidationException;
use SilverStripe\ORM\DataObjectInterface;
use SilverStripe\ORM\RelationList;
use SilverStripe\ORM\UnsavedRelationList;
use SilverStripe\Security\Permission; use SilverStripe\Security\Permission;
use SilverStripe\View\Requirements;
use SilverStripe\View\ArrayData; use SilverStripe\View\ArrayData;
use SilverStripe\View\ViewableData; use SilverStripe\View\ViewableData;
use SilverStripe\View\ViewableData_Customised; use SilverStripe\View\ViewableData_Customised;
@ -49,8 +45,8 @@ use Exception;
* Caution: The form field does not include any JavaScript or CSS when used outside of the CMS context, * Caution: The form field does not include any JavaScript or CSS when used outside of the CMS context,
* since the required frontend dependencies are included through CMS bundling. * since the required frontend dependencies are included through CMS bundling.
*/ */
class UploadField extends FileField class UploadField extends FormField {
{ use FileUploadable;
/** /**
* @var array * @var array
@ -86,20 +82,6 @@ class UploadField extends FileField
*/ */
protected $templateFileEdit = null; protected $templateFileEdit = null;
/**
* Parent data record. Will be infered from parent form or controller if blank.
*
* @var DataObject
*/
protected $record;
/**
* Items loaded into this field. May be a RelationList, or any other SS_List
*
* @var SS_List
*/
protected $items;
/** /**
* Config for this field used in the front-end javascript * Config for this field used in the front-end javascript
* (will be merged into the config of the javascript file upload plugin). * (will be merged into the config of the javascript file upload plugin).
@ -235,30 +217,19 @@ class UploadField extends FileField
* @param SS_List $items If no items are defined, the field will try to auto-detect an existing relation on * @param SS_List $items If no items are defined, the field will try to auto-detect an existing relation on
* @link $record}, with the same name as the field name. * @link $record}, with the same name as the field name.
*/ */
public function __construct($name, $title = null, SS_List $items = null) public function __construct($name, $title = null, SS_List $items = null) {
{
// TODO thats the first thing that came to my head, feel free to change it // TODO thats the first thing that came to my head, feel free to change it
$this->addExtraClass('ss-upload'); // class, used by js $this->addExtraClass('ss-upload'); // class, used by js
$this->addExtraClass('ss-uploadfield'); // class, used by css for uploadfield only $this->addExtraClass('ss-uploadfield'); // class, used by css for uploadfield only
$this->ufConfig = self::config()->defaultConfig; $this->ufConfig = self::config()->defaultConfig;
$this->constructFileUploadable();
parent::__construct($name, $title); parent::__construct($name, $title);
if ($items) { if ($items) {
$this->setItems($items); $this->setItems($items);
} }
// filter out '' since this would be a regex problem on JS end
$this->getValidator()->setAllowedExtensions(
array_filter(File::config()->allowed_extensions)
);
// get the lower max size
$maxUpload = File::ini2bytes(ini_get('upload_max_filesize'));
$maxPost = File::ini2bytes(ini_get('post_max_size'));
$this->getValidator()->setAllowedMaxFileSize(min($maxUpload, $maxPost));
} }
/** /**
@ -267,8 +238,7 @@ class UploadField extends FileField
* @param string $template * @param string $template
* @return $this * @return $this
*/ */
public function setTemplateFileButtons($template) public function setTemplateFileButtons($template) {
{
$this->templateFileButtons = $template; $this->templateFileButtons = $template;
return $this; return $this;
} }
@ -276,8 +246,7 @@ class UploadField extends FileField
/** /**
* @return string * @return string
*/ */
public function getTemplateFileButtons() public function getTemplateFileButtons() {
{
return $this->_templates($this->templateFileButtons, '_FileButtons'); return $this->_templates($this->templateFileButtons, '_FileButtons');
} }
@ -287,8 +256,7 @@ class UploadField extends FileField
* @param string $template * @param string $template
* @return $this * @return $this
*/ */
public function setTemplateFileEdit($template) public function setTemplateFileEdit($template) {
{
$this->templateFileEdit = $template; $this->templateFileEdit = $template;
return $this; return $this;
} }
@ -296,8 +264,7 @@ class UploadField extends FileField
/** /**
* @return string * @return string
*/ */
public function getTemplateFileEdit() public function getTemplateFileEdit() {
{
return $this->_templates($this->templateFileEdit, '_FileEdit'); return $this->_templates($this->templateFileEdit, '_FileEdit');
} }
@ -306,11 +273,8 @@ class UploadField extends FileField
* *
* @return boolean * @return boolean
*/ */
public function canPreviewFolder() public function canPreviewFolder() {
{ if(!$this->isActive()) return false;
if (!$this->isActive()) {
return false;
}
$can = $this->getConfig('canPreviewFolder'); $can = $this->getConfig('canPreviewFolder');
return (is_bool($can)) ? $can : Permission::check($can); return (is_bool($can)) ? $can : Permission::check($can);
} }
@ -323,8 +287,7 @@ class UploadField extends FileField
* required permission code * required permission code
* @return UploadField Self reference * @return UploadField Self reference
*/ */
public function setCanPreviewFolder($canPreviewFolder) public function setCanPreviewFolder($canPreviewFolder) {
{
return $this->setConfig('canPreviewFolder', $canPreviewFolder); return $this->setConfig('canPreviewFolder', $canPreviewFolder);
} }
@ -336,8 +299,7 @@ class UploadField extends FileField
* *
* @return boolean * @return boolean
*/ */
public function getOverwriteWarning() public function getOverwriteWarning() {
{
return $this->getConfig('overwriteWarning'); return $this->getConfig('overwriteWarning');
} }
@ -350,8 +312,7 @@ class UploadField extends FileField
* @param boolean $overwriteWarning * @param boolean $overwriteWarning
* @return UploadField Self reference * @return UploadField Self reference
*/ */
public function setOverwriteWarning($overwriteWarning) public function setOverwriteWarning($overwriteWarning) {
{
return $this->setConfig('overwriteWarning', $overwriteWarning); return $this->setConfig('overwriteWarning', $overwriteWarning);
} }
@ -359,8 +320,7 @@ class UploadField extends FileField
* @param string $name * @param string $name
* @return $this * @return $this
*/ */
public function setDisplayFolderName($name) public function setDisplayFolderName($name) {
{
$this->displayFolderName = $name; $this->displayFolderName = $name;
return $this; return $this;
} }
@ -368,155 +328,11 @@ class UploadField extends FileField
/** /**
* @return String * @return String
*/ */
public function getDisplayFolderName() public function getDisplayFolderName() {
{
return $this->displayFolderName; return $this->displayFolderName;
} }
/**
* Force a record to be used as "Parent" for uploaded Files (eg a Page with a has_one to File)
*
* @param DataObject $record
* @return $this
*/
public function setRecord($record)
{
$this->record = $record;
return $this;
}
/**
* Get the record to use as "Parent" for uploaded Files (eg a Page with a has_one to File) If none is set, it will
* use Form->getRecord() or Form->Controller()->data()
*
* @return DataObject
*/
public function getRecord()
{
if (!$this->record && $this->form) {
if (($record = $this->form->getRecord()) && ($record instanceof DataObject)) {
$this->record = $record;
} elseif (($controller = $this->form->getController())
&& $controller->hasMethod('data')
&& ($record = $controller->data())
&& ($record instanceof DataObject)
) {
$this->record = $record;
}
}
return $this->record;
}
/**
* Loads the related record values into this field. UploadField can be uploaded
* in one of three ways:
*
* - By passing in a list of file IDs in the $value parameter (an array with a single
* key 'Files', with the value being the actual array of IDs).
* - By passing in an explicit list of File objects in the $record parameter, and
* leaving $value blank.
* - By passing in a dataobject in the $record parameter, from which file objects
* will be extracting using the field name as the relation field.
*
* Each of these methods will update both the items (list of File objects) and the
* field value (list of file ID values).
*
* @param array $value Array of submitted form data, if submitting from a form
* @param array|DataObject|SS_List $record Full source record, either as a DataObject,
* SS_List of items, or an array of submitted form data
* @return $this Self reference
* @throws ValidationException
*/
public function setValue($value, $record = null)
{
// If we're not passed a value directly, we can attempt to infer the field
// value from the second parameter by inspecting its relations
$items = new ArrayList();
// Determine format of presented data
if (empty($value) && $record) {
// If a record is given as a second parameter, but no submitted values,
// then we should inspect this instead for the form values
if (($record instanceof DataObject) && $record->hasMethod($this->getName())) {
// If given a dataobject use reflection to extract details
$data = $record->{$this->getName()}();
if ($data instanceof DataObject) {
// If has_one, add sole item to default list
$items->push($data);
} elseif ($data instanceof SS_List) {
// For many_many and has_many relations we can use the relation list directly
$items = $data;
}
} elseif ($record instanceof SS_List) {
// If directly passing a list then save the items directly
$items = $record;
}
} elseif (!empty($value['Files'])) {
// If value is given as an array (such as a posted form), extract File IDs from this
$class = $this->getRelationAutosetClass();
$items = DataObject::get($class)->byIDs($value['Files']);
}
// If javascript is disabled, direct file upload (non-html5 style) can
// trigger a single or multiple file submission. Note that this may be
// included in addition to re-submitted File IDs as above, so these
// should be added to the list instead of operated on independently.
if ($uploadedFiles = $this->extractUploadedFileData($value)) {
foreach ($uploadedFiles as $tempFile) {
$file = $this->saveTemporaryFile($tempFile, $error);
if ($file) {
$items->add($file);
} else {
throw new ValidationException($error);
}
}
}
// Filter items by what's allowed to be viewed
$filteredItems = new ArrayList();
$fileIDs = array();
foreach ($items as $file) {
if ($file->exists() && $file->canView()) {
$filteredItems->push($file);
$fileIDs[] = $file->ID;
}
}
// Filter and cache updated item list
$this->items = $filteredItems;
// Same format as posted form values for this field. Also ensures that
// $this->setValue($this->getValue()); is non-destructive
$value = $fileIDs ? array('Files' => $fileIDs) : null;
// Set value using parent
parent::setValue($value, $record);
return $this;
}
/**
* Sets the items assigned to this field as an SS_List of File objects.
* Calling setItems will also update the value of this field, as well as
* updating the internal list of File items.
*
* @param SS_List $items
* @return UploadField self reference
*/
public function setItems(SS_List $items)
{
return $this->setValue(null, $items);
}
/**
* Retrieves the current list of files
*
* @return SS_List
*/
public function getItems()
{
return $this->items ? $this->items : new ArrayList();
}
/** /**
* Retrieves a customised list of all File records to ensure they are * Retrieves a customised list of all File records to ensure they are
@ -524,59 +340,14 @@ class UploadField extends FileField
* *
* @return SS_List[ViewableData_Customised] * @return SS_List[ViewableData_Customised]
*/ */
public function getCustomisedItems() public function getCustomisedItems() {
{
$customised = new ArrayList(); $customised = new ArrayList();
foreach ($this->getItems() as $file) { foreach($this->getItems() as $file) {
$customised->push($this->customiseFile($file)); $customised->push($this->customiseFile($file));
} }
return $customised; return $customised;
} }
/**
* Retrieves the list of selected file IDs
*
* @return array
*/
public function getItemIDs()
{
$value = $this->Value();
return empty($value['Files']) ? array() : $value['Files'];
}
public function Value()
{
// Re-override FileField Value to use data value
return $this->dataValue();
}
/**
* @param DataObject|DataObjectInterface $record
* @return $this
*/
public function saveInto(DataObjectInterface $record)
{
// Check required relation details are available
$fieldname = $this->getName();
if (!$fieldname) {
return $this;
}
// Get details to save
$idList = $this->getItemIDs();
// Check type of relation
$relation = $record->hasMethod($fieldname) ? $record->$fieldname() : null;
if ($relation && ($relation instanceof RelationList || $relation instanceof UnsavedRelationList)) {
// has_many or many_many
$relation->setByIDList($idList);
} elseif (DataObject::getSchema()->hasOneComponent(get_class($record), $fieldname)) {
// has_one
$record->{"{$fieldname}ID"} = $idList ? reset($idList) : 0;
}
return $this;
}
/** /**
* Customises a file with additional details suitable for rendering in the * Customises a file with additional details suitable for rendering in the
* UploadField.ss template * UploadField.ss template
@ -584,8 +355,7 @@ class UploadField extends FileField
* @param ViewableData|AssetContainer $file * @param ViewableData|AssetContainer $file
* @return ViewableData_Customised * @return ViewableData_Customised
*/ */
protected function customiseFile(AssetContainer $file) protected function customiseFile(AssetContainer $file) {
{
$file = $file->customise(array( $file = $file->customise(array(
'UploadFieldThumbnailURL' => $this->getThumbnailURLForFile($file), 'UploadFieldThumbnailURL' => $this->getThumbnailURLForFile($file),
'UploadFieldDeleteLink' => $this->getItemHandler($file->ID)->DeleteLink(), 'UploadFieldDeleteLink' => $this->getItemHandler($file->ID)->DeleteLink(),
@ -607,8 +377,7 @@ class UploadField extends FileField
* @param mixed $val * @param mixed $val
* @return UploadField self reference * @return UploadField self reference
*/ */
public function setConfig($key, $val) public function setConfig($key, $val) {
{
$this->ufConfig[$key] = $val; $this->ufConfig[$key] = $val;
return $this; return $this;
} }
@ -621,11 +390,8 @@ class UploadField extends FileField
* @param string $key * @param string $key
* @return mixed * @return mixed
*/ */
public function getConfig($key) public function getConfig($key) {
{ if(!isset($this->ufConfig[$key])) return null;
if (!isset($this->ufConfig[$key])) {
return null;
}
return $this->ufConfig[$key]; return $this->ufConfig[$key];
} }
@ -634,8 +400,7 @@ class UploadField extends FileField
* *
* @return boolean * @return boolean
*/ */
public function getAutoUpload() public function getAutoUpload() {
{
return $this->getConfig('autoUpload'); return $this->getConfig('autoUpload');
} }
@ -645,8 +410,7 @@ class UploadField extends FileField
* @param boolean $autoUpload * @param boolean $autoUpload
* @return UploadField Self reference * @return UploadField Self reference
*/ */
public function setAutoUpload($autoUpload) public function setAutoUpload($autoUpload) {
{
return $this->setConfig('autoUpload', $autoUpload); return $this->setConfig('autoUpload', $autoUpload);
} }
@ -657,16 +421,15 @@ class UploadField extends FileField
* *
* @return integer|null Maximum limit, or null for no limit * @return integer|null Maximum limit, or null for no limit
*/ */
public function getAllowedMaxFileNumber() public function getAllowedMaxFileNumber() {
{
$allowedMaxFileNumber = $this->getConfig('allowedMaxFileNumber'); $allowedMaxFileNumber = $this->getConfig('allowedMaxFileNumber');
// if there is a has_one relation with that name on the record and // if there is a has_one relation with that name on the record and
// allowedMaxFileNumber has not been set, it's wanted to be 1 // allowedMaxFileNumber has not been set, it's wanted to be 1
if (empty($allowedMaxFileNumber)) { if(empty($allowedMaxFileNumber)) {
$record = $this->getRecord(); $record = $this->getRecord();
$name = $this->getName(); $name = $this->getName();
if ($record && DataObject::getSchema()->hasOneComponent(get_class($record), $name)) { if($record && DataObject::getSchema()->hasOneComponent(get_class($record), $name)) {
return 1; // Default for has_one return 1; // Default for has_one
} else { } else {
return null; // Default for has_many and many_many return null; // Default for has_many and many_many
@ -682,8 +445,7 @@ class UploadField extends FileField
* @param integer|null $allowedMaxFileNumber Maximum limit. 0 or null will be treated as unlimited * @param integer|null $allowedMaxFileNumber Maximum limit. 0 or null will be treated as unlimited
* @return UploadField Self reference * @return UploadField Self reference
*/ */
public function setAllowedMaxFileNumber($allowedMaxFileNumber) public function setAllowedMaxFileNumber($allowedMaxFileNumber) {
{
return $this->setConfig('allowedMaxFileNumber', $allowedMaxFileNumber); return $this->setConfig('allowedMaxFileNumber', $allowedMaxFileNumber);
} }
@ -692,11 +454,8 @@ class UploadField extends FileField
* *
* @return boolean * @return boolean
*/ */
public function canUpload() public function canUpload() {
{ if(!$this->isActive()) return false;
if (!$this->isActive()) {
return false;
}
$can = $this->getConfig('canUpload'); $can = $this->getConfig('canUpload');
return (is_bool($can)) ? $can : Permission::check($can); return (is_bool($can)) ? $can : Permission::check($can);
} }
@ -709,8 +468,7 @@ class UploadField extends FileField
* permission code * permission code
* @return UploadField Self reference * @return UploadField Self reference
*/ */
public function setCanUpload($canUpload) public function setCanUpload($canUpload) {
{
return $this->setConfig('canUpload', $canUpload); return $this->setConfig('canUpload', $canUpload);
} }
@ -720,11 +478,8 @@ class UploadField extends FileField
* *
* @return boolean * @return boolean
*/ */
public function canAttachExisting() public function canAttachExisting() {
{ if(!$this->isActive()) return false;
if (!$this->isActive()) {
return false;
}
$can = $this->getConfig('canAttachExisting'); $can = $this->getConfig('canAttachExisting');
return (is_bool($can)) ? $can : Permission::check($can); return (is_bool($can)) ? $can : Permission::check($can);
} }
@ -734,8 +489,7 @@ class UploadField extends FileField
* *
* @return boolean * @return boolean
*/ */
public function isActive() public function isActive() {
{
return !$this->isDisabled() && !$this->isReadonly(); return !$this->isDisabled() && !$this->isReadonly();
} }
@ -747,8 +501,7 @@ class UploadField extends FileField
* required permission code * required permission code
* @return UploadField Self reference * @return UploadField Self reference
*/ */
public function setCanAttachExisting($canAttachExisting) public function setCanAttachExisting($canAttachExisting) {
{
return $this->setConfig('canAttachExisting', $canAttachExisting); return $this->setConfig('canAttachExisting', $canAttachExisting);
} }
@ -757,8 +510,7 @@ class UploadField extends FileField
* *
* @return integer * @return integer
*/ */
public function getPreviewMaxWidth() public function getPreviewMaxWidth() {
{
return $this->getConfig('previewMaxWidth'); return $this->getConfig('previewMaxWidth');
} }
@ -768,8 +520,7 @@ class UploadField extends FileField
* @param integer $previewMaxWidth * @param integer $previewMaxWidth
* @return UploadField Self reference * @return UploadField Self reference
*/ */
public function setPreviewMaxWidth($previewMaxWidth) public function setPreviewMaxWidth($previewMaxWidth) {
{
return $this->setConfig('previewMaxWidth', $previewMaxWidth); return $this->setConfig('previewMaxWidth', $previewMaxWidth);
} }
@ -778,8 +529,7 @@ class UploadField extends FileField
* *
* @return integer * @return integer
*/ */
public function getPreviewMaxHeight() public function getPreviewMaxHeight() {
{
return $this->getConfig('previewMaxHeight'); return $this->getConfig('previewMaxHeight');
} }
@ -789,8 +539,7 @@ class UploadField extends FileField
* @param integer $previewMaxHeight * @param integer $previewMaxHeight
* @return UploadField Self reference * @return UploadField Self reference
*/ */
public function setPreviewMaxHeight($previewMaxHeight) public function setPreviewMaxHeight($previewMaxHeight) {
{
return $this->setConfig('previewMaxHeight', $previewMaxHeight); return $this->setConfig('previewMaxHeight', $previewMaxHeight);
} }
@ -801,8 +550,7 @@ class UploadField extends FileField
* @see javascript/UploadField_uploadtemplate.js * @see javascript/UploadField_uploadtemplate.js
* @return string * @return string
*/ */
public function getUploadTemplateName() public function getUploadTemplateName() {
{
return $this->getConfig('uploadTemplateName'); return $this->getConfig('uploadTemplateName');
} }
@ -812,8 +560,7 @@ class UploadField extends FileField
* @param string $uploadTemplateName * @param string $uploadTemplateName
* @return UploadField Self reference * @return UploadField Self reference
*/ */
public function setUploadTemplateName($uploadTemplateName) public function setUploadTemplateName($uploadTemplateName) {
{
return $this->setConfig('uploadTemplateName', $uploadTemplateName); return $this->setConfig('uploadTemplateName', $uploadTemplateName);
} }
@ -824,8 +571,7 @@ class UploadField extends FileField
* @see javascript/DownloadField_downloadtemplate.js * @see javascript/DownloadField_downloadtemplate.js
* @return string * @return string
*/ */
public function getDownloadTemplateName() public function getDownloadTemplateName() {
{
return $this->getConfig('downloadTemplateName'); return $this->getConfig('downloadTemplateName');
} }
@ -835,8 +581,7 @@ class UploadField extends FileField
* @param string $downloadTemplateName * @param string $downloadTemplateName
* @return Uploadfield Self reference * @return Uploadfield Self reference
*/ */
public function setDownloadTemplateName($downloadTemplateName) public function setDownloadTemplateName($downloadTemplateName) {
{
return $this->setConfig('downloadTemplateName', $downloadTemplateName); return $this->setConfig('downloadTemplateName', $downloadTemplateName);
} }
@ -847,13 +592,12 @@ class UploadField extends FileField
* @param DataObject $file File context to generate fields for * @param DataObject $file File context to generate fields for
* @return FieldList List of form fields * @return FieldList List of form fields
*/ */
public function getFileEditFields(DataObject $file) public function getFileEditFields(DataObject $file) {
{
// Empty actions, generate default // Empty actions, generate default
if (empty($this->fileEditFields)) { if(empty($this->fileEditFields)) {
$fields = $file->getCMSFields(); $fields = $file->getCMSFields();
// Only display main tab, to avoid overly complex interface // Only display main tab, to avoid overly complex interface
if ($fields->hasTabSet() && ($mainTab = $fields->findOrMakeTab('Root.Main'))) { if($fields->hasTabSet() && ($mainTab = $fields->findOrMakeTab('Root.Main'))) {
$fields = $mainTab->Fields(); $fields = $mainTab->Fields();
} }
return $fields; return $fields;
@ -865,7 +609,7 @@ class UploadField extends FileField
} }
// Method to call on the given file // Method to call on the given file
if ($file->hasMethod($this->fileEditFields)) { if($file->hasMethod($this->fileEditFields)) {
return $file->{$this->fileEditFields}(); return $file->{$this->fileEditFields}();
} }
@ -879,8 +623,7 @@ class UploadField extends FileField
* @param FieldList|string * @param FieldList|string
* @return Uploadfield Self reference * @return Uploadfield Self reference
*/ */
public function setFileEditFields($fileEditFields) public function setFileEditFields($fileEditFields) {
{
$this->fileEditFields = $fileEditFields; $this->fileEditFields = $fileEditFields;
return $this; return $this;
} }
@ -892,10 +635,9 @@ class UploadField extends FileField
* @param DataObject $file File context to generate form actions for * @param DataObject $file File context to generate form actions for
* @return FieldList Field list containing FormAction * @return FieldList Field list containing FormAction
*/ */
public function getFileEditActions(DataObject $file) public function getFileEditActions(DataObject $file) {
{
// Empty actions, generate default // Empty actions, generate default
if (empty($this->fileEditActions)) { if(empty($this->fileEditActions)) {
$actions = new FieldList($saveAction = new FormAction('doEdit', _t('UploadField.DOEDIT', 'Save'))); $actions = new FieldList($saveAction = new FormAction('doEdit', _t('UploadField.DOEDIT', 'Save')));
$saveAction->addExtraClass('ss-ui-action-constructive icon-accept'); $saveAction->addExtraClass('ss-ui-action-constructive icon-accept');
return $actions; return $actions;
@ -907,7 +649,7 @@ class UploadField extends FileField
} }
// Method to call on the given file // Method to call on the given file
if ($file->hasMethod($this->fileEditActions)) { if($file->hasMethod($this->fileEditActions)) {
return $file->{$this->fileEditActions}(); return $file->{$this->fileEditActions}();
} }
@ -921,8 +663,7 @@ class UploadField extends FileField
* @param FieldList|string * @param FieldList|string
* @return Uploadfield Self reference * @return Uploadfield Self reference
*/ */
public function setFileEditActions($fileEditActions) public function setFileEditActions($fileEditActions) {
{
$this->fileEditActions = $fileEditActions; $this->fileEditActions = $fileEditActions;
return $this; return $this;
} }
@ -934,20 +675,19 @@ class UploadField extends FileField
* @param DataObject $file File context to generate validator from * @param DataObject $file File context to generate validator from
* @return Validator Validator object * @return Validator Validator object
*/ */
public function getFileEditValidator(DataObject $file) public function getFileEditValidator(DataObject $file) {
{
// Empty validator // Empty validator
if (empty($this->fileEditValidator)) { if(empty($this->fileEditValidator)) {
return null; return null;
} }
// Validator instance // Validator instance
if ($this->fileEditValidator instanceof Validator) { if($this->fileEditValidator instanceof Validator) {
return $this->fileEditValidator; return $this->fileEditValidator;
} }
// Method to call on the given file // Method to call on the given file
if ($file->hasMethod($this->fileEditValidator)) { if($file->hasMethod($this->fileEditValidator)) {
return $file->{$this->fileEditValidator}(); return $file->{$this->fileEditValidator}();
} }
@ -961,8 +701,7 @@ class UploadField extends FileField
* @param Validator|string * @param Validator|string
* @return Uploadfield Self reference * @return Uploadfield Self reference
*/ */
public function setFileEditValidator($fileEditValidator) public function setFileEditValidator($fileEditValidator) {
{
$this->fileEditValidator = $fileEditValidator; $this->fileEditValidator = $fileEditValidator;
return $this; return $this;
} }
@ -972,8 +711,7 @@ class UploadField extends FileField
* @param File|AssetContainer $file * @param File|AssetContainer $file
* @return string URL to thumbnail * @return string URL to thumbnail
*/ */
protected function getThumbnailURLForFile(AssetContainer $file) protected function getThumbnailURLForFile(AssetContainer $file) {
{
if (!$file->exists()) { if (!$file->exists()) {
return null; return null;
} }
@ -992,34 +730,34 @@ class UploadField extends FileField
} }
// Check if unsized icon is available // Check if unsized icon is available
if ($file->hasMethod('getIcon')) { if($file->hasMethod('getIcon')) {
return $file->getIcon(); return $file->getIcon();
} }
return null; return null;
} }
public function getAttributes() public function getAttributes() {
{
return array_merge( return array_merge(
parent::getAttributes(), parent::getAttributes(),
array('data-selectdialog-url', $this->Link('select')) array(
'type' => 'file',
'data-selectdialog-url' => $this->Link('select')
)
); );
} }
public function extraClass() public function extraClass() {
{ if($this->isDisabled()) {
if ($this->isDisabled()) {
$this->addExtraClass('disabled'); $this->addExtraClass('disabled');
} }
if ($this->isReadonly()) { if($this->isReadonly()) {
$this->addExtraClass('readonly'); $this->addExtraClass('readonly');
} }
return parent::extraClass(); return parent::extraClass();
} }
public function Field($properties = array()) public function Field($properties = array()) {
{
// Calculated config as per jquery.fileupload-ui.js // Calculated config as per jquery.fileupload-ui.js
$allowedMaxFileNumber = $this->getAllowedMaxFileNumber(); $allowedMaxFileNumber = $this->getAllowedMaxFileNumber();
$config = array( $config = array(
@ -1054,7 +792,7 @@ class UploadField extends FileField
// Validation: Number of files // Validation: Number of files
if ($allowedMaxFileNumber) { if ($allowedMaxFileNumber) {
if ($allowedMaxFileNumber > 1) { if($allowedMaxFileNumber > 1) {
$config['errorMessages']['maxNumberOfFiles'] = _t( $config['errorMessages']['maxNumberOfFiles'] = _t(
'UploadField.MAXNUMBEROFFILESSHORT', 'UploadField.MAXNUMBEROFFILESSHORT',
'Can only upload {count} files', 'Can only upload {count} files',
@ -1088,19 +826,16 @@ class UploadField extends FileField
* @param Validator $validator * @param Validator $validator
* @return boolean * @return boolean
*/ */
public function validate($validator) public function validate($validator) {
{
$name = $this->getName(); $name = $this->getName();
$files = $this->getItems(); $files = $this->getItems();
// If there are no files then quit // If there are no files then quit
if ($files->count() == 0) { if($files->count() == 0) return true;
return true;
}
// Check max number of files // Check max number of files
$maxFiles = $this->getAllowedMaxFileNumber(); $maxFiles = $this->getAllowedMaxFileNumber();
if ($maxFiles && ($files->count() > $maxFiles)) { if($maxFiles && ($files->count() > $maxFiles)) {
$validator->validationError( $validator->validationError(
$name, $name,
_t( _t(
@ -1115,7 +850,7 @@ class UploadField extends FileField
// Revalidate each file against nested validator // Revalidate each file against nested validator
$this->upload->clearErrors(); $this->upload->clearErrors();
foreach ($files as $file) { foreach($files as $file) {
// Generate $_FILES style file attribute array for upload validator // Generate $_FILES style file attribute array for upload validator
$tmpFile = array( $tmpFile = array(
'name' => $file->Name, 'name' => $file->Name,
@ -1128,8 +863,8 @@ class UploadField extends FileField
} }
// Check all errors // Check all errors
if ($errors = $this->upload->getErrors()) { if($errors = $this->upload->getErrors()) {
foreach ($errors as $error) { foreach($errors as $error) {
$validator->validationError($name, $error, "validation"); $validator->validationError($name, $error, "validation");
} }
return false; return false;
@ -1142,8 +877,7 @@ class UploadField extends FileField
* @param HTTPRequest $request * @param HTTPRequest $request
* @return UploadField_ItemHandler * @return UploadField_ItemHandler
*/ */
public function handleItem(HTTPRequest $request) public function handleItem(HTTPRequest $request) {
{
return $this->getItemHandler($request->param('ID')); return $this->getItemHandler($request->param('ID'));
} }
@ -1151,8 +885,7 @@ class UploadField extends FileField
* @param int $itemID * @param int $itemID
* @return UploadField_ItemHandler * @return UploadField_ItemHandler
*/ */
public function getItemHandler($itemID) public function getItemHandler($itemID) {
{
return UploadField_ItemHandler::create($this, $itemID); return UploadField_ItemHandler::create($this, $itemID);
} }
@ -1160,107 +893,13 @@ class UploadField extends FileField
* @param HTTPRequest $request * @param HTTPRequest $request
* @return UploadField_SelectHandler * @return UploadField_SelectHandler
*/ */
public function handleSelect(HTTPRequest $request) public function handleSelect(HTTPRequest $request) {
{ if(!$this->canAttachExisting()) {
if (!$this->canAttachExisting()) {
return $this->httpError(403); return $this->httpError(403);
} }
return UploadField_SelectHandler::create($this, $this->getFolderName()); return UploadField_SelectHandler::create($this, $this->getFolderName());
} }
/**
* Given an array of post variables, extract all temporary file data into an array
*
* @param array $postVars Array of posted form data
* @return array List of temporary file data
*/
protected function extractUploadedFileData($postVars)
{
// Note: Format of posted file parameters in php is a feature of using
// <input name='{$Name}[Uploads][]' /> for multiple file uploads
$tmpFiles = array();
if (!empty($postVars['tmp_name'])
&& is_array($postVars['tmp_name'])
&& !empty($postVars['tmp_name']['Uploads'])
) {
for ($i = 0; $i < count($postVars['tmp_name']['Uploads']); $i++) {
// Skip if "empty" file
if (empty($postVars['tmp_name']['Uploads'][$i])) {
continue;
}
$tmpFile = array();
foreach (array('name', 'type', 'tmp_name', 'error', 'size') as $field) {
$tmpFile[$field] = $postVars[$field]['Uploads'][$i];
}
$tmpFiles[] = $tmpFile;
}
} elseif (!empty($postVars['tmp_name'])) {
// Fallback to allow single file uploads (method used by AssetUploadField)
$tmpFiles[] = $postVars;
}
return $tmpFiles;
}
/**
* Loads the temporary file data into a File object
*
* @param array $tmpFile Temporary file data
* @param string $error Error message
* @return AssetContainer File object, or null if error
*/
protected function saveTemporaryFile($tmpFile, &$error = null)
{
// Determine container object
$error = null;
$fileObject = null;
if (empty($tmpFile)) {
$error = _t('UploadField.FIELDNOTSET', 'File information not found');
return null;
}
if ($tmpFile['error']) {
$error = $tmpFile['error'];
return null;
}
// Search for relations that can hold the uploaded files, but don't fallback
// to default if there is no automatic relation
if ($relationClass = $this->getRelationAutosetClass(null)) {
// Allow File to be subclassed
if ($relationClass === 'SilverStripe\\Assets\\File' && isset($tmpFile['name'])) {
$relationClass = File::get_class_for_file_extension(
File::get_file_extension($tmpFile['name'])
);
}
// Create new object explicitly. Otherwise rely on Upload::load to choose the class.
$fileObject = Object::create($relationClass);
if (! ($fileObject instanceof DataObject) || !($fileObject instanceof AssetContainer)) {
throw new InvalidArgumentException("Invalid asset container $relationClass");
}
}
// Get the uploaded file into a new file object.
try {
$this->upload->loadIntoFile($tmpFile, $fileObject, $this->getFolderName());
} catch (Exception $e) {
// we shouldn't get an error here, but just in case
$error = $e->getMessage();
return null;
}
// Check if upload field has an error
if ($this->upload->isError()) {
$error = implode(' ' . PHP_EOL, $this->upload->getErrors());
return null;
}
// return file
return $this->upload->getFile();
}
/** /**
* Safely encodes the File object with all standard fields required * Safely encodes the File object with all standard fields required
* by the front end * by the front end
@ -1268,8 +907,7 @@ class UploadField extends FileField
* @param File|AssetContainer $file Object which contains a file * @param File|AssetContainer $file Object which contains a file
* @return array Array encoded list of file attributes * @return array Array encoded list of file attributes
*/ */
protected function encodeFileAttributes(AssetContainer $file) protected function encodeFileAttributes(AssetContainer $file) {
{
// Collect all output data. // Collect all output data.
$customised = $this->customiseFile($file); $customised = $this->customiseFile($file);
return array( return array(
@ -1292,17 +930,14 @@ class UploadField extends FileField
* @return HTTPResponse * @return HTTPResponse
* @return HTTPResponse * @return HTTPResponse
*/ */
public function upload(HTTPRequest $request) public function upload(HTTPRequest $request) {
{ if($this->isDisabled() || $this->isReadonly() || !$this->canUpload()) {
if ($this->isDisabled() || $this->isReadonly() || !$this->canUpload()) {
return $this->httpError(403); return $this->httpError(403);
} }
// Protect against CSRF on destructive action // Protect against CSRF on destructive action
$token = $this->getForm()->getSecurityToken(); $token = $this->getForm()->getSecurityToken();
if (!$token->checkRequest($request)) { if(!$token->checkRequest($request)) return $this->httpError(400);
return $this->httpError(400);
}
// Get form details // Get form details
$name = $this->getName(); $name = $this->getName();
@ -1316,7 +951,7 @@ class UploadField extends FileField
// and save data/error on a per file basis // and save data/error on a per file basis
foreach ($uploadedFiles as $tempFile) { foreach ($uploadedFiles as $tempFile) {
$file = $this->saveTemporaryFile($tempFile, $error); $file = $this->saveTemporaryFile($tempFile, $error);
if (empty($file)) { if(empty($file)) {
array_push($return, array('error' => $error)); array_push($return, array('error' => $error));
} else { } else {
array_push($return, $this->encodeFileAttributes($file)); array_push($return, $this->encodeFileAttributes($file));
@ -1337,19 +972,14 @@ class UploadField extends FileField
* @param HTTPRequest $request * @param HTTPRequest $request
* @return HTTPResponse * @return HTTPResponse
*/ */
public function attach(HTTPRequest $request) public function attach(HTTPRequest $request) {
{ if(!$request->isPOST()) return $this->httpError(403);
if (!$request->isPOST()) { if(!$this->canAttachExisting()) return $this->httpError(403);
return $this->httpError(403);
}
if (!$this->canAttachExisting()) {
return $this->httpError(403);
}
// Retrieve file attributes required by front end // Retrieve file attributes required by front end
$return = array(); $return = array();
$files = File::get()->byIDs($request->postVar('ids')); $files = File::get()->byIDs($request->postVar('ids'));
foreach ($files as $file) { foreach($files as $file) {
$return[] = $this->encodeFileAttributes($file); $return[] = $this->encodeFileAttributes($file);
} }
$response = new HTTPResponse(Convert::raw2json($return)); $response = new HTTPResponse(Convert::raw2json($return));
@ -1363,8 +993,7 @@ class UploadField extends FileField
* @param string $originalFile Filename * @param string $originalFile Filename
* @return bool * @return bool
*/ */
protected function checkFileExists($originalFile) protected function checkFileExists($originalFile) {
{
// Check both original and safely filtered filename // Check both original and safely filtered filename
$nameFilter = FileNameFilter::create(); $nameFilter = FileNameFilter::create();
@ -1385,11 +1014,10 @@ class UploadField extends FileField
* @param HTTPRequest $request * @param HTTPRequest $request
* @return HTTPResponse * @return HTTPResponse
*/ */
public function fileexists(HTTPRequest $request) public function fileexists(HTTPRequest $request) {
{
// Assert that requested filename doesn't attempt to escape the directory // Assert that requested filename doesn't attempt to escape the directory
$originalFile = $request->requestVar('filename'); $originalFile = $request->requestVar('filename');
if ($originalFile !== basename($originalFile)) { if($originalFile !== basename($originalFile)) {
$return = array( $return = array(
'error' => _t('File.NOVALIDUPLOAD', 'File is not a valid upload') 'error' => _t('File.NOVALIDUPLOAD', 'File is not a valid upload')
); );
@ -1402,43 +1030,15 @@ class UploadField extends FileField
// Encode and present response // Encode and present response
$response = new HTTPResponse(Convert::raw2json($return)); $response = new HTTPResponse(Convert::raw2json($return));
$response->addHeader('Content-Type', 'application/json'); $response->addHeader('Content-Type', 'application/json');
if (!empty($return['error'])) { if (!empty($return['error'])) $response->setStatusCode(400);
$response->setStatusCode(400);
}
return $response; return $response;
} }
public function performReadonlyTransformation() public function performReadonlyTransformation() {
{
$clone = clone $this; $clone = clone $this;
$clone->addExtraClass('readonly'); $clone->addExtraClass('readonly');
$clone->setReadonly(true); $clone->setReadonly(true);
return $clone; return $clone;
} }
/**
* Gets the foreign class that needs to be created, or 'File' as default if there
* is no relationship, or it cannot be determined.
*
* @param string $default Default value to return if no value could be calculated
* @return string Foreign class name.
*/
public function getRelationAutosetClass($default = 'SilverStripe\\Assets\\File')
{
// Don't autodetermine relation if no relationship between parent record
if (!$this->relationAutoSetting) {
return $default;
}
// Check record and name
$name = $this->getName();
$record = $this->getRecord();
if (empty($name) || empty($record)) {
return $default;
} else {
$class = $record->getRelationClass($name);
return empty($class) ? $default : $class;
}
}
} }

153
src/Forms/Uploadable.php Normal file
View File

@ -0,0 +1,153 @@
<?php
namespace SilverStripe\Forms;
use SilverStripe\Assets\File;
use SilverStripe\Assets\Upload;
use SilverStripe\Assets\Upload_Validator;
/**
* Represents a form field which has an Upload() instance and can upload to a folder
*
* @mixin FormField
*/
trait Uploadable
{
/**
* Upload object (needed for validation
* and actually moving the temporary file
* created by PHP).
*
* @var Upload
*/
protected $upload;
/**
* Partial filesystem path relative to /assets directory.
* Defaults to Upload::$uploads_folder.
*
* @var string
*/
protected $folderName = false;
/**
* Bootstrap Uploadable field
*/
protected function constructUploadable() {
// Set Upload instance
$this->setUpload(Upload::create());
// filter out '' since this would be a regex problem on JS end
$this->getValidator()->setAllowedExtensions(
array_filter(File::config()->allowed_extensions)
);
// get the lower max size
$maxUpload = File::ini2bytes(ini_get('upload_max_filesize'));
$maxPost = File::ini2bytes(ini_get('post_max_size'));
$this->getValidator()->setAllowedMaxFileSize(min($maxUpload, $maxPost));
}
/**
* Retrieves the Upload handler
*
* @return Upload
*/
public function getUpload() {
return $this->upload;
}
/**
* Sets the upload handler
*
* @param Upload $upload
* @return $this Self reference
*/
public function setUpload(Upload $upload) {
$this->upload = $upload;
return $this;
}
/**
* Limit allowed file extensions. Empty by default, allowing all extensions.
* To allow files without an extension, use an empty string.
* See {@link File::$allowed_extensions} to get a good standard set of
* extensions that are typically not harmful in a webserver context.
* See {@link setAllowedMaxFileSize()} to limit file size by extension.
*
* @param array $rules List of extensions
* @return $this
*/
public function setAllowedExtensions($rules) {
$this->getValidator()->setAllowedExtensions($rules);
return $this;
}
/**
* Limit allowed file extensions by specifying categories of file types.
* These may be 'image', 'image/supported', 'audio', 'video', 'archive', 'flash', or 'document'
* See {@link File::$allowed_extensions} for details of allowed extensions
* for each of these categories
*
* @param string $category Category name
* @param string,... $categories Additional category names
* @return $this
*/
public function setAllowedFileCategories($category) {
$extensions = File::get_category_extensions(func_get_args());
return $this->setAllowedExtensions($extensions);
}
/**
* Returns list of extensions allowed by this field, or an empty array
* if there is no restriction
*
* @return array
*/
public function getAllowedExtensions() {
return $this->getValidator()->getAllowedExtensions();
}
/**
* Get custom validator for this field
*
* @return Upload_Validator
*/
public function getValidator() {
return $this->getUpload()->getValidator();
}
/**
* Set custom validator for this field
*
* @param Upload_Validator $validator
* @return $this Self reference
*/
public function setValidator(Upload_Validator $validator) {
$this->getUpload()->setValidator($validator);
return $this;
}
/**
* Sets the upload folder name
*
* @param string $folderName
* @return $this Self reference
*/
public function setFolderName($folderName) {
$this->folderName = $folderName;
return $this;
}
/**
* Gets the upload folder name
*
* @return string
*/
public function getFolderName() {
return ($this->folderName !== false)
? $this->folderName
: Upload::config()->uploads_folder;
}
}