mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
PSR2 cleanup
This commit is contained in:
parent
d4abfea4eb
commit
bc19b2a491
@ -33,405 +33,405 @@ use Exception;
|
|||||||
class Upload extends Controller
|
class Upload extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $allowed_actions = array(
|
private static $allowed_actions = array(
|
||||||
'index',
|
'index',
|
||||||
'load'
|
'load'
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A dataobject (typically {@see File}) which implements {@see AssetContainer}
|
* A dataobject (typically {@see File}) which implements {@see AssetContainer}
|
||||||
*
|
*
|
||||||
* @var AssetContainer
|
* @var AssetContainer
|
||||||
*/
|
*/
|
||||||
protected $file;
|
protected $file;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validator for this upload field
|
* Validator for this upload field
|
||||||
*
|
*
|
||||||
* @var Upload_Validator
|
* @var Upload_Validator
|
||||||
*/
|
*/
|
||||||
protected $validator;
|
protected $validator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information about the temporary file produced
|
* Information about the temporary file produced
|
||||||
* by the PHP-runtime.
|
* by the PHP-runtime.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $tmpFile;
|
protected $tmpFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace an existing file rather than renaming the new one.
|
* Replace an existing file rather than renaming the new one.
|
||||||
*
|
*
|
||||||
* @var boolean
|
* @var boolean
|
||||||
*/
|
*/
|
||||||
protected $replaceFile = false;
|
protected $replaceFile = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processing errors that can be evaluated,
|
* Processing errors that can be evaluated,
|
||||||
* e.g. by Form-validation.
|
* e.g. by Form-validation.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $errors = array();
|
protected $errors = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default visibility to assign uploaded files
|
* Default visibility to assign uploaded files
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $defaultVisibility = AssetStore::VISIBILITY_PROTECTED;
|
protected $defaultVisibility = AssetStore::VISIBILITY_PROTECTED;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A foldername relative to /assets,
|
* A foldername relative to /assets,
|
||||||
* where all uploaded files are stored by default.
|
* where all uploaded files are stored by default.
|
||||||
*
|
*
|
||||||
* @config
|
* @config
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
private static $uploads_folder = "Uploads";
|
private static $uploads_folder = "Uploads";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A prefix for the version number added to an uploaded file
|
* A prefix for the version number added to an uploaded file
|
||||||
* when a file with the same name already exists.
|
* when a file with the same name already exists.
|
||||||
* Example using no prefix: IMG001.jpg becomes IMG2.jpg
|
* Example using no prefix: IMG001.jpg becomes IMG2.jpg
|
||||||
* Example using '-v' prefix: IMG001.jpg becomes IMG001-v2.jpg
|
* Example using '-v' prefix: IMG001.jpg becomes IMG001-v2.jpg
|
||||||
*
|
*
|
||||||
* @config
|
* @config
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
private static $version_prefix = '-v';
|
private static $version_prefix = '-v';
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
$this->validator = Upload_Validator::create();
|
$this->validator = Upload_Validator::create();
|
||||||
$this->replaceFile = self::config()->replaceFile;
|
$this->replaceFile = self::config()->replaceFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
return $this->httpError(404); // no-op
|
return $this->httpError(404); // no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get current validator
|
* Get current validator
|
||||||
*
|
*
|
||||||
* @return Upload_Validator $validator
|
* @return Upload_Validator $validator
|
||||||
*/
|
*/
|
||||||
public function getValidator()
|
public function getValidator()
|
||||||
{
|
{
|
||||||
return $this->validator;
|
return $this->validator;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a different instance than {@link Upload_Validator}
|
* Set a different instance than {@link Upload_Validator}
|
||||||
* for this upload session.
|
* for this upload session.
|
||||||
*
|
*
|
||||||
* @param object $validator
|
* @param object $validator
|
||||||
*/
|
*/
|
||||||
public function setValidator($validator)
|
public function setValidator($validator)
|
||||||
{
|
{
|
||||||
$this->validator = $validator;
|
$this->validator = $validator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an asset renamer for the given filename.
|
* Get an asset renamer for the given filename.
|
||||||
*
|
*
|
||||||
* @param string $filename Path name
|
* @param string $filename Path name
|
||||||
* @return AssetNameGenerator
|
* @return AssetNameGenerator
|
||||||
*/
|
*/
|
||||||
protected function getNameGenerator($filename)
|
protected function getNameGenerator($filename)
|
||||||
{
|
{
|
||||||
return Injector::inst()->createWithArgs('AssetNameGenerator', array($filename));
|
return Injector::inst()->createWithArgs('AssetNameGenerator', array($filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @return AssetStore
|
* @return AssetStore
|
||||||
*/
|
*/
|
||||||
protected function getAssetStore()
|
protected function getAssetStore()
|
||||||
{
|
{
|
||||||
return Injector::inst()->get('AssetStore');
|
return Injector::inst()->get('AssetStore');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save an file passed from a form post into the AssetStore directly
|
* Save an file passed from a form post into the AssetStore directly
|
||||||
*
|
*
|
||||||
* @param array $tmpFile Indexed array that PHP generated for every file it uploads.
|
* @param array $tmpFile Indexed array that PHP generated for every file it uploads.
|
||||||
* @param string|bool $folderPath Folder path relative to /assets
|
* @param string|bool $folderPath Folder path relative to /assets
|
||||||
* @return array|false Either the tuple array, or false if the file could not be saved
|
* @return array|false Either the tuple array, or false if the file could not be saved
|
||||||
*/
|
*/
|
||||||
public function load($tmpFile, $folderPath = false)
|
public function load($tmpFile, $folderPath = false)
|
||||||
{
|
{
|
||||||
// Validate filename
|
// Validate filename
|
||||||
$filename = $this->getValidFilename($tmpFile, $folderPath);
|
$filename = $this->getValidFilename($tmpFile, $folderPath);
|
||||||
if(!$filename) {
|
if (!$filename) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save file into backend
|
// Save file into backend
|
||||||
$result = $this->storeTempFile($tmpFile, $filename, $this->getAssetStore());
|
$result = $this->storeTempFile($tmpFile, $filename, $this->getAssetStore());
|
||||||
|
|
||||||
//to allow extensions to e.g. create a version after an upload
|
//to allow extensions to e.g. create a version after an upload
|
||||||
$this->extend('onAfterLoad', $result, $tmpFile);
|
$this->extend('onAfterLoad', $result, $tmpFile);
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save an file passed from a form post into this object.
|
* Save an file passed from a form post into this object.
|
||||||
* File names are filtered through {@link FileNameFilter}, see class documentation
|
* File names are filtered through {@link FileNameFilter}, see class documentation
|
||||||
* on how to influence this behaviour.
|
* on how to influence this behaviour.
|
||||||
*
|
*
|
||||||
* @param array $tmpFile
|
* @param array $tmpFile
|
||||||
* @param AssetContainer $file
|
* @param AssetContainer $file
|
||||||
* @param string|bool $folderPath
|
* @param string|bool $folderPath
|
||||||
* @return bool True if the file was successfully saved into this record
|
* @return bool True if the file was successfully saved into this record
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function loadIntoFile($tmpFile, $file = null, $folderPath = false)
|
public function loadIntoFile($tmpFile, $file = null, $folderPath = false)
|
||||||
{
|
{
|
||||||
$this->file = $file;
|
$this->file = $file;
|
||||||
|
|
||||||
// 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
//to allow extensions to e.g. create a version after an upload
|
//to allow extensions to e.g. create a version after an upload
|
||||||
$this->file->extend('onAfterUpload');
|
$this->file->extend('onAfterUpload');
|
||||||
$this->extend('onAfterLoadIntoFile', $this->file);
|
$this->extend('onAfterLoadIntoFile', $this->file);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assign this temporary file into the given destination
|
* Assign this temporary file into the given destination
|
||||||
*
|
*
|
||||||
* @param array $tmpFile
|
* @param array $tmpFile
|
||||||
* @param string $filename
|
* @param string $filename
|
||||||
* @param AssetContainer|AssetStore $container
|
* @param AssetContainer|AssetStore $container
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function storeTempFile($tmpFile, $filename, $container)
|
protected function storeTempFile($tmpFile, $filename, $container)
|
||||||
{
|
{
|
||||||
// Save file into backend
|
// Save file into backend
|
||||||
$conflictResolution = $this->replaceFile
|
$conflictResolution = $this->replaceFile
|
||||||
? AssetStore::CONFLICT_OVERWRITE
|
? AssetStore::CONFLICT_OVERWRITE
|
||||||
: AssetStore::CONFLICT_RENAME;
|
: AssetStore::CONFLICT_RENAME;
|
||||||
$config = array(
|
$config = array(
|
||||||
'conflict' => $conflictResolution,
|
'conflict' => $conflictResolution,
|
||||||
'visibility' => $this->getDefaultVisibility()
|
'visibility' => $this->getDefaultVisibility()
|
||||||
);
|
);
|
||||||
return $container->setFromLocalFile($tmpFile['tmp_name'], $filename, null, null, $config);
|
return $container->setFromLocalFile($tmpFile['tmp_name'], $filename, null, null, $config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a temporary file and upload path, validate the file and determine the
|
* Given a temporary file and upload path, validate the file and determine the
|
||||||
* value of the 'Filename' tuple that should be used to store this asset.
|
* value of the 'Filename' tuple that should be used to store this asset.
|
||||||
*
|
*
|
||||||
* @param array $tmpFile
|
* @param array $tmpFile
|
||||||
* @param string $folderPath
|
* @param string $folderPath
|
||||||
* @return string|false Value of filename tuple, or false if invalid
|
* @return string|false Value of filename tuple, or false if invalid
|
||||||
*/
|
*/
|
||||||
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"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a file and filename, ensure that file renaming / replacing rules are satisfied
|
* Given a file and filename, ensure that file renaming / replacing rules are satisfied
|
||||||
*
|
*
|
||||||
* If replacing, this method may replace $this->file with an existing record to overwrite.
|
* If replacing, this method may replace $this->file with an existing record to overwrite.
|
||||||
* If renaming, a new value for $filename may be returned
|
* If renaming, a new value for $filename may be returned
|
||||||
*
|
*
|
||||||
* @param string $filename
|
* @param string $filename
|
||||||
* @return string $filename A filename safe to write to
|
* @return string $filename A filename safe to write to
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
);
|
);
|
||||||
$this->file = Object::create($fileClass);
|
$this->file = Object::create($fileClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check there is if existing file
|
// Check there is if existing file
|
||||||
$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();
|
||||||
}
|
}
|
||||||
// Filename won't change if replacing
|
// Filename won't change if replacing
|
||||||
return $filename;
|
return $filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fail
|
// Fail
|
||||||
$tries = $renamer->getMaxTries();
|
$tries = $renamer->getMaxTries();
|
||||||
throw new Exception("Could not rename {$filename} with {$tries} tries");
|
throw new Exception("Could not rename {$filename} with {$tries} tries");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param bool $replace
|
* @param bool $replace
|
||||||
*/
|
*/
|
||||||
public function setReplaceFile($replace)
|
public function setReplaceFile($replace)
|
||||||
{
|
{
|
||||||
$this->replaceFile = $replace;
|
$this->replaceFile = $replace;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function getReplaceFile()
|
public function getReplaceFile()
|
||||||
{
|
{
|
||||||
return $this->replaceFile;
|
return $this->replaceFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container for all validation on the file
|
* Container for all validation on the file
|
||||||
* (e.g. size and extension restrictions).
|
* (e.g. size and extension restrictions).
|
||||||
* Is NOT connected to the {Validator} classes,
|
* Is NOT connected to the {Validator} classes,
|
||||||
* please have a look at {FileField->validate()}
|
* please have a look at {FileField->validate()}
|
||||||
* for an example implementation of external validation.
|
* for an example implementation of external validation.
|
||||||
*
|
*
|
||||||
* @param array $tmpFile
|
* @param array $tmpFile
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function validate($tmpFile)
|
public function validate($tmpFile)
|
||||||
{
|
{
|
||||||
$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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get file-object, either generated from {load()},
|
* Get file-object, either generated from {load()},
|
||||||
* or manually set.
|
* or manually set.
|
||||||
*
|
*
|
||||||
* @return AssetContainer
|
* @return AssetContainer
|
||||||
*/
|
*/
|
||||||
public function getFile()
|
public function getFile()
|
||||||
{
|
{
|
||||||
return $this->file;
|
return $this->file;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a file-object (similiar to {loadIntoFile()})
|
* Set a file-object (similiar to {loadIntoFile()})
|
||||||
*
|
*
|
||||||
* @param AssetContainer $file
|
* @param AssetContainer $file
|
||||||
*/
|
*/
|
||||||
public function setFile(AssetContainer $file)
|
public function setFile(AssetContainer $file)
|
||||||
{
|
{
|
||||||
$this->file = $file;
|
$this->file = $file;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear out all errors (mostly set by {loadUploaded()})
|
* Clear out all errors (mostly set by {loadUploaded()})
|
||||||
* including the validator's errors
|
* including the validator's errors
|
||||||
*/
|
*/
|
||||||
public function clearErrors()
|
public function clearErrors()
|
||||||
{
|
{
|
||||||
$this->errors = array();
|
$this->errors = array();
|
||||||
$this->validator->clearErrors();
|
$this->validator->clearErrors();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines wether previous operations caused an error.
|
* Determines wether previous operations caused an error.
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function isError()
|
public function isError()
|
||||||
{
|
{
|
||||||
return (count($this->errors));
|
return (count($this->errors));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all errors that occurred while processing so far
|
* Return all errors that occurred while processing so far
|
||||||
* (mostly set by {loadUploaded()})
|
* (mostly set by {loadUploaded()})
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getErrors()
|
public function getErrors()
|
||||||
{
|
{
|
||||||
return $this->errors;
|
return $this->errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get default visibility for uploaded files. {@see AssetStore}
|
* Get default visibility for uploaded files. {@see AssetStore}
|
||||||
* One of the values of AssetStore::VISIBILITY_* constants
|
* One of the values of AssetStore::VISIBILITY_* constants
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getDefaultVisibility()
|
public function getDefaultVisibility()
|
||||||
{
|
{
|
||||||
return $this->defaultVisibility;
|
return $this->defaultVisibility;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assign default visibility for uploaded files. {@see AssetStore}
|
* Assign default visibility for uploaded files. {@see AssetStore}
|
||||||
* One of the values of AssetStore::VISIBILITY_* constants
|
* One of the values of AssetStore::VISIBILITY_* constants
|
||||||
*
|
*
|
||||||
* @param string $visibility
|
* @param string $visibility
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setDefaultVisibility($visibility)
|
public function setDefaultVisibility($visibility)
|
||||||
{
|
{
|
||||||
$this->defaultVisibility = $visibility;
|
$this->defaultVisibility = $visibility;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use SilverStripe\Dev\SapphireTest;
|
|||||||
|
|
||||||
class Upload_Validator
|
class Upload_Validator
|
||||||
{
|
{
|
||||||
use Injectable;
|
use Injectable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains a list of the max file sizes shared by
|
* Contains a list of the max file sizes shared by
|
||||||
|
@ -26,754 +26,794 @@ 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 FormField {
|
class AssetField extends FormField
|
||||||
use UploadReceiver;
|
{
|
||||||
|
use UploadReceiver;
|
||||||
/**
|
|
||||||
* @var array
|
/**
|
||||||
*/
|
* @var array
|
||||||
private static $allowed_actions = array(
|
*/
|
||||||
'upload'
|
private static $allowed_actions = array(
|
||||||
);
|
'upload'
|
||||||
|
);
|
||||||
/**
|
|
||||||
* @var array
|
/**
|
||||||
*/
|
* @var array
|
||||||
private static $url_handlers = array(
|
*/
|
||||||
'$Action!' => '$Action',
|
private static $url_handlers = array(
|
||||||
);
|
'$Action!' => '$Action',
|
||||||
|
);
|
||||||
private static $casting = array(
|
|
||||||
'Value' => 'DBFile',
|
private static $casting = array(
|
||||||
'UploadFieldThumbnailURL' => 'Varchar'
|
'Value' => 'DBFile',
|
||||||
);
|
'UploadFieldThumbnailURL' => 'Varchar'
|
||||||
|
);
|
||||||
/**
|
|
||||||
* Template to use for the file button widget
|
/**
|
||||||
*
|
* Template to use for the file button widget
|
||||||
* @var string
|
*
|
||||||
*/
|
* @var string
|
||||||
protected $templateFileButtons = null;
|
*/
|
||||||
|
protected $templateFileButtons = null;
|
||||||
/**
|
|
||||||
* Parent data record. Will be infered from parent form or controller if blank. The destination
|
/**
|
||||||
* DBFile should be a property of the name $name on this object.
|
* Parent data record. Will be infered from parent form or controller if blank. The destination
|
||||||
*
|
* DBFile should be a property of the name $name on this object.
|
||||||
* @var DataObject
|
*
|
||||||
*/
|
* @var DataObject
|
||||||
protected $record;
|
*/
|
||||||
|
protected $record;
|
||||||
/**
|
|
||||||
* Config for this field used in the front-end javascript
|
/**
|
||||||
* (will be merged into the config of the javascript file upload plugin).
|
* Config for this field used in the front-end javascript
|
||||||
*
|
* (will be merged into the config of the javascript file upload plugin).
|
||||||
* @var array
|
*
|
||||||
*/
|
* @var array
|
||||||
protected $ufConfig = array();
|
*/
|
||||||
|
protected $ufConfig = array();
|
||||||
/**
|
|
||||||
* Front end config defaults
|
/**
|
||||||
*
|
* Front end config defaults
|
||||||
* @config
|
*
|
||||||
* @var array
|
* @config
|
||||||
*/
|
* @var array
|
||||||
private static $defaultConfig = array(
|
*/
|
||||||
/**
|
private static $defaultConfig = array(
|
||||||
* Automatically upload the file once selected
|
/**
|
||||||
*
|
* Automatically upload the file once selected
|
||||||
* @var boolean
|
*
|
||||||
*/
|
* @var boolean
|
||||||
'autoUpload' => true,
|
*/
|
||||||
|
'autoUpload' => true,
|
||||||
/**
|
|
||||||
* Can the user upload new files.
|
/**
|
||||||
* String values are interpreted as permission codes.
|
* Can the user upload new files.
|
||||||
*
|
* String values are interpreted as permission codes.
|
||||||
* @var boolean|string
|
*
|
||||||
*/
|
* @var boolean|string
|
||||||
'canUpload' => true,
|
*/
|
||||||
|
'canUpload' => true,
|
||||||
/**
|
|
||||||
* Shows the target folder for new uploads in the field UI.
|
/**
|
||||||
* Disable to keep the internal filesystem structure hidden from users.
|
* Shows the target folder for new uploads in the field UI.
|
||||||
*
|
* Disable to keep the internal filesystem structure hidden from users.
|
||||||
* @var boolean|string
|
*
|
||||||
*/
|
* @var boolean|string
|
||||||
'canPreviewFolder' => true,
|
*/
|
||||||
|
'canPreviewFolder' => true,
|
||||||
/**
|
|
||||||
* Indicate a change event to the containing form if an upload
|
/**
|
||||||
* or file edit/delete was performed.
|
* Indicate a change event to the containing form if an upload
|
||||||
*
|
* or file edit/delete was performed.
|
||||||
* @var boolean
|
*
|
||||||
*/
|
* @var boolean
|
||||||
'changeDetection' => true,
|
*/
|
||||||
|
'changeDetection' => true,
|
||||||
/**
|
|
||||||
* Maximum width of the preview thumbnail
|
/**
|
||||||
*
|
* Maximum width of the preview thumbnail
|
||||||
* @var integer
|
*
|
||||||
*/
|
* @var integer
|
||||||
'previewMaxWidth' => 80,
|
*/
|
||||||
|
'previewMaxWidth' => 80,
|
||||||
/**
|
|
||||||
* Maximum height of the preview thumbnail
|
/**
|
||||||
*
|
* Maximum height of the preview thumbnail
|
||||||
* @var integer
|
*
|
||||||
*/
|
* @var integer
|
||||||
'previewMaxHeight' => 60,
|
*/
|
||||||
|
'previewMaxHeight' => 60,
|
||||||
/**
|
|
||||||
* javascript template used to display uploading files
|
/**
|
||||||
*
|
* javascript template used to display uploading files
|
||||||
* @see javascript/UploadField_uploadtemplate.js
|
*
|
||||||
* @var string
|
* @see javascript/UploadField_uploadtemplate.js
|
||||||
*/
|
* @var string
|
||||||
'uploadTemplateName' => 'ss-uploadfield-uploadtemplate',
|
*/
|
||||||
|
'uploadTemplateName' => 'ss-uploadfield-uploadtemplate',
|
||||||
/**
|
|
||||||
* javascript template used to display already uploaded files
|
/**
|
||||||
*
|
* javascript template used to display already uploaded files
|
||||||
* @see javascript/UploadField_downloadtemplate.js
|
*
|
||||||
* @var string
|
* @see javascript/UploadField_downloadtemplate.js
|
||||||
*/
|
* @var string
|
||||||
'downloadTemplateName' => 'ss-uploadfield-downloadtemplate'
|
*/
|
||||||
);
|
'downloadTemplateName' => 'ss-uploadfield-downloadtemplate'
|
||||||
|
);
|
||||||
/**
|
|
||||||
* Folder to display in "Select files" list.
|
/**
|
||||||
* Defaults to listing all files regardless of folder.
|
* Folder to display in "Select files" list.
|
||||||
* The folder path should be relative to the webroot.
|
* Defaults to listing all files regardless of folder.
|
||||||
* See {@link FileField->folderName} to set the upload target instead.
|
* The folder path should be relative to the webroot.
|
||||||
*
|
* See {@link FileField->folderName} to set the upload target instead.
|
||||||
* @var string
|
*
|
||||||
* @example admin/folder/subfolder
|
* @var string
|
||||||
*/
|
* @example admin/folder/subfolder
|
||||||
protected $displayFolderName;
|
*/
|
||||||
|
protected $displayFolderName;
|
||||||
/**
|
|
||||||
* Construct a new UploadField instance
|
/**
|
||||||
*
|
* Construct a new UploadField instance
|
||||||
* @param string $name The internal field name, passed to forms.
|
*
|
||||||
* @param string $title The field label.
|
* @param string $name The internal field name, passed to forms.
|
||||||
*/
|
* @param string $title The field label.
|
||||||
public function __construct($name, $title = null) {
|
*/
|
||||||
$this->addExtraClass('ss-upload'); // class, used by js
|
public function __construct($name, $title = null)
|
||||||
$this->addExtraClass('ss-uploadfield'); // class, used by css for uploadfield only
|
{
|
||||||
|
$this->addExtraClass('ss-upload'); // class, used by js
|
||||||
$this->ufConfig = array_merge($this->ufConfig, self::config()->defaultConfig);
|
$this->addExtraClass('ss-uploadfield'); // class, used by css for uploadfield only
|
||||||
|
|
||||||
$this->constructUploadReceiver();
|
$this->ufConfig = array_merge($this->ufConfig, self::config()->defaultConfig);
|
||||||
parent::__construct($name, $title);
|
|
||||||
|
$this->constructUploadReceiver();
|
||||||
// AssetField always uses rename replacement method
|
parent::__construct($name, $title);
|
||||||
$this->getUpload()->setReplaceFile(false);
|
|
||||||
|
// AssetField always uses rename replacement method
|
||||||
// filter out '' since this would be a regex problem on JS end
|
$this->getUpload()->setReplaceFile(false);
|
||||||
$this->getValidator()->setAllowedExtensions(
|
|
||||||
array_filter(File::config()->allowed_extensions)
|
// 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'));
|
// get the lower max size
|
||||||
$this->getValidator()->setAllowedMaxFileSize(min($maxUpload, $maxPost));
|
$maxUpload = File::ini2bytes(ini_get('upload_max_filesize'));
|
||||||
}
|
$maxPost = File::ini2bytes(ini_get('post_max_size'));
|
||||||
|
$this->getValidator()->setAllowedMaxFileSize(min($maxUpload, $maxPost));
|
||||||
/**
|
}
|
||||||
* Set name of template used for Buttons on each file (replace, edit, remove, delete) (without path or extension)
|
|
||||||
*
|
/**
|
||||||
* @param string
|
* Set name of template used for Buttons on each file (replace, edit, remove, delete) (without path or extension)
|
||||||
* @return $this
|
*
|
||||||
*/
|
* @param string
|
||||||
public function setTemplateFileButtons($template) {
|
* @return $this
|
||||||
$this->templateFileButtons = $template;
|
*/
|
||||||
return $this;
|
public function setTemplateFileButtons($template)
|
||||||
}
|
{
|
||||||
|
$this->templateFileButtons = $template;
|
||||||
/**
|
return $this;
|
||||||
* @return string
|
}
|
||||||
*/
|
|
||||||
public function getTemplateFileButtons() {
|
/**
|
||||||
return $this->_templates($this->templateFileButtons, '_FileButtons');
|
* @return string
|
||||||
}
|
*/
|
||||||
|
public function getTemplateFileButtons()
|
||||||
/**
|
{
|
||||||
* Determine if the target folder for new uploads in is visible the field UI.
|
return $this->_templates($this->templateFileButtons, '_FileButtons');
|
||||||
*
|
}
|
||||||
* @return boolean
|
|
||||||
*/
|
/**
|
||||||
public function canPreviewFolder() {
|
* Determine if the target folder for new uploads in is visible the field UI.
|
||||||
if(!$this->isActive()) {
|
*
|
||||||
return false;
|
* @return boolean
|
||||||
}
|
*/
|
||||||
$can = $this->getConfig('canPreviewFolder');
|
public function canPreviewFolder()
|
||||||
if(is_bool($can)) {
|
{
|
||||||
return $can;
|
if (!$this->isActive()) {
|
||||||
}
|
return false;
|
||||||
return Permission::check($can);
|
}
|
||||||
}
|
$can = $this->getConfig('canPreviewFolder');
|
||||||
|
if (is_bool($can)) {
|
||||||
/**
|
return $can;
|
||||||
* Determine if the target folder for new uploads in is visible the field UI.
|
}
|
||||||
* Disable to keep the internal filesystem structure hidden from users.
|
return Permission::check($can);
|
||||||
*
|
}
|
||||||
* @param boolean|string $canPreviewFolder Either a boolean flag, or a
|
|
||||||
* required permission code
|
/**
|
||||||
* @return $this Self reference
|
* Determine if the target folder for new uploads in is visible the field UI.
|
||||||
*/
|
* Disable to keep the internal filesystem structure hidden from users.
|
||||||
public function setCanPreviewFolder($canPreviewFolder) {
|
*
|
||||||
return $this->setConfig('canPreviewFolder', $canPreviewFolder);
|
* @param boolean|string $canPreviewFolder Either a boolean flag, or a
|
||||||
}
|
* required permission code
|
||||||
|
* @return $this Self reference
|
||||||
/**
|
*/
|
||||||
* @param string
|
public function setCanPreviewFolder($canPreviewFolder)
|
||||||
* @return $this
|
{
|
||||||
*/
|
return $this->setConfig('canPreviewFolder', $canPreviewFolder);
|
||||||
public function setDisplayFolderName($name) {
|
}
|
||||||
$this->displayFolderName = $name;
|
|
||||||
return $this;
|
/**
|
||||||
}
|
* @param string
|
||||||
|
* @return $this
|
||||||
/**
|
*/
|
||||||
* @return string
|
public function setDisplayFolderName($name)
|
||||||
*/
|
{
|
||||||
public function getDisplayFolderName() {
|
$this->displayFolderName = $name;
|
||||||
return $this->displayFolderName;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Force a record to be used as "Parent" for uploaded Files (eg a Page with a has_one to File)
|
* @return string
|
||||||
*
|
*/
|
||||||
* @param DataObject $record
|
public function getDisplayFolderName()
|
||||||
* @return $this
|
{
|
||||||
*/
|
return $this->displayFolderName;
|
||||||
public function setRecord($record) {
|
}
|
||||||
$this->record = $record;
|
|
||||||
return $this;
|
/**
|
||||||
}
|
* Force a record to be used as "Parent" for uploaded Files (eg a Page with a has_one to File)
|
||||||
|
*
|
||||||
/**
|
* @param DataObject $record
|
||||||
* 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
|
* @return $this
|
||||||
* use Form->getRecord().
|
*/
|
||||||
*
|
public function setRecord($record)
|
||||||
* @return DataObject
|
{
|
||||||
*/
|
$this->record = $record;
|
||||||
public function getRecord() {
|
return $this;
|
||||||
if (!$this->record
|
}
|
||||||
&& $this->form
|
|
||||||
&& ($record = $this->form->getRecord())
|
/**
|
||||||
&& $record instanceof DataObject
|
* 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().
|
||||||
$this->record = $record;
|
*
|
||||||
}
|
* @return DataObject
|
||||||
return $this->record;
|
*/
|
||||||
}
|
public function getRecord()
|
||||||
|
{
|
||||||
public function setValue($value, $record = null) {
|
if (!$this->record
|
||||||
// Extract value from underlying record
|
&& $this->form
|
||||||
if(empty($value) && $this->getName() && $record instanceof DataObject) {
|
&& ($record = $this->form->getRecord())
|
||||||
$name = $this->getName();
|
&& $record instanceof DataObject
|
||||||
$value = $record->$name;
|
) {
|
||||||
}
|
$this->record = $record;
|
||||||
|
}
|
||||||
// Convert asset container to tuple value
|
return $this->record;
|
||||||
if($value instanceof AssetContainer) {
|
}
|
||||||
if($value->exists()) {
|
|
||||||
$value = array(
|
public function setValue($value, $record = null)
|
||||||
'Filename' => $value->getFilename(),
|
{
|
||||||
'Hash' => $value->getHash(),
|
// Extract value from underlying record
|
||||||
'Variant' => $value->getVariant()
|
if (empty($value) && $this->getName() && $record instanceof DataObject) {
|
||||||
);
|
$name = $this->getName();
|
||||||
} else {
|
$value = $record->$name;
|
||||||
$value = null;
|
}
|
||||||
}
|
|
||||||
}
|
// Convert asset container to tuple value
|
||||||
|
if ($value instanceof AssetContainer) {
|
||||||
// If javascript is disabled, direct file upload (non-html5 style) can
|
if ($value->exists()) {
|
||||||
// trigger a single or multiple file submission. Note that this may be
|
$value = array(
|
||||||
// included in addition to re-submitted File IDs as above, so these
|
'Filename' => $value->getFilename(),
|
||||||
// should be added to the list instead of operated on independently.
|
'Hash' => $value->getHash(),
|
||||||
if($uploadedFile = $this->extractUploadedFileData($value)) {
|
'Variant' => $value->getVariant()
|
||||||
$value = $this->saveTemporaryFile($uploadedFile, $error);
|
);
|
||||||
if(!$value) {
|
} else {
|
||||||
throw new ValidationException($error);
|
$value = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set value using parent
|
// If javascript is disabled, direct file upload (non-html5 style) can
|
||||||
return parent::setValue($value, $record);
|
// 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.
|
||||||
public function Value() {
|
if ($uploadedFile = $this->extractUploadedFileData($value)) {
|
||||||
// Re-override FileField Value to use data value
|
$value = $this->saveTemporaryFile($uploadedFile, $error);
|
||||||
return $this->dataValue();
|
if (!$value) {
|
||||||
}
|
throw new ValidationException($error);
|
||||||
|
}
|
||||||
public function saveInto(DataObjectInterface $record) {
|
}
|
||||||
// Check required relation details are available
|
|
||||||
$name = $this->getName();
|
// Set value using parent
|
||||||
if(!$name) {
|
return parent::setValue($value, $record);
|
||||||
return $this;
|
}
|
||||||
}
|
|
||||||
$value = $this->Value();
|
public function Value()
|
||||||
foreach(array('Filename', 'Hash', 'Variant') as $part) {
|
{
|
||||||
$partValue = isset($value[$part])
|
// Re-override FileField Value to use data value
|
||||||
? $value[$part]
|
return $this->dataValue();
|
||||||
: null;
|
}
|
||||||
$record->setField("{$name}{$part}", $partValue);
|
|
||||||
}
|
public function saveInto(DataObjectInterface $record)
|
||||||
return $this;
|
{
|
||||||
}
|
// Check required relation details are available
|
||||||
|
$name = $this->getName();
|
||||||
/**
|
if (!$name) {
|
||||||
* Assign a front-end config variable for the upload field
|
return $this;
|
||||||
*
|
}
|
||||||
* @see https://github.com/blueimp/jQuery-File-Upload/wiki/Options for the list of front end options available
|
$value = $this->Value();
|
||||||
*
|
foreach (array('Filename', 'Hash', 'Variant') as $part) {
|
||||||
* @param string $key
|
$partValue = isset($value[$part])
|
||||||
* @param mixed $val
|
? $value[$part]
|
||||||
* @return $this self reference
|
: null;
|
||||||
*/
|
$record->setField("{$name}{$part}", $partValue);
|
||||||
public function setConfig($key, $val) {
|
}
|
||||||
$this->ufConfig[$key] = $val;
|
return $this;
|
||||||
return $this;
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Assign a front-end config variable for the upload field
|
||||||
* Gets a front-end config variable for the upload field
|
*
|
||||||
*
|
* @see https://github.com/blueimp/jQuery-File-Upload/wiki/Options for the list of front end options available
|
||||||
* @see https://github.com/blueimp/jQuery-File-Upload/wiki/Options for the list of front end options available
|
*
|
||||||
*
|
* @param string $key
|
||||||
* @param string $key
|
* @param mixed $val
|
||||||
* @return mixed
|
* @return $this self reference
|
||||||
*/
|
*/
|
||||||
public function getConfig($key) {
|
public function setConfig($key, $val)
|
||||||
if(isset($this->ufConfig[$key])) {
|
{
|
||||||
return $this->ufConfig[$key];
|
$this->ufConfig[$key] = $val;
|
||||||
}
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the field should automatically upload the file.
|
* Gets a front-end config variable for the upload field
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @see https://github.com/blueimp/jQuery-File-Upload/wiki/Options for the list of front end options available
|
||||||
*/
|
*
|
||||||
public function getAutoUpload() {
|
* @param string $key
|
||||||
return $this->getConfig('autoUpload');
|
* @return mixed
|
||||||
}
|
*/
|
||||||
|
public function getConfig($key)
|
||||||
/**
|
{
|
||||||
* Determine if the field should automatically upload the file
|
if (isset($this->ufConfig[$key])) {
|
||||||
*
|
return $this->ufConfig[$key];
|
||||||
* @param boolean $autoUpload
|
}
|
||||||
* @return $this Self reference
|
}
|
||||||
*/
|
|
||||||
public function setAutoUpload($autoUpload) {
|
/**
|
||||||
return $this->setConfig('autoUpload', $autoUpload);
|
* Determine if the field should automatically upload the file.
|
||||||
}
|
*
|
||||||
|
* @return boolean
|
||||||
/**
|
*/
|
||||||
* Determine if the user has permission to upload.
|
public function getAutoUpload()
|
||||||
*
|
{
|
||||||
* @return boolean
|
return $this->getConfig('autoUpload');
|
||||||
*/
|
}
|
||||||
public function canUpload() {
|
|
||||||
if(!$this->isActive()) {
|
/**
|
||||||
return false;
|
* Determine if the field should automatically upload the file
|
||||||
}
|
*
|
||||||
$can = $this->getConfig('canUpload');
|
* @param boolean $autoUpload
|
||||||
if(is_bool($can)) {
|
* @return $this Self reference
|
||||||
return $can;
|
*/
|
||||||
}
|
public function setAutoUpload($autoUpload)
|
||||||
return Permission::check($can);
|
{
|
||||||
}
|
return $this->setConfig('autoUpload', $autoUpload);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Specify whether the user can upload files.
|
/**
|
||||||
* String values will be treated as required permission codes
|
* Determine if the user has permission to upload.
|
||||||
*
|
*
|
||||||
* @param bool|string $canUpload Either a boolean flag, or a required
|
* @return boolean
|
||||||
* permission code
|
*/
|
||||||
* @return $this Self reference
|
public function canUpload()
|
||||||
*/
|
{
|
||||||
public function setCanUpload($canUpload) {
|
if (!$this->isActive()) {
|
||||||
return $this->setConfig('canUpload', $canUpload);
|
return false;
|
||||||
}
|
}
|
||||||
|
$can = $this->getConfig('canUpload');
|
||||||
/**
|
if (is_bool($can)) {
|
||||||
* Returns true if the field is neither readonly nor disabled
|
return $can;
|
||||||
*
|
}
|
||||||
* @return bool
|
return Permission::check($can);
|
||||||
*/
|
}
|
||||||
public function isActive() {
|
|
||||||
return !$this->isDisabled() && !$this->isReadonly();
|
/**
|
||||||
}
|
* Specify whether the user can upload files.
|
||||||
|
* String values will be treated as required permission codes
|
||||||
/**
|
*
|
||||||
* Gets thumbnail width. Defaults to 80
|
* @param bool|string $canUpload Either a boolean flag, or a required
|
||||||
*
|
* permission code
|
||||||
* @return int
|
* @return $this Self reference
|
||||||
*/
|
*/
|
||||||
public function getPreviewMaxWidth() {
|
public function setCanUpload($canUpload)
|
||||||
return $this->getConfig('previewMaxWidth');
|
{
|
||||||
}
|
return $this->setConfig('canUpload', $canUpload);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Set thumbnail width.
|
/**
|
||||||
*
|
* Returns true if the field is neither readonly nor disabled
|
||||||
* @param int $previewMaxWidth
|
*
|
||||||
* @return $this Self reference
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function setPreviewMaxWidth($previewMaxWidth) {
|
public function isActive()
|
||||||
return $this->setConfig('previewMaxWidth', $previewMaxWidth);
|
{
|
||||||
}
|
return !$this->isDisabled() && !$this->isReadonly();
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Gets thumbnail height. Defaults to 60
|
/**
|
||||||
*
|
* Gets thumbnail width. Defaults to 80
|
||||||
* @return int
|
*
|
||||||
*/
|
* @return int
|
||||||
public function getPreviewMaxHeight() {
|
*/
|
||||||
return $this->getConfig('previewMaxHeight');
|
public function getPreviewMaxWidth()
|
||||||
}
|
{
|
||||||
|
return $this->getConfig('previewMaxWidth');
|
||||||
/**
|
}
|
||||||
* Set thumbnail height.
|
|
||||||
*
|
/**
|
||||||
* @param int $previewMaxHeight
|
* Set thumbnail width.
|
||||||
* @return $this Self reference
|
*
|
||||||
*/
|
* @param int $previewMaxWidth
|
||||||
public function setPreviewMaxHeight($previewMaxHeight) {
|
* @return $this Self reference
|
||||||
return $this->setConfig('previewMaxHeight', $previewMaxHeight);
|
*/
|
||||||
}
|
public function setPreviewMaxWidth($previewMaxWidth)
|
||||||
|
{
|
||||||
/**
|
return $this->setConfig('previewMaxWidth', $previewMaxWidth);
|
||||||
* javascript template used to display uploading files
|
}
|
||||||
* Defaults to 'ss-uploadfield-uploadtemplate'
|
|
||||||
*
|
/**
|
||||||
* @see javascript/UploadField_uploadtemplate.js
|
* Gets thumbnail height. Defaults to 60
|
||||||
* @return string
|
*
|
||||||
*/
|
* @return int
|
||||||
public function getUploadTemplateName() {
|
*/
|
||||||
return $this->getConfig('uploadTemplateName');
|
public function getPreviewMaxHeight()
|
||||||
}
|
{
|
||||||
|
return $this->getConfig('previewMaxHeight');
|
||||||
/**
|
}
|
||||||
* Set javascript template used to display uploading files
|
|
||||||
*
|
/**
|
||||||
* @param string $uploadTemplateName
|
* Set thumbnail height.
|
||||||
* @return $this Self reference
|
*
|
||||||
*/
|
* @param int $previewMaxHeight
|
||||||
public function setUploadTemplateName($uploadTemplateName) {
|
* @return $this Self reference
|
||||||
return $this->setConfig('uploadTemplateName', $uploadTemplateName);
|
*/
|
||||||
}
|
public function setPreviewMaxHeight($previewMaxHeight)
|
||||||
|
{
|
||||||
/**
|
return $this->setConfig('previewMaxHeight', $previewMaxHeight);
|
||||||
* javascript template used to display already uploaded files
|
}
|
||||||
* Defaults to 'ss-downloadfield-downloadtemplate'
|
|
||||||
*
|
/**
|
||||||
* @see javascript/DownloadField_downloadtemplate.js
|
* javascript template used to display uploading files
|
||||||
* @return string
|
* Defaults to 'ss-uploadfield-uploadtemplate'
|
||||||
*/
|
*
|
||||||
public function getDownloadTemplateName() {
|
* @see javascript/UploadField_uploadtemplate.js
|
||||||
return $this->getConfig('downloadTemplateName');
|
* @return string
|
||||||
}
|
*/
|
||||||
|
public function getUploadTemplateName()
|
||||||
/**
|
{
|
||||||
* Set javascript template used to display already uploaded files
|
return $this->getConfig('uploadTemplateName');
|
||||||
*
|
}
|
||||||
* @param string $downloadTemplateName
|
|
||||||
* @return $this Self reference
|
/**
|
||||||
*/
|
* Set javascript template used to display uploading files
|
||||||
public function setDownloadTemplateName($downloadTemplateName) {
|
*
|
||||||
return $this->setConfig('downloadTemplateName', $downloadTemplateName);
|
* @param string $uploadTemplateName
|
||||||
}
|
* @return $this Self reference
|
||||||
|
*/
|
||||||
public function extraClass() {
|
public function setUploadTemplateName($uploadTemplateName)
|
||||||
if($this->isDisabled()) {
|
{
|
||||||
$this->addExtraClass('disabled');
|
return $this->setConfig('uploadTemplateName', $uploadTemplateName);
|
||||||
}
|
}
|
||||||
if($this->isReadonly()) {
|
|
||||||
$this->addExtraClass('readonly');
|
/**
|
||||||
}
|
* javascript template used to display already uploaded files
|
||||||
|
* Defaults to 'ss-downloadfield-downloadtemplate'
|
||||||
return parent::extraClass();
|
*
|
||||||
}
|
* @see javascript/DownloadField_downloadtemplate.js
|
||||||
|
* @return string
|
||||||
public function Field($properties = array()) {
|
*/
|
||||||
// Calculated config as per jquery.fileupload-ui.js
|
public function getDownloadTemplateName()
|
||||||
$config = array(
|
{
|
||||||
'allowedMaxFileNumber' => 1, // Only one file allowed for AssetField
|
return $this->getConfig('downloadTemplateName');
|
||||||
'url' => $this->Link('upload'),
|
}
|
||||||
'urlSelectDialog' => $this->Link('select'),
|
|
||||||
'urlAttach' => $this->Link('attach'),
|
/**
|
||||||
'urlFileExists' => $this->link('fileexists'),
|
* Set javascript template used to display already uploaded files
|
||||||
'acceptFileTypes' => '.+$',
|
*
|
||||||
// Fileupload treats maxNumberOfFiles as the max number of _additional_ items allowed
|
* @param string $downloadTemplateName
|
||||||
'maxNumberOfFiles' => $this->Value() ? 0 : 1,
|
* @return $this Self reference
|
||||||
'replaceFile' => false, // Should always be false for AssetField
|
*/
|
||||||
);
|
public function setDownloadTemplateName($downloadTemplateName)
|
||||||
|
{
|
||||||
// Validation: File extensions
|
return $this->setConfig('downloadTemplateName', $downloadTemplateName);
|
||||||
if ($allowedExtensions = $this->getAllowedExtensions()) {
|
}
|
||||||
$config['acceptFileTypes'] = '(\.|\/)(' . implode('|', $allowedExtensions) . ')$';
|
|
||||||
$config['errorMessages']['acceptFileTypes'] = _t(
|
public function extraClass()
|
||||||
'File.INVALIDEXTENSIONSHORT',
|
{
|
||||||
'Extension is not allowed'
|
if ($this->isDisabled()) {
|
||||||
);
|
$this->addExtraClass('disabled');
|
||||||
}
|
}
|
||||||
|
if ($this->isReadonly()) {
|
||||||
// Validation: File size
|
$this->addExtraClass('readonly');
|
||||||
if ($allowedMaxFileSize = $this->getValidator()->getAllowedMaxFileSize()) {
|
}
|
||||||
$config['maxFileSize'] = $allowedMaxFileSize;
|
|
||||||
$config['errorMessages']['maxFileSize'] = _t(
|
return parent::extraClass();
|
||||||
'File.TOOLARGESHORT',
|
}
|
||||||
'Filesize exceeds {size}',
|
|
||||||
array('size' => File::format_size($config['maxFileSize']))
|
public function Field($properties = array())
|
||||||
);
|
{
|
||||||
}
|
// Calculated config as per jquery.fileupload-ui.js
|
||||||
|
$config = array(
|
||||||
$mergedConfig = array_merge($config, $this->ufConfig);
|
'allowedMaxFileNumber' => 1, // Only one file allowed for AssetField
|
||||||
return $this->customise(array(
|
'url' => $this->Link('upload'),
|
||||||
'ConfigString' => Convert::raw2json($mergedConfig),
|
'urlSelectDialog' => $this->Link('select'),
|
||||||
'UploadFieldFileButtons' => $this->renderWith($this->getTemplateFileButtons())
|
'urlAttach' => $this->Link('attach'),
|
||||||
))->renderWith($this->getTemplates());
|
'urlFileExists' => $this->link('fileexists'),
|
||||||
}
|
'acceptFileTypes' => '.+$',
|
||||||
|
// Fileupload treats maxNumberOfFiles as the max number of _additional_ items allowed
|
||||||
/**
|
'maxNumberOfFiles' => $this->Value() ? 0 : 1,
|
||||||
* Validation method for this field, called when the entire form is validated
|
'replaceFile' => false, // Should always be false for AssetField
|
||||||
*
|
);
|
||||||
* @param Validator $validator
|
|
||||||
* @return boolean
|
// Validation: File extensions
|
||||||
*/
|
if ($allowedExtensions = $this->getAllowedExtensions()) {
|
||||||
public function validate($validator) {
|
$config['acceptFileTypes'] = '(\.|\/)(' . implode('|', $allowedExtensions) . ')$';
|
||||||
$name = $this->getName();
|
$config['errorMessages']['acceptFileTypes'] = _t(
|
||||||
$value = $this->Value();
|
'File.INVALIDEXTENSIONSHORT',
|
||||||
|
'Extension is not allowed'
|
||||||
// If there is no file then quit
|
);
|
||||||
if(!$value) {
|
}
|
||||||
return true;
|
|
||||||
}
|
// Validation: File size
|
||||||
|
if ($allowedMaxFileSize = $this->getValidator()->getAllowedMaxFileSize()) {
|
||||||
// Revalidate each file against nested validator
|
$config['maxFileSize'] = $allowedMaxFileSize;
|
||||||
$this->getUpload()->clearErrors();
|
$config['errorMessages']['maxFileSize'] = _t(
|
||||||
|
'File.TOOLARGESHORT',
|
||||||
// Generate $_FILES style file attribute array for upload validator
|
'Filesize exceeds {size}',
|
||||||
$store = $this->getAssetStore();
|
array('size' => File::format_size($config['maxFileSize']))
|
||||||
$mime = $store->getMimeType($value['Filename'], $value['Hash'], $value['Variant']);
|
);
|
||||||
$metadata = $store->getMetadata($value['Filename'], $value['Hash'], $value['Variant']);
|
}
|
||||||
$tmpFile = array(
|
|
||||||
'name' => $value['Filename'],
|
$mergedConfig = array_merge($config, $this->ufConfig);
|
||||||
'type' => $mime,
|
return $this->customise(array(
|
||||||
'size' => isset($metadata['size']) ? $metadata['size'] : 0,
|
'ConfigString' => Convert::raw2json($mergedConfig),
|
||||||
'tmp_name' => null, // Should bypass is_uploaded_file check
|
'UploadFieldFileButtons' => $this->renderWith($this->getTemplateFileButtons())
|
||||||
'error' => UPLOAD_ERR_OK,
|
))->renderWith($this->getTemplates());
|
||||||
);
|
}
|
||||||
$this->getUpload()->validate($tmpFile);
|
|
||||||
|
/**
|
||||||
// Check all errors
|
* Validation method for this field, called when the entire form is validated
|
||||||
if($errors = $this->getUpload()->getErrors()) {
|
*
|
||||||
foreach($errors as $error) {
|
* @param Validator $validator
|
||||||
$validator->validationError($name, $error, "validation");
|
* @return boolean
|
||||||
}
|
*/
|
||||||
return false;
|
public function validate($validator)
|
||||||
}
|
{
|
||||||
|
$name = $this->getName();
|
||||||
return true;
|
$value = $this->Value();
|
||||||
}
|
|
||||||
|
// If there is no file then quit
|
||||||
/**
|
if (!$value) {
|
||||||
* Given an array of post variables, extract all temporary file data into an array
|
return true;
|
||||||
*
|
}
|
||||||
* @param array $postVars Array of posted form data
|
|
||||||
* @return array data for uploaded file
|
// Revalidate each file against nested validator
|
||||||
*/
|
$this->getUpload()->clearErrors();
|
||||||
protected function extractUploadedFileData($postVars) {
|
|
||||||
// Note: Format of posted file parameters in php is a feature of using
|
// Generate $_FILES style file attribute array for upload validator
|
||||||
// <input name='{$Name}[Upload]' /> for multiple file uploads
|
$store = $this->getAssetStore();
|
||||||
|
$mime = $store->getMimeType($value['Filename'], $value['Hash'], $value['Variant']);
|
||||||
// Skip empty file
|
$metadata = $store->getMetadata($value['Filename'], $value['Hash'], $value['Variant']);
|
||||||
if(empty($postVars['tmp_name'])) {
|
$tmpFile = array(
|
||||||
return null;
|
'name' => $value['Filename'],
|
||||||
}
|
'type' => $mime,
|
||||||
|
'size' => isset($metadata['size']) ? $metadata['size'] : 0,
|
||||||
// Return single level array for posted file
|
'tmp_name' => null, // Should bypass is_uploaded_file check
|
||||||
/** @skipUpgrade */
|
'error' => UPLOAD_ERR_OK,
|
||||||
if(empty($postVars['tmp_name']['Upload'])) {
|
);
|
||||||
return $postVars;
|
$this->getUpload()->validate($tmpFile);
|
||||||
}
|
|
||||||
|
// Check all errors
|
||||||
// Extract posted feedback value
|
if ($errors = $this->getUpload()->getErrors()) {
|
||||||
$tmpFile = array();
|
foreach ($errors as $error) {
|
||||||
foreach(array('name', 'type', 'tmp_name', 'error', 'size') as $field) {
|
$validator->validationError($name, $error, "validation");
|
||||||
/** @skipUpgrade */
|
}
|
||||||
$tmpFile[$field] = $postVars[$field]['Upload'];
|
return false;
|
||||||
}
|
}
|
||||||
return $tmpFile;
|
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Loads the temporary file data into the asset store, and return the tuple details
|
/**
|
||||||
* for the result.
|
* Given an array of post variables, extract all temporary file data into an array
|
||||||
*
|
*
|
||||||
* @param array $tmpFile Temporary file data
|
* @param array $postVars Array of posted form data
|
||||||
* @param string $error Error message
|
* @return array data for uploaded file
|
||||||
* @return array Result of saved file, or null if error
|
*/
|
||||||
*/
|
protected function extractUploadedFileData($postVars)
|
||||||
protected function saveTemporaryFile($tmpFile, &$error = null) {
|
{
|
||||||
$error = null;
|
// Note: Format of posted file parameters in php is a feature of using
|
||||||
if (empty($tmpFile)) {
|
// <input name='{$Name}[Upload]' /> for multiple file uploads
|
||||||
$error = _t('UploadField.FIELDNOTSET', 'File information not found');
|
|
||||||
return null;
|
// Skip empty file
|
||||||
}
|
if (empty($postVars['tmp_name'])) {
|
||||||
|
return null;
|
||||||
if($tmpFile['error']) {
|
}
|
||||||
$error = $tmpFile['error'];
|
|
||||||
return null;
|
// Return single level array for posted file
|
||||||
}
|
/** @skipUpgrade */
|
||||||
|
if (empty($postVars['tmp_name']['Upload'])) {
|
||||||
// Get the uploaded file into a new file object.
|
return $postVars;
|
||||||
try {
|
}
|
||||||
$result = $this
|
|
||||||
->getUpload()
|
// Extract posted feedback value
|
||||||
->load($tmpFile, $this->getFolderName());
|
$tmpFile = array();
|
||||||
} catch (Exception $e) {
|
foreach (array('name', 'type', 'tmp_name', 'error', 'size') as $field) {
|
||||||
// we shouldn't get an error here, but just in case
|
/** @skipUpgrade */
|
||||||
$error = $e->getMessage();
|
$tmpFile[$field] = $postVars[$field]['Upload'];
|
||||||
return null;
|
}
|
||||||
}
|
return $tmpFile;
|
||||||
|
}
|
||||||
// Check if upload field has an error
|
|
||||||
if ($this->getUpload()->isError()) {
|
/**
|
||||||
$error = implode(' ' . PHP_EOL, $this->getUpload()->getErrors());
|
* Loads the temporary file data into the asset store, and return the tuple details
|
||||||
return null;
|
* for the result.
|
||||||
}
|
*
|
||||||
|
* @param array $tmpFile Temporary file data
|
||||||
// return tuple array of Filename, Hash and Variant
|
* @param string $error Error message
|
||||||
return $result;
|
* @return array Result of saved file, or null if error
|
||||||
}
|
*/
|
||||||
|
protected function saveTemporaryFile($tmpFile, &$error = null)
|
||||||
/**
|
{
|
||||||
* Safely encodes the File object with all standard fields required
|
$error = null;
|
||||||
* by the front end
|
if (empty($tmpFile)) {
|
||||||
*
|
$error = _t('UploadField.FIELDNOTSET', 'File information not found');
|
||||||
* @param string $filename
|
return null;
|
||||||
* @param string $hash
|
}
|
||||||
* @param string $variant
|
|
||||||
* @return array Encoded list of file attributes
|
if ($tmpFile['error']) {
|
||||||
*/
|
$error = $tmpFile['error'];
|
||||||
protected function encodeAssetAttributes($filename, $hash, $variant) {
|
return null;
|
||||||
// Force regeneration of file thumbnail for this tuple (without saving into db)
|
}
|
||||||
$object = DBFile::create();
|
|
||||||
$object->setValue(array('Filename' => $filename, 'Hash' => $hash, 'Variant' => $variant));
|
// Get the uploaded file into a new file object.
|
||||||
|
try {
|
||||||
return array(
|
$result = $this
|
||||||
'filename' => $filename,
|
->getUpload()
|
||||||
'hash' => $hash,
|
->load($tmpFile, $this->getFolderName());
|
||||||
'variant' => $variant,
|
} catch (Exception $e) {
|
||||||
'name' => $object->getBasename(),
|
// we shouldn't get an error here, but just in case
|
||||||
'url' => $object->getURL(),
|
$error = $e->getMessage();
|
||||||
'thumbnail_url' => $object->ThumbnailURL(
|
return null;
|
||||||
$this->getPreviewMaxWidth(),
|
}
|
||||||
$this->getPreviewMaxHeight()
|
|
||||||
),
|
// Check if upload field has an error
|
||||||
'size' => $object->getAbsoluteSize(),
|
if ($this->getUpload()->isError()) {
|
||||||
'type' => File::get_file_type($object->getFilename()),
|
$error = implode(' ' . PHP_EOL, $this->getUpload()->getErrors());
|
||||||
'buttons' => (string)$this->renderWith($this->getTemplateFileButtons()),
|
return null;
|
||||||
'fieldname' => $this->getName()
|
}
|
||||||
);
|
|
||||||
}
|
// return tuple array of Filename, Hash and Variant
|
||||||
|
return $result;
|
||||||
/**
|
}
|
||||||
* Action to handle upload of a single file
|
|
||||||
*
|
/**
|
||||||
* @param HTTPRequest $request
|
* Safely encodes the File object with all standard fields required
|
||||||
* @return HTTPResponse
|
* by the front end
|
||||||
*/
|
*
|
||||||
public function upload(HTTPRequest $request) {
|
* @param string $filename
|
||||||
if($this->isDisabled() || $this->isReadonly() || !$this->canUpload()) {
|
* @param string $hash
|
||||||
return $this->httpError(403);
|
* @param string $variant
|
||||||
}
|
* @return array Encoded list of file attributes
|
||||||
|
*/
|
||||||
// Protect against CSRF on destructive action
|
protected function encodeAssetAttributes($filename, $hash, $variant)
|
||||||
$token = $this
|
{
|
||||||
->getForm()
|
// Force regeneration of file thumbnail for this tuple (without saving into db)
|
||||||
->getSecurityToken();
|
$object = DBFile::create();
|
||||||
if(!$token->checkRequest($request)) {
|
$object->setValue(array('Filename' => $filename, 'Hash' => $hash, 'Variant' => $variant));
|
||||||
return $this->httpError(400);
|
|
||||||
}
|
return array(
|
||||||
|
'filename' => $filename,
|
||||||
// Get form details
|
'hash' => $hash,
|
||||||
$name = $this->getName();
|
'variant' => $variant,
|
||||||
$postVars = $request->postVar($name);
|
'name' => $object->getBasename(),
|
||||||
|
'url' => $object->getURL(),
|
||||||
// Extract uploaded files from Form data
|
'thumbnail_url' => $object->ThumbnailURL(
|
||||||
$uploadedFile = $this->extractUploadedFileData($postVars);
|
$this->getPreviewMaxWidth(),
|
||||||
if(!$uploadedFile) {
|
$this->getPreviewMaxHeight()
|
||||||
return $this->httpError(400);
|
),
|
||||||
}
|
'size' => $object->getAbsoluteSize(),
|
||||||
|
'type' => File::get_file_type($object->getFilename()),
|
||||||
// Save the temporary files into a File objects
|
'buttons' => (string)$this->renderWith($this->getTemplateFileButtons()),
|
||||||
// and save data/error on a per file basis
|
'fieldname' => $this->getName()
|
||||||
$result = $this->saveTemporaryFile($uploadedFile, $error);
|
);
|
||||||
if(empty($result)) {
|
}
|
||||||
$return = array('error' => $error);
|
|
||||||
} else {
|
/**
|
||||||
$return = $this->encodeAssetAttributes($result['Filename'], $result['Hash'], $result['Variant']);
|
* Action to handle upload of a single file
|
||||||
}
|
*
|
||||||
$this
|
* @param HTTPRequest $request
|
||||||
->getUpload()
|
* @return HTTPResponse
|
||||||
->clearErrors();
|
*/
|
||||||
|
public function upload(HTTPRequest $request)
|
||||||
// Format response with json
|
{
|
||||||
$response = new HTTPResponse(Convert::raw2json(array($return)));
|
if ($this->isDisabled() || $this->isReadonly() || !$this->canUpload()) {
|
||||||
$response->addHeader('Content-Type', 'text/plain');
|
return $this->httpError(403);
|
||||||
return $response;
|
}
|
||||||
}
|
|
||||||
|
// Protect against CSRF on destructive action
|
||||||
public function performReadonlyTransformation() {
|
$token = $this
|
||||||
$clone = clone $this;
|
->getForm()
|
||||||
$clone->addExtraClass('readonly');
|
->getSecurityToken();
|
||||||
$clone->setReadonly(true);
|
if (!$token->checkRequest($request)) {
|
||||||
return $clone;
|
return $this->httpError(400);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Get form details
|
||||||
* Gets the foreign class that needs to be created, or 'File' as default if there
|
$name = $this->getName();
|
||||||
* is no relationship, or it cannot be determined.
|
$postVars = $request->postVar($name);
|
||||||
*
|
|
||||||
* @param string $default Default value to return if no value could be calculated
|
// Extract uploaded files from Form data
|
||||||
* @return string Foreign class name.
|
$uploadedFile = $this->extractUploadedFileData($postVars);
|
||||||
*/
|
if (!$uploadedFile) {
|
||||||
public function getRelationAutosetClass($default = 'SilverStripe\\Assets\\File') {
|
return $this->httpError(400);
|
||||||
|
}
|
||||||
// Don't autodetermine relation if no relationship between parent record
|
|
||||||
if(!$this->relationAutoSetting) return $default;
|
// Save the temporary files into a File objects
|
||||||
|
// and save data/error on a per file basis
|
||||||
// Check record and name
|
$result = $this->saveTemporaryFile($uploadedFile, $error);
|
||||||
$name = $this->getName();
|
if (empty($result)) {
|
||||||
$record = $this->getRecord();
|
$return = array('error' => $error);
|
||||||
if(empty($name) || empty($record)) {
|
} else {
|
||||||
return $default;
|
$return = $this->encodeAssetAttributes($result['Filename'], $result['Hash'], $result['Variant']);
|
||||||
} else {
|
}
|
||||||
$class = $record->getRelationClass($name);
|
$this
|
||||||
return empty($class) ? $default : $class;
|
->getUpload()
|
||||||
}
|
->clearErrors();
|
||||||
}
|
|
||||||
|
// Format response with json
|
||||||
/**
|
$response = new HTTPResponse(Convert::raw2json(array($return)));
|
||||||
* @return AssetStore
|
$response->addHeader('Content-Type', 'text/plain');
|
||||||
*/
|
return $response;
|
||||||
protected function getAssetStore() {
|
}
|
||||||
return Injector::inst()->get('AssetStore');
|
|
||||||
}
|
public function performReadonlyTransformation()
|
||||||
|
{
|
||||||
public function getAttributes() {
|
$clone = clone $this;
|
||||||
return array_merge(
|
$clone->addExtraClass('readonly');
|
||||||
parent::getAttributes(),
|
$clone->setReadonly(true);
|
||||||
['type' => 'file']
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return AssetStore
|
||||||
|
*/
|
||||||
|
protected function getAssetStore()
|
||||||
|
{
|
||||||
|
return Injector::inst()->get('AssetStore');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAttributes()
|
||||||
|
{
|
||||||
|
return array_merge(
|
||||||
|
parent::getAttributes(),
|
||||||
|
['type' => 'file']
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,156 +25,168 @@ use SilverStripe\Core\Object;
|
|||||||
* <code>
|
* <code>
|
||||||
* class ExampleForm_Controller extends Page_Controller {
|
* class ExampleForm_Controller extends Page_Controller {
|
||||||
*
|
*
|
||||||
* function Form() {
|
* function Form() {
|
||||||
* $fields = new FieldList(
|
* $fields = new FieldList(
|
||||||
* new TextField('MyName'),
|
* new TextField('MyName'),
|
||||||
* new FileField('MyFile')
|
* new FileField('MyFile')
|
||||||
* );
|
* );
|
||||||
* $actions = new FieldList(
|
* $actions = new FieldList(
|
||||||
* new FormAction('doUpload', 'Upload file')
|
* new FormAction('doUpload', 'Upload file')
|
||||||
* );
|
* );
|
||||||
* $validator = new RequiredFields(array('MyName', 'MyFile'));
|
* $validator = new RequiredFields(array('MyName', 'MyFile'));
|
||||||
*
|
*
|
||||||
* return new Form($this, 'Form', $fields, $actions, $validator);
|
* return new Form($this, 'Form', $fields, $actions, $validator);
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* function doUpload($data, $form) {
|
* function doUpload($data, $form) {
|
||||||
* $file = $data['MyFile'];
|
* $file = $data['MyFile'];
|
||||||
* $content = file_get_contents($file['tmp_name']);
|
* $content = file_get_contents($file['tmp_name']);
|
||||||
* // ... process content
|
* // ... process content
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* </code>
|
* </code>
|
||||||
*/
|
*/
|
||||||
class FileField extends FormField {
|
class FileField extends FormField
|
||||||
use UploadReceiver;
|
{
|
||||||
|
use UploadReceiver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag to automatically determine and save a has_one-relationship
|
* Flag to automatically determine and save a has_one-relationship
|
||||||
* on the saved record (e.g. a "Player" has_one "PlayerImage" would
|
* on the saved record (e.g. a "Player" has_one "PlayerImage" would
|
||||||
* trigger saving the ID of newly created file into "PlayerImageID"
|
* trigger saving the ID of newly created file into "PlayerImageID"
|
||||||
* on the record).
|
* on the record).
|
||||||
*
|
*
|
||||||
* @var boolean
|
* @var boolean
|
||||||
*/
|
*/
|
||||||
protected $relationAutoSetting = true;
|
protected $relationAutoSetting = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new file field.
|
* Create a new file field.
|
||||||
*
|
*
|
||||||
* @param string $name The internal field name, passed to forms.
|
* @param string $name The internal field name, passed to forms.
|
||||||
* @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->constructUploadReceiver();
|
{
|
||||||
parent::__construct($name, $title, $value);
|
$this->constructUploadReceiver();
|
||||||
}
|
parent::__construct($name, $title, $value);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @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(
|
{
|
||||||
'MaxFileSize' => $this->getValidator()->getAllowedMaxFileSize()
|
$properties = array_merge($properties, array(
|
||||||
));
|
'MaxFileSize' => $this->getValidator()->getAllowedMaxFileSize()
|
||||||
|
));
|
||||||
|
|
||||||
return parent::Field($properties);
|
return parent::Field($properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAttributes() {
|
public function getAttributes()
|
||||||
return array_merge(
|
{
|
||||||
parent::getAttributes(),
|
return array_merge(
|
||||||
array('type' => 'file')
|
parent::getAttributes(),
|
||||||
);
|
array('type' => 'file')
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param DataObject|DataObjectInterface $record
|
* @param DataObject|DataObjectInterface $record
|
||||||
*/
|
*/
|
||||||
public function saveInto(DataObjectInterface $record) {
|
public function saveInto(DataObjectInterface $record)
|
||||||
if(!isset($_FILES[$this->name])) {
|
{
|
||||||
return;
|
if (!isset($_FILES[$this->name])) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$fileClass = File::get_class_for_file_extension(
|
$fileClass = File::get_class_for_file_extension(
|
||||||
File::get_file_extension($_FILES[$this->name]['name'])
|
File::get_file_extension($_FILES[$this->name]['name'])
|
||||||
);
|
);
|
||||||
|
|
||||||
/** @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);
|
||||||
}
|
}
|
||||||
} else if($record instanceof File) {
|
} elseif ($record instanceof File) {
|
||||||
$file = $record;
|
$file = $record;
|
||||||
} else {
|
} else {
|
||||||
$file = Object::create($fileClass);
|
$file = Object::create($fileClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
$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;
|
||||||
}
|
}
|
||||||
|
|
||||||
$file = $this->upload->getFile();
|
$file = $this->upload->getFile();
|
||||||
|
|
||||||
$record->{$this->name . 'ID'} = $file->ID;
|
$record->{$this->name . 'ID'} = $file->ID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
public function validate($validator)
|
||||||
if(!isset($_FILES[$this->name])) return true;
|
{
|
||||||
|
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) foreach($errors as $error) {
|
if ($errors) {
|
||||||
$validator->validationError($this->name, $error, "validation");
|
foreach ($errors as $error) {
|
||||||
}
|
$validator->validationError($this->name, $error, "validation");
|
||||||
return false;
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set if relation can be automatically assigned to the underlying dataobject
|
* Set if relation can be automatically assigned to the underlying dataobject
|
||||||
*
|
*
|
||||||
* @param bool $auto
|
* @param bool $auto
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setRelationAutoSetting($auto) {
|
public function setRelationAutoSetting($auto)
|
||||||
$this->relationAutoSetting = $auto;
|
{
|
||||||
return $this;
|
$this->relationAutoSetting = $auto;
|
||||||
}
|
return $this;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Check if relation can be automatically assigned to the underlying dataobject
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function getRelationAutoSetting() {
|
|
||||||
return $this->relationAutoSetting;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if relation can be automatically assigned to the underlying dataobject
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getRelationAutoSetting()
|
||||||
|
{
|
||||||
|
return $this->relationAutoSetting;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,373 +29,388 @@ use SilverStripe\ORM\ValidationException;
|
|||||||
*
|
*
|
||||||
* @mixin FormField
|
* @mixin FormField
|
||||||
*/
|
*/
|
||||||
trait FileUploadReceiver {
|
trait FileUploadReceiver
|
||||||
use UploadReceiver;
|
{
|
||||||
|
use UploadReceiver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag to automatically determine and save a has_one-relationship
|
* Flag to automatically determine and save a has_one-relationship
|
||||||
* on the saved record (e.g. a "Player" has_one "PlayerImage" would
|
* on the saved record (e.g. a "Player" has_one "PlayerImage" would
|
||||||
* trigger saving the ID of newly created file into "PlayerImageID"
|
* trigger saving the ID of newly created file into "PlayerImageID"
|
||||||
* on the record).
|
* on the record).
|
||||||
*
|
*
|
||||||
* @var boolean
|
* @var boolean
|
||||||
*/
|
*/
|
||||||
public $relationAutoSetting = true;
|
public $relationAutoSetting = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parent data record. Will be infered from parent form or controller if blank.
|
* Parent data record. Will be infered from parent form or controller if blank.
|
||||||
*
|
*
|
||||||
* @var DataObject
|
* @var DataObject
|
||||||
*/
|
*/
|
||||||
protected $record;
|
protected $record;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Items loaded into this field. May be a RelationList, or any other SS_List
|
* Items loaded into this field. May be a RelationList, or any other SS_List
|
||||||
*
|
*
|
||||||
* @var SS_List
|
* @var SS_List
|
||||||
*/
|
*/
|
||||||
protected $items;
|
protected $items;
|
||||||
|
|
||||||
protected function constructFileUploadReceiver() {
|
protected function constructFileUploadReceiver()
|
||||||
$this->constructUploadReceiver();
|
{
|
||||||
}
|
$this->constructUploadReceiver();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Force a record to be used as "Parent" for uploaded Files (eg a Page with a has_one to File)
|
* Force a record to be used as "Parent" for uploaded Files (eg a Page with a has_one to File)
|
||||||
*
|
*
|
||||||
* @param DataObject $record
|
* @param DataObject $record
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setRecord($record) {
|
public function setRecord($record)
|
||||||
$this->record = $record;
|
{
|
||||||
return $this;
|
$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()
|
* 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
|
*
|
||||||
*/
|
* @return DataObject
|
||||||
public function getRecord() {
|
*/
|
||||||
if ($this->record) {
|
public function getRecord()
|
||||||
return $this->record;
|
{
|
||||||
}
|
if ($this->record) {
|
||||||
if (!$this->getForm()) {
|
return $this->record;
|
||||||
return null;
|
}
|
||||||
}
|
if (!$this->getForm()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Get record from form
|
// Get record from form
|
||||||
$record = $this->getForm()->getRecord();
|
$record = $this->getForm()->getRecord();
|
||||||
if ($record && ($record instanceof DataObject)) {
|
if ($record && ($record instanceof DataObject)) {
|
||||||
$this->record = $record;
|
$this->record = $record;
|
||||||
return $record;
|
return $record;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get record from controller
|
// Get record from controller
|
||||||
$controller = $this->getForm()->getController();
|
$controller = $this->getForm()->getController();
|
||||||
if ($controller
|
if ($controller
|
||||||
&& $controller->hasMethod('data')
|
&& $controller->hasMethod('data')
|
||||||
&& ($record = $controller->data())
|
&& ($record = $controller->data())
|
||||||
&& ($record instanceof DataObject)
|
&& ($record instanceof DataObject)
|
||||||
) {
|
) {
|
||||||
$this->record = $record;
|
$this->record = $record;
|
||||||
return $record;
|
return $record;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the related record values into this field. This can be uploaded
|
* Loads the related record values into this field. This can be uploaded
|
||||||
* in one of three ways:
|
* in one of three ways:
|
||||||
*
|
*
|
||||||
* - By passing in a list of file IDs in the $value parameter (an array with a single
|
* - 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).
|
* 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
|
* - By passing in an explicit list of File objects in the $record parameter, and
|
||||||
* leaving $value blank.
|
* leaving $value blank.
|
||||||
* - By passing in a dataobject in the $record parameter, from which file objects
|
* - By passing in a dataobject in the $record parameter, from which file objects
|
||||||
* will be extracting using the field name as the relation field.
|
* 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
|
* Each of these methods will update both the items (list of File objects) and the
|
||||||
* field value (list of file ID values).
|
* field value (list of file ID values).
|
||||||
*
|
*
|
||||||
* @param array $value Array of submitted form data, if submitting from a form
|
* @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,
|
* @param array|DataObject|SS_List $record Full source record, either as a DataObject,
|
||||||
* SS_List of items, or an array of submitted form data
|
* SS_List of items, or an array of submitted form data
|
||||||
* @return $this Self reference
|
* @return $this Self reference
|
||||||
* @throws ValidationException
|
* @throws ValidationException
|
||||||
*/
|
*/
|
||||||
public function setValue($value, $record = null) {
|
public function setValue($value, $record = null)
|
||||||
|
{
|
||||||
|
|
||||||
// If we're not passed a value directly, we can attempt to infer the field
|
// If we're not passed a value directly, we can attempt to infer the field
|
||||||
// value from the second parameter by inspecting its relations
|
// value from the second parameter by inspecting its relations
|
||||||
$items = new ArrayList();
|
$items = new ArrayList();
|
||||||
|
|
||||||
// Determine format of presented data
|
// Determine format of presented data
|
||||||
if(empty($value) && $record) {
|
if (empty($value) && $record) {
|
||||||
// If a record is given as a second parameter, but no submitted values,
|
// If a record is given as a second parameter, but no submitted values,
|
||||||
// then we should inspect this instead for the form values
|
// then we should inspect this instead for the form values
|
||||||
|
|
||||||
if(($record instanceof DataObject) && $record->hasMethod($this->getName())) {
|
if (($record instanceof DataObject) && $record->hasMethod($this->getName())) {
|
||||||
// If given a dataobject use reflection to extract details
|
// If given a dataobject use reflection to extract details
|
||||||
|
|
||||||
$data = $record->{$this->getName()}();
|
$data = $record->{$this->getName()}();
|
||||||
if($data instanceof DataObject) {
|
if ($data instanceof DataObject) {
|
||||||
// If has_one, add sole item to default list
|
// If has_one, add sole item to default list
|
||||||
$items->push($data);
|
$items->push($data);
|
||||||
} elseif($data instanceof SS_List) {
|
} elseif ($data instanceof SS_List) {
|
||||||
// For many_many and has_many relations we can use the relation list directly
|
// For many_many and has_many relations we can use the relation list directly
|
||||||
$items = $data;
|
$items = $data;
|
||||||
}
|
}
|
||||||
} elseif($record instanceof SS_List) {
|
} elseif ($record instanceof SS_List) {
|
||||||
// If directly passing a list then save the items directly
|
// If directly passing a list then save the items directly
|
||||||
$items = $record;
|
$items = $record;
|
||||||
}
|
}
|
||||||
} elseif(!empty($value['Files'])) {
|
} elseif (!empty($value['Files'])) {
|
||||||
// If value is given as an array (such as a posted form), extract File IDs from this
|
// If value is given as an array (such as a posted form), extract File IDs from this
|
||||||
$class = $this->getRelationAutosetClass();
|
$class = $this->getRelationAutosetClass();
|
||||||
$items = DataObject::get($class)->byIDs($value['Files']);
|
$items = DataObject::get($class)->byIDs($value['Files']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If javascript is disabled, direct file upload (non-html5 style) can
|
// If javascript is disabled, direct file upload (non-html5 style) can
|
||||||
// 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($uploadedFiles = $this->extractUploadedFileData($value)) {
|
if ($uploadedFiles = $this->extractUploadedFileData($value)) {
|
||||||
foreach($uploadedFiles as $tempFile) {
|
foreach ($uploadedFiles as $tempFile) {
|
||||||
$file = $this->saveTemporaryFile($tempFile, $error);
|
$file = $this->saveTemporaryFile($tempFile, $error);
|
||||||
if($file) {
|
if ($file) {
|
||||||
$items->add($file);
|
$items->add($file);
|
||||||
} else {
|
} else {
|
||||||
throw new ValidationException($error);
|
throw new ValidationException($error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter items by what's allowed to be viewed
|
// Filter items by what's allowed to be viewed
|
||||||
$filteredItems = new ArrayList();
|
$filteredItems = new ArrayList();
|
||||||
$fileIDs = array();
|
$fileIDs = array();
|
||||||
foreach($items as $file) {
|
foreach ($items as $file) {
|
||||||
if($file->exists() && $file->canView()) {
|
if ($file->exists() && $file->canView()) {
|
||||||
$filteredItems->push($file);
|
$filteredItems->push($file);
|
||||||
$fileIDs[] = $file->ID;
|
$fileIDs[] = $file->ID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter and cache updated item list
|
// Filter and cache updated item list
|
||||||
$this->items = $filteredItems;
|
$this->items = $filteredItems;
|
||||||
// Same format as posted form values for this field. Also ensures that
|
// Same format as posted form values for this field. Also ensures that
|
||||||
// $this->setValue($this->getValue()); is non-destructive
|
// $this->setValue($this->getValue()); is non-destructive
|
||||||
$value = $fileIDs ? array('Files' => $fileIDs) : null;
|
$value = $fileIDs ? array('Files' => $fileIDs) : null;
|
||||||
|
|
||||||
// Set value using parent
|
// Set value using parent
|
||||||
parent::setValue($value, $record);
|
parent::setValue($value, $record);
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the items assigned to this field as an SS_List of File objects.
|
* 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
|
* Calling setItems will also update the value of this field, as well as
|
||||||
* updating the internal list of File items.
|
* updating the internal list of File items.
|
||||||
*
|
*
|
||||||
* @param SS_List $items
|
* @param SS_List $items
|
||||||
* @return $this self reference
|
* @return $this self reference
|
||||||
*/
|
*/
|
||||||
public function setItems(SS_List $items) {
|
public function setItems(SS_List $items)
|
||||||
return $this->setValue(null, $items);
|
{
|
||||||
}
|
return $this->setValue(null, $items);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the current list of files
|
* Retrieves the current list of files
|
||||||
*
|
*
|
||||||
* @return SS_List|File[]
|
* @return SS_List|File[]
|
||||||
*/
|
*/
|
||||||
public function getItems() {
|
public function getItems()
|
||||||
return $this->items ? $this->items : new ArrayList();
|
{
|
||||||
}
|
return $this->items ? $this->items : new ArrayList();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the list of selected file IDs
|
* Retrieves the list of selected file IDs
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getItemIDs() {
|
public function getItemIDs()
|
||||||
$value = $this->Value();
|
{
|
||||||
return empty($value['Files']) ? array() : $value['Files'];
|
$value = $this->Value();
|
||||||
}
|
return empty($value['Files']) ? array() : $value['Files'];
|
||||||
|
}
|
||||||
|
|
||||||
public function Value() {
|
public function Value()
|
||||||
// Re-override FileField Value to use data value
|
{
|
||||||
return $this->dataValue();
|
// Re-override FileField Value to use data value
|
||||||
}
|
return $this->dataValue();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param DataObject|DataObjectInterface $record
|
* @param DataObject|DataObjectInterface $record
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function saveInto(DataObjectInterface $record) {
|
public function saveInto(DataObjectInterface $record)
|
||||||
// Check required relation details are available
|
{
|
||||||
$fieldname = $this->getName();
|
// Check required relation details are available
|
||||||
if(!$fieldname) {
|
$fieldname = $this->getName();
|
||||||
return $this;
|
if (!$fieldname) {
|
||||||
}
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
// Get details to save
|
// Get details to save
|
||||||
$idList = $this->getItemIDs();
|
$idList = $this->getItemIDs();
|
||||||
|
|
||||||
// Check type of relation
|
// Check type of relation
|
||||||
$relation = $record->hasMethod($fieldname) ? $record->$fieldname() : null;
|
$relation = $record->hasMethod($fieldname) ? $record->$fieldname() : null;
|
||||||
if($relation && ($relation instanceof RelationList || $relation instanceof UnsavedRelationList)) {
|
if ($relation && ($relation instanceof RelationList || $relation instanceof UnsavedRelationList)) {
|
||||||
// has_many or many_many
|
// has_many or many_many
|
||||||
$relation->setByIDList($idList);
|
$relation->setByIDList($idList);
|
||||||
} elseif($class = DataObject::getSchema()->hasOneComponent(get_class($record), $fieldname)) {
|
} elseif ($class = DataObject::getSchema()->hasOneComponent(get_class($record), $fieldname)) {
|
||||||
// Assign has_one ID
|
// Assign has_one ID
|
||||||
$id = $idList ? reset($idList) : 0;
|
$id = $idList ? reset($idList) : 0;
|
||||||
$record->{"{$fieldname}ID"} = $id;
|
$record->{"{$fieldname}ID"} = $id;
|
||||||
|
|
||||||
// Polymorphic asignment
|
// Polymorphic asignment
|
||||||
if ($class === DataObject::class) {
|
if ($class === DataObject::class) {
|
||||||
$file = $id ? File::get()->byID($id) : null;
|
$file = $id ? File::get()->byID($id) : null;
|
||||||
$fileClass = $file ? get_class($file) : File::class;
|
$fileClass = $file ? get_class($file) : File::class;
|
||||||
$record->{"{$fieldname}Class"} = $id ? $fileClass : null;
|
$record->{"{$fieldname}Class"} = $id ? $fileClass : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the temporary file data into a File object
|
* Loads the temporary file data into a File object
|
||||||
*
|
*
|
||||||
* @param array $tmpFile Temporary file data
|
* @param array $tmpFile Temporary file data
|
||||||
* @param string $error Error message
|
* @param string $error Error message
|
||||||
* @return AssetContainer File object, or null if error
|
* @return AssetContainer File object, or null if error
|
||||||
*/
|
*/
|
||||||
protected function saveTemporaryFile($tmpFile, &$error = null) {
|
protected function saveTemporaryFile($tmpFile, &$error = null)
|
||||||
// Determine container object
|
{
|
||||||
$error = null;
|
// Determine container object
|
||||||
$fileObject = null;
|
$error = null;
|
||||||
|
$fileObject = null;
|
||||||
|
|
||||||
if (empty($tmpFile)) {
|
if (empty($tmpFile)) {
|
||||||
$error = _t('UploadField.FIELDNOTSET', 'File information not found');
|
$error = _t('UploadField.FIELDNOTSET', 'File information not found');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($tmpFile['error']) {
|
if ($tmpFile['error']) {
|
||||||
$error = $tmpFile['error'];
|
$error = $tmpFile['error'];
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search for relations that can hold the uploaded files, but don't fallback
|
// Search for relations that can hold the uploaded files, but don't fallback
|
||||||
// to default if there is no automatic relation
|
// to default if there is no automatic relation
|
||||||
if ($relationClass = $this->getRelationAutosetClass(null)) {
|
if ($relationClass = $this->getRelationAutosetClass(null)) {
|
||||||
// Allow File to be subclassed
|
// Allow File to be subclassed
|
||||||
if($relationClass === File::class && isset($tmpFile['name'])) {
|
if ($relationClass === File::class && isset($tmpFile['name'])) {
|
||||||
$relationClass = File::get_class_for_file_extension(
|
$relationClass = File::get_class_for_file_extension(
|
||||||
File::get_file_extension($tmpFile['name'])
|
File::get_file_extension($tmpFile['name'])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Create new object explicitly. Otherwise rely on Upload::load to choose the class.
|
// Create new object explicitly. Otherwise rely on Upload::load to choose the class.
|
||||||
$fileObject = Object::create($relationClass);
|
$fileObject = Object::create($relationClass);
|
||||||
if(! ($fileObject instanceof DataObject) || !($fileObject instanceof AssetContainer)) {
|
if (! ($fileObject instanceof DataObject) || !($fileObject instanceof AssetContainer)) {
|
||||||
throw new InvalidArgumentException("Invalid asset container $relationClass");
|
throw new InvalidArgumentException("Invalid asset container $relationClass");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the uploaded file into a new file object.
|
// Get the uploaded file into a new file object.
|
||||||
try {
|
try {
|
||||||
$this->getUpload()->loadIntoFile($tmpFile, $fileObject, $this->getFolderName());
|
$this->getUpload()->loadIntoFile($tmpFile, $fileObject, $this->getFolderName());
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
// we shouldn't get an error here, but just in case
|
// we shouldn't get an error here, but just in case
|
||||||
$error = $e->getMessage();
|
$error = $e->getMessage();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if upload field has an error
|
// Check if upload field has an error
|
||||||
if ($this->getUpload()->isError()) {
|
if ($this->getUpload()->isError()) {
|
||||||
$error = implode(' ' . PHP_EOL, $this->getUpload()->getErrors());
|
$error = implode(' ' . PHP_EOL, $this->getUpload()->getErrors());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// return file
|
// return file
|
||||||
return $this->getUpload()->getFile();
|
return $this->getUpload()->getFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the foreign class that needs to be created, or 'File' as default if there
|
* Gets the foreign class that needs to be created, or 'File' as default if there
|
||||||
* is no relationship, or it cannot be determined.
|
* is no relationship, or it cannot be determined.
|
||||||
*
|
*
|
||||||
* @param string $default Default value to return if no value could be calculated
|
* @param string $default Default value to return if no value could be calculated
|
||||||
* @return string Foreign class name.
|
* @return string Foreign class name.
|
||||||
*/
|
*/
|
||||||
public function getRelationAutosetClass($default = File::class) {
|
public function getRelationAutosetClass($default = File::class)
|
||||||
// Don't autodetermine relation if no relationship between parent record
|
{
|
||||||
if(!$this->getRelationAutoSetting()) {
|
// Don't autodetermine relation if no relationship between parent record
|
||||||
return $default;
|
if (!$this->getRelationAutoSetting()) {
|
||||||
}
|
return $default;
|
||||||
|
}
|
||||||
|
|
||||||
// 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);
|
||||||
return empty($class) ? $default : $class;
|
return empty($class) ? $default : $class;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set if relation can be automatically assigned to the underlying dataobject
|
* Set if relation can be automatically assigned to the underlying dataobject
|
||||||
*
|
*
|
||||||
* @param bool $auto
|
* @param bool $auto
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setRelationAutoSetting($auto) {
|
public function setRelationAutoSetting($auto)
|
||||||
$this->relationAutoSetting = $auto;
|
{
|
||||||
return $this;
|
$this->relationAutoSetting = $auto;
|
||||||
}
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if relation can be automatically assigned to the underlying dataobject
|
* Check if relation can be automatically assigned to the underlying dataobject
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function getRelationAutoSetting() {
|
public function getRelationAutoSetting()
|
||||||
return $this->relationAutoSetting;
|
{
|
||||||
}
|
return $this->relationAutoSetting;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an array of post variables, extract all temporary file data into an array
|
* Given an array of post variables, extract all temporary file data into an array
|
||||||
*
|
*
|
||||||
* @param array $postVars Array of posted form data
|
* @param array $postVars Array of posted form data
|
||||||
* @return array List of temporary file data
|
* @return array List of temporary file data
|
||||||
*/
|
*/
|
||||||
protected function extractUploadedFileData($postVars) {
|
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
|
// Note: Format of posted file parameters in php is a feature of using
|
||||||
$tmpFiles = array();
|
// <input name='{$Name}[Uploads][]' /> for multiple file uploads
|
||||||
if( !empty($postVars['tmp_name'])
|
$tmpFiles = array();
|
||||||
&& is_array($postVars['tmp_name'])
|
if (!empty($postVars['tmp_name'])
|
||||||
&& !empty($postVars['tmp_name']['Uploads'])
|
&& is_array($postVars['tmp_name'])
|
||||||
) {
|
&& !empty($postVars['tmp_name']['Uploads'])
|
||||||
for($i = 0; $i < count($postVars['tmp_name']['Uploads']); $i++) {
|
) {
|
||||||
// Skip if "empty" file
|
for ($i = 0; $i < count($postVars['tmp_name']['Uploads']); $i++) {
|
||||||
if(empty($postVars['tmp_name']['Uploads'][$i])) {
|
// Skip if "empty" file
|
||||||
continue;
|
if (empty($postVars['tmp_name']['Uploads'][$i])) {
|
||||||
}
|
continue;
|
||||||
$tmpFile = array();
|
}
|
||||||
foreach(array('name', 'type', 'tmp_name', 'error', 'size') as $field) {
|
$tmpFile = array();
|
||||||
$tmpFile[$field] = $postVars[$field]['Uploads'][$i];
|
foreach (array('name', 'type', 'tmp_name', 'error', 'size') as $field) {
|
||||||
}
|
$tmpFile[$field] = $postVars[$field]['Uploads'][$i];
|
||||||
$tmpFiles[] = $tmpFile;
|
}
|
||||||
}
|
$tmpFiles[] = $tmpFile;
|
||||||
} elseif(!empty($postVars['tmp_name'])) {
|
}
|
||||||
// Fallback to allow single file uploads (method used by AssetUploadField)
|
} elseif (!empty($postVars['tmp_name'])) {
|
||||||
$tmpFiles[] = $postVars;
|
// Fallback to allow single file uploads (method used by AssetUploadField)
|
||||||
}
|
$tmpFiles[] = $postVars;
|
||||||
|
}
|
||||||
|
|
||||||
return $tmpFiles;
|
return $tmpFiles;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,1000 +45,1070 @@ 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 FormField {
|
class UploadField extends FormField
|
||||||
use FileUploadReceiver;
|
{
|
||||||
|
use FileUploadReceiver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private static $allowed_actions = array(
|
private static $allowed_actions = array(
|
||||||
'upload',
|
'upload',
|
||||||
'attach',
|
'attach',
|
||||||
'handleItem',
|
'handleItem',
|
||||||
'handleSelect',
|
'handleSelect',
|
||||||
'fileexists'
|
'fileexists'
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private static $url_handlers = array(
|
private static $url_handlers = array(
|
||||||
'item/$ID' => 'handleItem',
|
'item/$ID' => 'handleItem',
|
||||||
'select' => 'handleSelect',
|
'select' => 'handleSelect',
|
||||||
'$Action!' => '$Action',
|
'$Action!' => '$Action',
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Template to use for the file button widget
|
* Template to use for the file button widget
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $templateFileButtons = null;
|
protected $templateFileButtons = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Template to use for the edit form
|
* Template to use for the edit form
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $templateFileEdit = null;
|
protected $templateFileEdit = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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).
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $ufConfig = array();
|
protected $ufConfig = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Front end config defaults
|
* Front end config defaults
|
||||||
*
|
*
|
||||||
* @config
|
* @config
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private static $defaultConfig = array(
|
private static $defaultConfig = array(
|
||||||
/**
|
/**
|
||||||
* Automatically upload the file once selected
|
* Automatically upload the file once selected
|
||||||
*
|
*
|
||||||
* @var boolean
|
* @var boolean
|
||||||
*/
|
*/
|
||||||
'autoUpload' => true,
|
'autoUpload' => true,
|
||||||
/**
|
/**
|
||||||
* Restriction on number of files that may be set for this field. Set to null to allow
|
* Restriction on number of files that may be set for this field. Set to null to allow
|
||||||
* unlimited. If record has a has_one and allowedMaxFileNumber is null, it will be set to 1.
|
* unlimited. If record has a has_one and allowedMaxFileNumber is null, it will be set to 1.
|
||||||
* The resulting value will be set to maxNumberOfFiles
|
* The resulting value will be set to maxNumberOfFiles
|
||||||
*
|
*
|
||||||
* @var integer
|
* @var integer
|
||||||
*/
|
*/
|
||||||
'allowedMaxFileNumber' => null,
|
'allowedMaxFileNumber' => null,
|
||||||
/**
|
/**
|
||||||
* Can the user upload new files, or just select from existing files.
|
* Can the user upload new files, or just select from existing files.
|
||||||
* String values are interpreted as permission codes.
|
* String values are interpreted as permission codes.
|
||||||
*
|
*
|
||||||
* @var boolean|string
|
* @var boolean|string
|
||||||
*/
|
*/
|
||||||
'canUpload' => true,
|
'canUpload' => true,
|
||||||
/**
|
/**
|
||||||
* Can the user attach files from the assets archive on the site?
|
* Can the user attach files from the assets archive on the site?
|
||||||
* String values are interpreted as permission codes.
|
* String values are interpreted as permission codes.
|
||||||
*
|
*
|
||||||
* @var boolean|string
|
* @var boolean|string
|
||||||
*/
|
*/
|
||||||
'canAttachExisting' => "CMS_ACCESS_AssetAdmin",
|
'canAttachExisting' => "CMS_ACCESS_AssetAdmin",
|
||||||
/**
|
/**
|
||||||
* Shows the target folder for new uploads in the field UI.
|
* Shows the target folder for new uploads in the field UI.
|
||||||
* Disable to keep the internal filesystem structure hidden from users.
|
* Disable to keep the internal filesystem structure hidden from users.
|
||||||
*
|
*
|
||||||
* @var boolean|string
|
* @var boolean|string
|
||||||
*/
|
*/
|
||||||
'canPreviewFolder' => true,
|
'canPreviewFolder' => true,
|
||||||
/**
|
/**
|
||||||
* Indicate a change event to the containing form if an upload
|
* Indicate a change event to the containing form if an upload
|
||||||
* or file edit/delete was performed.
|
* or file edit/delete was performed.
|
||||||
*
|
*
|
||||||
* @var boolean
|
* @var boolean
|
||||||
*/
|
*/
|
||||||
'changeDetection' => true,
|
'changeDetection' => true,
|
||||||
/**
|
/**
|
||||||
* Maximum width of the preview thumbnail
|
* Maximum width of the preview thumbnail
|
||||||
*
|
*
|
||||||
* @var integer
|
* @var integer
|
||||||
*/
|
*/
|
||||||
'previewMaxWidth' => 80,
|
'previewMaxWidth' => 80,
|
||||||
/**
|
/**
|
||||||
* Maximum height of the preview thumbnail
|
* Maximum height of the preview thumbnail
|
||||||
*
|
*
|
||||||
* @var integer
|
* @var integer
|
||||||
*/
|
*/
|
||||||
'previewMaxHeight' => 60,
|
'previewMaxHeight' => 60,
|
||||||
/**
|
/**
|
||||||
* javascript template used to display uploading files
|
* javascript template used to display uploading files
|
||||||
*
|
*
|
||||||
* @see javascript/UploadField_uploadtemplate.js
|
* @see javascript/UploadField_uploadtemplate.js
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
'uploadTemplateName' => 'ss-uploadfield-uploadtemplate',
|
'uploadTemplateName' => 'ss-uploadfield-uploadtemplate',
|
||||||
/**
|
/**
|
||||||
* javascript template used to display already uploaded files
|
* javascript template used to display already uploaded files
|
||||||
*
|
*
|
||||||
* @see javascript/UploadField_downloadtemplate.js
|
* @see javascript/UploadField_downloadtemplate.js
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
'downloadTemplateName' => 'ss-uploadfield-downloadtemplate',
|
'downloadTemplateName' => 'ss-uploadfield-downloadtemplate',
|
||||||
/**
|
/**
|
||||||
* Show a warning when overwriting a file.
|
* Show a warning when overwriting a file.
|
||||||
* This requires Upload->replaceFile config to be set to true, otherwise
|
* This requires Upload->replaceFile config to be set to true, otherwise
|
||||||
* files will be renamed instead of overwritten
|
* files will be renamed instead of overwritten
|
||||||
*
|
*
|
||||||
* @see Upload
|
* @see Upload
|
||||||
* @var boolean
|
* @var boolean
|
||||||
*/
|
*/
|
||||||
'overwriteWarning' => true
|
'overwriteWarning' => true
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var String Folder to display in "Select files" list.
|
* @var String Folder to display in "Select files" list.
|
||||||
* Defaults to listing all files regardless of folder.
|
* Defaults to listing all files regardless of folder.
|
||||||
* The folder path should be relative to the webroot.
|
* The folder path should be relative to the webroot.
|
||||||
* See {@link FileField->folderName} to set the upload target instead.
|
* See {@link FileField->folderName} to set the upload target instead.
|
||||||
* @example admin/folder/subfolder
|
* @example admin/folder/subfolder
|
||||||
*/
|
*/
|
||||||
protected $displayFolderName;
|
protected $displayFolderName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FieldList $fields or string $name (of a method on File to provide a fields) for the EditForm
|
* FieldList $fields or string $name (of a method on File to provide a fields) for the EditForm
|
||||||
* @example 'getCMSFields'
|
* @example 'getCMSFields'
|
||||||
*
|
*
|
||||||
* @var FieldList|string
|
* @var FieldList|string
|
||||||
*/
|
*/
|
||||||
protected $fileEditFields = null;
|
protected $fileEditFields = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FieldList $actions or string $name (of a method on File to provide a actions) for the EditForm
|
* FieldList $actions or string $name (of a method on File to provide a actions) for the EditForm
|
||||||
* @example 'getCMSActions'
|
* @example 'getCMSActions'
|
||||||
*
|
*
|
||||||
* @var FieldList|string
|
* @var FieldList|string
|
||||||
*/
|
*/
|
||||||
protected $fileEditActions = null;
|
protected $fileEditActions = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validator (eg RequiredFields) or string $name (of a method on File to provide a Validator) for the EditForm
|
* Validator (eg RequiredFields) or string $name (of a method on File to provide a Validator) for the EditForm
|
||||||
* @example 'getCMSValidator'
|
* @example 'getCMSValidator'
|
||||||
*
|
*
|
||||||
* @var RequiredFields|string
|
* @var RequiredFields|string
|
||||||
*/
|
*/
|
||||||
protected $fileEditValidator = null;
|
protected $fileEditValidator = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new UploadField instance
|
* Construct a new UploadField instance
|
||||||
*
|
*
|
||||||
* @param string $name The internal field name, passed to forms.
|
* @param string $name The internal field name, passed to forms.
|
||||||
* @param string $title The field label.
|
* @param string $title The field label.
|
||||||
* @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
|
{
|
||||||
$this->addExtraClass('ss-upload'); // class, used by js
|
// TODO thats the first thing that came to my head, feel free to change it
|
||||||
$this->addExtraClass('ss-uploadfield'); // class, used by css for uploadfield only
|
$this->addExtraClass('ss-upload'); // class, used by js
|
||||||
|
$this->addExtraClass('ss-uploadfield'); // class, used by css for uploadfield only
|
||||||
|
|
||||||
$this->ufConfig = self::config()->defaultConfig;
|
$this->ufConfig = self::config()->defaultConfig;
|
||||||
$this->constructFileUploadReceiver();
|
$this->constructFileUploadReceiver();
|
||||||
|
|
||||||
parent::__construct($name, $title);
|
parent::__construct($name, $title);
|
||||||
|
|
||||||
if ($items) {
|
if ($items) {
|
||||||
$this->setItems($items);
|
$this->setItems($items);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set name of template used for Buttons on each file (replace, edit, remove, delete) (without path or extension)
|
* Set name of template used for Buttons on each file (replace, edit, remove, delete) (without path or extension)
|
||||||
*
|
*
|
||||||
* @param string $template
|
* @param string $template
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setTemplateFileButtons($template) {
|
public function setTemplateFileButtons($template)
|
||||||
$this->templateFileButtons = $template;
|
{
|
||||||
return $this;
|
$this->templateFileButtons = $template;
|
||||||
}
|
return $this;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* @return string
|
/**
|
||||||
*/
|
* @return string
|
||||||
public function getTemplateFileButtons() {
|
*/
|
||||||
return $this->_templates($this->templateFileButtons, '_FileButtons');
|
public function getTemplateFileButtons()
|
||||||
}
|
{
|
||||||
|
return $this->_templates($this->templateFileButtons, '_FileButtons');
|
||||||
/**
|
}
|
||||||
* Set name of template used for the edit (inline & popup) of a file file (without path or extension)
|
|
||||||
*
|
/**
|
||||||
* @param string $template
|
* Set name of template used for the edit (inline & popup) of a file file (without path or extension)
|
||||||
* @return $this
|
*
|
||||||
*/
|
* @param string $template
|
||||||
public function setTemplateFileEdit($template) {
|
* @return $this
|
||||||
$this->templateFileEdit = $template;
|
*/
|
||||||
return $this;
|
public function setTemplateFileEdit($template)
|
||||||
}
|
{
|
||||||
|
$this->templateFileEdit = $template;
|
||||||
/**
|
return $this;
|
||||||
* @return string
|
}
|
||||||
*/
|
|
||||||
public function getTemplateFileEdit() {
|
/**
|
||||||
return $this->_templates($this->templateFileEdit, '_FileEdit');
|
* @return string
|
||||||
}
|
*/
|
||||||
|
public function getTemplateFileEdit()
|
||||||
/**
|
{
|
||||||
* Determine if the target folder for new uploads in is visible the field UI.
|
return $this->_templates($this->templateFileEdit, '_FileEdit');
|
||||||
*
|
}
|
||||||
* @return boolean
|
|
||||||
*/
|
/**
|
||||||
public function canPreviewFolder() {
|
* Determine if the target folder for new uploads in is visible the field UI.
|
||||||
if(!$this->isActive()) return false;
|
*
|
||||||
$can = $this->getConfig('canPreviewFolder');
|
* @return boolean
|
||||||
return (is_bool($can)) ? $can : Permission::check($can);
|
*/
|
||||||
}
|
public function canPreviewFolder()
|
||||||
|
{
|
||||||
/**
|
if (!$this->isActive()) {
|
||||||
* Determine if the target folder for new uploads in is visible the field UI.
|
return false;
|
||||||
* Disable to keep the internal filesystem structure hidden from users.
|
}
|
||||||
*
|
$can = $this->getConfig('canPreviewFolder');
|
||||||
* @param boolean|string $canPreviewFolder Either a boolean flag, or a
|
return (is_bool($can)) ? $can : Permission::check($can);
|
||||||
* required permission code
|
}
|
||||||
* @return UploadField Self reference
|
|
||||||
*/
|
/**
|
||||||
public function setCanPreviewFolder($canPreviewFolder) {
|
* Determine if the target folder for new uploads in is visible the field UI.
|
||||||
return $this->setConfig('canPreviewFolder', $canPreviewFolder);
|
* Disable to keep the internal filesystem structure hidden from users.
|
||||||
}
|
*
|
||||||
|
* @param boolean|string $canPreviewFolder Either a boolean flag, or a
|
||||||
/**
|
* required permission code
|
||||||
* Determine if the field should show a warning when overwriting a file.
|
* @return UploadField Self reference
|
||||||
* This requires Upload->replaceFile config to be set to true, otherwise
|
*/
|
||||||
* files will be renamed instead of overwritten (although the warning will
|
public function setCanPreviewFolder($canPreviewFolder)
|
||||||
* still be displayed)
|
{
|
||||||
*
|
return $this->setConfig('canPreviewFolder', $canPreviewFolder);
|
||||||
* @return boolean
|
}
|
||||||
*/
|
|
||||||
public function getOverwriteWarning() {
|
/**
|
||||||
return $this->getConfig('overwriteWarning');
|
* Determine if the field should show a warning when overwriting a file.
|
||||||
}
|
* This requires Upload->replaceFile config to be set to true, otherwise
|
||||||
|
* files will be renamed instead of overwritten (although the warning will
|
||||||
/**
|
* still be displayed)
|
||||||
* Determine if the field should show a warning when overwriting a file.
|
*
|
||||||
* This requires Upload->replaceFile config to be set to true, otherwise
|
* @return boolean
|
||||||
* files will be renamed instead of overwritten (although the warning will
|
*/
|
||||||
* still be displayed)
|
public function getOverwriteWarning()
|
||||||
*
|
{
|
||||||
* @param boolean $overwriteWarning
|
return $this->getConfig('overwriteWarning');
|
||||||
* @return UploadField Self reference
|
}
|
||||||
*/
|
|
||||||
public function setOverwriteWarning($overwriteWarning) {
|
/**
|
||||||
return $this->setConfig('overwriteWarning', $overwriteWarning);
|
* Determine if the field should show a warning when overwriting a file.
|
||||||
}
|
* This requires Upload->replaceFile config to be set to true, otherwise
|
||||||
|
* files will be renamed instead of overwritten (although the warning will
|
||||||
/**
|
* still be displayed)
|
||||||
* @param string $name
|
*
|
||||||
* @return $this
|
* @param boolean $overwriteWarning
|
||||||
*/
|
* @return UploadField Self reference
|
||||||
public function setDisplayFolderName($name) {
|
*/
|
||||||
$this->displayFolderName = $name;
|
public function setOverwriteWarning($overwriteWarning)
|
||||||
return $this;
|
{
|
||||||
}
|
return $this->setConfig('overwriteWarning', $overwriteWarning);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* @return String
|
/**
|
||||||
*/
|
* @param string $name
|
||||||
public function getDisplayFolderName() {
|
* @return $this
|
||||||
return $this->displayFolderName;
|
*/
|
||||||
}
|
public function setDisplayFolderName($name)
|
||||||
|
{
|
||||||
|
$this->displayFolderName = $name;
|
||||||
|
return $this;
|
||||||
/**
|
}
|
||||||
* Retrieves a customised list of all File records to ensure they are
|
|
||||||
* properly viewable when rendered in the field template.
|
/**
|
||||||
*
|
* @return String
|
||||||
* @return SS_List[ViewableData_Customised]
|
*/
|
||||||
*/
|
public function getDisplayFolderName()
|
||||||
public function getCustomisedItems() {
|
{
|
||||||
$customised = new ArrayList();
|
return $this->displayFolderName;
|
||||||
foreach($this->getItems() as $file) {
|
}
|
||||||
$customised->push($this->customiseFile($file));
|
|
||||||
}
|
|
||||||
return $customised;
|
|
||||||
}
|
/**
|
||||||
|
* Retrieves a customised list of all File records to ensure they are
|
||||||
/**
|
* properly viewable when rendered in the field template.
|
||||||
* Customises a file with additional details suitable for rendering in the
|
*
|
||||||
* UploadField.ss template
|
* @return SS_List[ViewableData_Customised]
|
||||||
*
|
*/
|
||||||
* @param ViewableData|AssetContainer $file
|
public function getCustomisedItems()
|
||||||
* @return ViewableData_Customised
|
{
|
||||||
*/
|
$customised = new ArrayList();
|
||||||
protected function customiseFile(AssetContainer $file) {
|
foreach ($this->getItems() as $file) {
|
||||||
$file = $file->customise(array(
|
$customised->push($this->customiseFile($file));
|
||||||
'UploadFieldThumbnailURL' => $this->getThumbnailURLForFile($file),
|
}
|
||||||
'UploadFieldDeleteLink' => $this->getItemHandler($file->ID)->DeleteLink(),
|
return $customised;
|
||||||
'UploadFieldEditLink' => $this->getItemHandler($file->ID)->EditLink(),
|
}
|
||||||
'UploadField' => $this
|
|
||||||
));
|
/**
|
||||||
// we do this in a second customise to have the access to the previous customisations
|
* Customises a file with additional details suitable for rendering in the
|
||||||
return $file->customise(array(
|
* UploadField.ss template
|
||||||
'UploadFieldFileButtons' => $file->renderWith($this->getTemplateFileButtons())
|
*
|
||||||
));
|
* @param ViewableData|AssetContainer $file
|
||||||
}
|
* @return ViewableData_Customised
|
||||||
|
*/
|
||||||
/**
|
protected function customiseFile(AssetContainer $file)
|
||||||
* Assign a front-end config variable for the upload field
|
{
|
||||||
*
|
$file = $file->customise(array(
|
||||||
* @see https://github.com/blueimp/jQuery-File-Upload/wiki/Options for the list of front end options available
|
'UploadFieldThumbnailURL' => $this->getThumbnailURLForFile($file),
|
||||||
*
|
'UploadFieldDeleteLink' => $this->getItemHandler($file->ID)->DeleteLink(),
|
||||||
* @param string $key
|
'UploadFieldEditLink' => $this->getItemHandler($file->ID)->EditLink(),
|
||||||
* @param mixed $val
|
'UploadField' => $this
|
||||||
* @return UploadField self reference
|
));
|
||||||
*/
|
// we do this in a second customise to have the access to the previous customisations
|
||||||
public function setConfig($key, $val) {
|
return $file->customise(array(
|
||||||
$this->ufConfig[$key] = $val;
|
'UploadFieldFileButtons' => $file->renderWith($this->getTemplateFileButtons())
|
||||||
return $this;
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a front-end config variable for the upload field
|
* Assign a front-end config variable for the upload field
|
||||||
*
|
*
|
||||||
* @see https://github.com/blueimp/jQuery-File-Upload/wiki/Options for the list of front end options available
|
* @see https://github.com/blueimp/jQuery-File-Upload/wiki/Options for the list of front end options available
|
||||||
*
|
*
|
||||||
* @param string $key
|
* @param string $key
|
||||||
* @return mixed
|
* @param mixed $val
|
||||||
*/
|
* @return UploadField self reference
|
||||||
public function getConfig($key) {
|
*/
|
||||||
if(!isset($this->ufConfig[$key])) return null;
|
public function setConfig($key, $val)
|
||||||
return $this->ufConfig[$key];
|
{
|
||||||
}
|
$this->ufConfig[$key] = $val;
|
||||||
|
return $this;
|
||||||
/**
|
}
|
||||||
* Determine if the field should automatically upload the file.
|
|
||||||
*
|
/**
|
||||||
* @return boolean
|
* Gets a front-end config variable for the upload field
|
||||||
*/
|
*
|
||||||
public function getAutoUpload() {
|
* @see https://github.com/blueimp/jQuery-File-Upload/wiki/Options for the list of front end options available
|
||||||
return $this->getConfig('autoUpload');
|
*
|
||||||
}
|
* @param string $key
|
||||||
|
* @return mixed
|
||||||
/**
|
*/
|
||||||
* Determine if the field should automatically upload the file
|
public function getConfig($key)
|
||||||
*
|
{
|
||||||
* @param boolean $autoUpload
|
if (!isset($this->ufConfig[$key])) {
|
||||||
* @return UploadField Self reference
|
return null;
|
||||||
*/
|
}
|
||||||
public function setAutoUpload($autoUpload) {
|
return $this->ufConfig[$key];
|
||||||
return $this->setConfig('autoUpload', $autoUpload);
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Determine if the field should automatically upload the file.
|
||||||
* Determine maximum number of files allowed to be attached
|
*
|
||||||
* Defaults to 1 for has_one and null (unlimited) for
|
* @return boolean
|
||||||
* many_many and has_many relations.
|
*/
|
||||||
*
|
public function getAutoUpload()
|
||||||
* @return integer|null Maximum limit, or null for no limit
|
{
|
||||||
*/
|
return $this->getConfig('autoUpload');
|
||||||
public function getAllowedMaxFileNumber() {
|
}
|
||||||
$allowedMaxFileNumber = $this->getConfig('allowedMaxFileNumber');
|
|
||||||
|
/**
|
||||||
// if there is a has_one relation with that name on the record and
|
* Determine if the field should automatically upload the file
|
||||||
// allowedMaxFileNumber has not been set, it's wanted to be 1
|
*
|
||||||
if(empty($allowedMaxFileNumber)) {
|
* @param boolean $autoUpload
|
||||||
$record = $this->getRecord();
|
* @return UploadField Self reference
|
||||||
$name = $this->getName();
|
*/
|
||||||
if($record && DataObject::getSchema()->hasOneComponent(get_class($record), $name)) {
|
public function setAutoUpload($autoUpload)
|
||||||
return 1; // Default for has_one
|
{
|
||||||
} else {
|
return $this->setConfig('autoUpload', $autoUpload);
|
||||||
return null; // Default for has_many and many_many
|
}
|
||||||
}
|
|
||||||
} else {
|
/**
|
||||||
return $allowedMaxFileNumber;
|
* Determine maximum number of files allowed to be attached
|
||||||
}
|
* Defaults to 1 for has_one and null (unlimited) for
|
||||||
}
|
* many_many and has_many relations.
|
||||||
|
*
|
||||||
/**
|
* @return integer|null Maximum limit, or null for no limit
|
||||||
* Determine maximum number of files allowed to be attached.
|
*/
|
||||||
*
|
public function getAllowedMaxFileNumber()
|
||||||
* @param integer|null $allowedMaxFileNumber Maximum limit. 0 or null will be treated as unlimited
|
{
|
||||||
* @return UploadField Self reference
|
$allowedMaxFileNumber = $this->getConfig('allowedMaxFileNumber');
|
||||||
*/
|
|
||||||
public function setAllowedMaxFileNumber($allowedMaxFileNumber) {
|
// if there is a has_one relation with that name on the record and
|
||||||
return $this->setConfig('allowedMaxFileNumber', $allowedMaxFileNumber);
|
// allowedMaxFileNumber has not been set, it's wanted to be 1
|
||||||
}
|
if (empty($allowedMaxFileNumber)) {
|
||||||
|
$record = $this->getRecord();
|
||||||
/**
|
$name = $this->getName();
|
||||||
* Determine if the user has permission to upload.
|
if ($record && DataObject::getSchema()->hasOneComponent(get_class($record), $name)) {
|
||||||
*
|
return 1; // Default for has_one
|
||||||
* @return boolean
|
} else {
|
||||||
*/
|
return null; // Default for has_many and many_many
|
||||||
public function canUpload() {
|
}
|
||||||
if(!$this->isActive()) return false;
|
} else {
|
||||||
$can = $this->getConfig('canUpload');
|
return $allowedMaxFileNumber;
|
||||||
return (is_bool($can)) ? $can : Permission::check($can);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify whether the user can upload files.
|
* Determine maximum number of files allowed to be attached.
|
||||||
* String values will be treated as required permission codes
|
*
|
||||||
*
|
* @param integer|null $allowedMaxFileNumber Maximum limit. 0 or null will be treated as unlimited
|
||||||
* @param boolean|string $canUpload Either a boolean flag, or a required
|
* @return UploadField Self reference
|
||||||
* permission code
|
*/
|
||||||
* @return UploadField Self reference
|
public function setAllowedMaxFileNumber($allowedMaxFileNumber)
|
||||||
*/
|
{
|
||||||
public function setCanUpload($canUpload) {
|
return $this->setConfig('allowedMaxFileNumber', $allowedMaxFileNumber);
|
||||||
return $this->setConfig('canUpload', $canUpload);
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Determine if the user has permission to upload.
|
||||||
* Determine if the user has permission to attach existing files
|
*
|
||||||
* By default returns true if the user has the CMS_ACCESS_AssetAdmin permission
|
* @return boolean
|
||||||
*
|
*/
|
||||||
* @return boolean
|
public function canUpload()
|
||||||
*/
|
{
|
||||||
public function canAttachExisting() {
|
if (!$this->isActive()) {
|
||||||
if(!$this->isActive()) return false;
|
return false;
|
||||||
$can = $this->getConfig('canAttachExisting');
|
}
|
||||||
return (is_bool($can)) ? $can : Permission::check($can);
|
$can = $this->getConfig('canUpload');
|
||||||
}
|
return (is_bool($can)) ? $can : Permission::check($can);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Returns true if the field is neither readonly nor disabled
|
/**
|
||||||
*
|
* Specify whether the user can upload files.
|
||||||
* @return boolean
|
* String values will be treated as required permission codes
|
||||||
*/
|
*
|
||||||
public function isActive() {
|
* @param boolean|string $canUpload Either a boolean flag, or a required
|
||||||
return !$this->isDisabled() && !$this->isReadonly();
|
* permission code
|
||||||
}
|
* @return UploadField Self reference
|
||||||
|
*/
|
||||||
/**
|
public function setCanUpload($canUpload)
|
||||||
* Specify whether the user can attach existing files
|
{
|
||||||
* String values will be treated as required permission codes
|
return $this->setConfig('canUpload', $canUpload);
|
||||||
*
|
}
|
||||||
* @param boolean|string $canAttachExisting Either a boolean flag, or a
|
|
||||||
* required permission code
|
/**
|
||||||
* @return UploadField Self reference
|
* Determine if the user has permission to attach existing files
|
||||||
*/
|
* By default returns true if the user has the CMS_ACCESS_AssetAdmin permission
|
||||||
public function setCanAttachExisting($canAttachExisting) {
|
*
|
||||||
return $this->setConfig('canAttachExisting', $canAttachExisting);
|
* @return boolean
|
||||||
}
|
*/
|
||||||
|
public function canAttachExisting()
|
||||||
/**
|
{
|
||||||
* Gets thumbnail width. Defaults to 80
|
if (!$this->isActive()) {
|
||||||
*
|
return false;
|
||||||
* @return integer
|
}
|
||||||
*/
|
$can = $this->getConfig('canAttachExisting');
|
||||||
public function getPreviewMaxWidth() {
|
return (is_bool($can)) ? $can : Permission::check($can);
|
||||||
return $this->getConfig('previewMaxWidth');
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Returns true if the field is neither readonly nor disabled
|
||||||
* @see UploadField::getPreviewMaxWidth()
|
*
|
||||||
*
|
* @return boolean
|
||||||
* @param integer $previewMaxWidth
|
*/
|
||||||
* @return UploadField Self reference
|
public function isActive()
|
||||||
*/
|
{
|
||||||
public function setPreviewMaxWidth($previewMaxWidth) {
|
return !$this->isDisabled() && !$this->isReadonly();
|
||||||
return $this->setConfig('previewMaxWidth', $previewMaxWidth);
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Specify whether the user can attach existing files
|
||||||
* Gets thumbnail height. Defaults to 60
|
* String values will be treated as required permission codes
|
||||||
*
|
*
|
||||||
* @return integer
|
* @param boolean|string $canAttachExisting Either a boolean flag, or a
|
||||||
*/
|
* required permission code
|
||||||
public function getPreviewMaxHeight() {
|
* @return UploadField Self reference
|
||||||
return $this->getConfig('previewMaxHeight');
|
*/
|
||||||
}
|
public function setCanAttachExisting($canAttachExisting)
|
||||||
|
{
|
||||||
/**
|
return $this->setConfig('canAttachExisting', $canAttachExisting);
|
||||||
* @see UploadField::getPreviewMaxHeight()
|
}
|
||||||
*
|
|
||||||
* @param integer $previewMaxHeight
|
/**
|
||||||
* @return UploadField Self reference
|
* Gets thumbnail width. Defaults to 80
|
||||||
*/
|
*
|
||||||
public function setPreviewMaxHeight($previewMaxHeight) {
|
* @return integer
|
||||||
return $this->setConfig('previewMaxHeight', $previewMaxHeight);
|
*/
|
||||||
}
|
public function getPreviewMaxWidth()
|
||||||
|
{
|
||||||
/**
|
return $this->getConfig('previewMaxWidth');
|
||||||
* javascript template used to display uploading files
|
}
|
||||||
* Defaults to 'ss-uploadfield-uploadtemplate'
|
|
||||||
*
|
/**
|
||||||
* @see javascript/UploadField_uploadtemplate.js
|
* @see UploadField::getPreviewMaxWidth()
|
||||||
* @return string
|
*
|
||||||
*/
|
* @param integer $previewMaxWidth
|
||||||
public function getUploadTemplateName() {
|
* @return UploadField Self reference
|
||||||
return $this->getConfig('uploadTemplateName');
|
*/
|
||||||
}
|
public function setPreviewMaxWidth($previewMaxWidth)
|
||||||
|
{
|
||||||
/**
|
return $this->setConfig('previewMaxWidth', $previewMaxWidth);
|
||||||
* @see UploadField::getUploadTemplateName()
|
}
|
||||||
*
|
|
||||||
* @param string $uploadTemplateName
|
/**
|
||||||
* @return UploadField Self reference
|
* Gets thumbnail height. Defaults to 60
|
||||||
*/
|
*
|
||||||
public function setUploadTemplateName($uploadTemplateName) {
|
* @return integer
|
||||||
return $this->setConfig('uploadTemplateName', $uploadTemplateName);
|
*/
|
||||||
}
|
public function getPreviewMaxHeight()
|
||||||
|
{
|
||||||
/**
|
return $this->getConfig('previewMaxHeight');
|
||||||
* javascript template used to display already uploaded files
|
}
|
||||||
* Defaults to 'ss-downloadfield-downloadtemplate'
|
|
||||||
*
|
/**
|
||||||
* @see javascript/DownloadField_downloadtemplate.js
|
* @see UploadField::getPreviewMaxHeight()
|
||||||
* @return string
|
*
|
||||||
*/
|
* @param integer $previewMaxHeight
|
||||||
public function getDownloadTemplateName() {
|
* @return UploadField Self reference
|
||||||
return $this->getConfig('downloadTemplateName');
|
*/
|
||||||
}
|
public function setPreviewMaxHeight($previewMaxHeight)
|
||||||
|
{
|
||||||
/**
|
return $this->setConfig('previewMaxHeight', $previewMaxHeight);
|
||||||
* @see Uploadfield::getDownloadTemplateName()
|
}
|
||||||
*
|
|
||||||
* @param string $downloadTemplateName
|
/**
|
||||||
* @return Uploadfield Self reference
|
* javascript template used to display uploading files
|
||||||
*/
|
* Defaults to 'ss-uploadfield-uploadtemplate'
|
||||||
public function setDownloadTemplateName($downloadTemplateName) {
|
*
|
||||||
return $this->setConfig('downloadTemplateName', $downloadTemplateName);
|
* @see javascript/UploadField_uploadtemplate.js
|
||||||
}
|
* @return string
|
||||||
|
*/
|
||||||
/**
|
public function getUploadTemplateName()
|
||||||
* FieldList $fields for the EditForm
|
{
|
||||||
* @example 'getCMSFields'
|
return $this->getConfig('uploadTemplateName');
|
||||||
*
|
}
|
||||||
* @param DataObject $file File context to generate fields for
|
|
||||||
* @return FieldList List of form fields
|
/**
|
||||||
*/
|
* @see UploadField::getUploadTemplateName()
|
||||||
public function getFileEditFields(DataObject $file) {
|
*
|
||||||
// Empty actions, generate default
|
* @param string $uploadTemplateName
|
||||||
if(empty($this->fileEditFields)) {
|
* @return UploadField Self reference
|
||||||
$fields = $file->getCMSFields();
|
*/
|
||||||
// Only display main tab, to avoid overly complex interface
|
public function setUploadTemplateName($uploadTemplateName)
|
||||||
if($fields->hasTabSet() && ($mainTab = $fields->findOrMakeTab('Root.Main'))) {
|
{
|
||||||
$fields = $mainTab->Fields();
|
return $this->setConfig('uploadTemplateName', $uploadTemplateName);
|
||||||
}
|
}
|
||||||
return $fields;
|
|
||||||
}
|
/**
|
||||||
|
* javascript template used to display already uploaded files
|
||||||
// Fields instance
|
* Defaults to 'ss-downloadfield-downloadtemplate'
|
||||||
if ($this->fileEditFields instanceof FieldList) {
|
*
|
||||||
return $this->fileEditFields;
|
* @see javascript/DownloadField_downloadtemplate.js
|
||||||
}
|
* @return string
|
||||||
|
*/
|
||||||
// Method to call on the given file
|
public function getDownloadTemplateName()
|
||||||
if($file->hasMethod($this->fileEditFields)) {
|
{
|
||||||
return $file->{$this->fileEditFields}();
|
return $this->getConfig('downloadTemplateName');
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidArgumentException("Invalid value for UploadField::fileEditFields");
|
/**
|
||||||
}
|
* @see Uploadfield::getDownloadTemplateName()
|
||||||
|
*
|
||||||
/**
|
* @param string $downloadTemplateName
|
||||||
* FieldList $fields or string $name (of a method on File to provide a fields) for the EditForm
|
* @return Uploadfield Self reference
|
||||||
* @example 'getCMSFields'
|
*/
|
||||||
*
|
public function setDownloadTemplateName($downloadTemplateName)
|
||||||
* @param FieldList|string
|
{
|
||||||
* @return Uploadfield Self reference
|
return $this->setConfig('downloadTemplateName', $downloadTemplateName);
|
||||||
*/
|
}
|
||||||
public function setFileEditFields($fileEditFields) {
|
|
||||||
$this->fileEditFields = $fileEditFields;
|
/**
|
||||||
return $this;
|
* FieldList $fields for the EditForm
|
||||||
}
|
* @example 'getCMSFields'
|
||||||
|
*
|
||||||
/**
|
* @param DataObject $file File context to generate fields for
|
||||||
* FieldList $actions or string $name (of a method on File to provide a actions) for the EditForm
|
* @return FieldList List of form fields
|
||||||
* @example 'getCMSActions'
|
*/
|
||||||
*
|
public function getFileEditFields(DataObject $file)
|
||||||
* @param DataObject $file File context to generate form actions for
|
{
|
||||||
* @return FieldList Field list containing FormAction
|
// Empty actions, generate default
|
||||||
*/
|
if (empty($this->fileEditFields)) {
|
||||||
public function getFileEditActions(DataObject $file) {
|
$fields = $file->getCMSFields();
|
||||||
// Empty actions, generate default
|
// Only display main tab, to avoid overly complex interface
|
||||||
if(empty($this->fileEditActions)) {
|
if ($fields->hasTabSet() && ($mainTab = $fields->findOrMakeTab('Root.Main'))) {
|
||||||
$actions = new FieldList($saveAction = new FormAction('doEdit', _t('UploadField.DOEDIT', 'Save')));
|
$fields = $mainTab->Fields();
|
||||||
$saveAction->addExtraClass('ss-ui-action-constructive icon-accept');
|
}
|
||||||
return $actions;
|
return $fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actions instance
|
// Fields instance
|
||||||
if ($this->fileEditActions instanceof FieldList) {
|
if ($this->fileEditFields instanceof FieldList) {
|
||||||
return $this->fileEditActions;
|
return $this->fileEditFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method to call on the given file
|
// Method to call on the given file
|
||||||
if($file->hasMethod($this->fileEditActions)) {
|
if ($file->hasMethod($this->fileEditFields)) {
|
||||||
return $file->{$this->fileEditActions}();
|
return $file->{$this->fileEditFields}();
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidArgumentException("Invalid value for UploadField::fileEditActions");
|
throw new InvalidArgumentException("Invalid value for UploadField::fileEditFields");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FieldList $actions or string $name (of a method on File to provide a actions) for the EditForm
|
* FieldList $fields or string $name (of a method on File to provide a fields) for the EditForm
|
||||||
* @example 'getCMSActions'
|
* @example 'getCMSFields'
|
||||||
*
|
*
|
||||||
* @param FieldList|string
|
* @param FieldList|string
|
||||||
* @return Uploadfield Self reference
|
* @return Uploadfield Self reference
|
||||||
*/
|
*/
|
||||||
public function setFileEditActions($fileEditActions) {
|
public function setFileEditFields($fileEditFields)
|
||||||
$this->fileEditActions = $fileEditActions;
|
{
|
||||||
return $this;
|
$this->fileEditFields = $fileEditFields;
|
||||||
}
|
return $this;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Determines the validator to use for the edit form
|
/**
|
||||||
* @example 'getCMSValidator'
|
* FieldList $actions or string $name (of a method on File to provide a actions) for the EditForm
|
||||||
*
|
* @example 'getCMSActions'
|
||||||
* @param DataObject $file File context to generate validator from
|
*
|
||||||
* @return Validator Validator object
|
* @param DataObject $file File context to generate form actions for
|
||||||
*/
|
* @return FieldList Field list containing FormAction
|
||||||
public function getFileEditValidator(DataObject $file) {
|
*/
|
||||||
// Empty validator
|
public function getFileEditActions(DataObject $file)
|
||||||
if(empty($this->fileEditValidator)) {
|
{
|
||||||
return null;
|
// Empty actions, generate default
|
||||||
}
|
if (empty($this->fileEditActions)) {
|
||||||
|
$actions = new FieldList($saveAction = new FormAction('doEdit', _t('UploadField.DOEDIT', 'Save')));
|
||||||
// Validator instance
|
$saveAction->addExtraClass('ss-ui-action-constructive icon-accept');
|
||||||
if($this->fileEditValidator instanceof Validator) {
|
return $actions;
|
||||||
return $this->fileEditValidator;
|
}
|
||||||
}
|
|
||||||
|
// Actions instance
|
||||||
// Method to call on the given file
|
if ($this->fileEditActions instanceof FieldList) {
|
||||||
if($file->hasMethod($this->fileEditValidator)) {
|
return $this->fileEditActions;
|
||||||
return $file->{$this->fileEditValidator}();
|
}
|
||||||
}
|
|
||||||
|
// Method to call on the given file
|
||||||
throw new InvalidArgumentException("Invalid value for UploadField::fileEditValidator");
|
if ($file->hasMethod($this->fileEditActions)) {
|
||||||
}
|
return $file->{$this->fileEditActions}();
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Validator (eg RequiredFields) or string $name (of a method on File to provide a Validator) for the EditForm
|
throw new InvalidArgumentException("Invalid value for UploadField::fileEditActions");
|
||||||
* @example 'getCMSValidator'
|
}
|
||||||
*
|
|
||||||
* @param Validator|string
|
/**
|
||||||
* @return Uploadfield Self reference
|
* FieldList $actions or string $name (of a method on File to provide a actions) for the EditForm
|
||||||
*/
|
* @example 'getCMSActions'
|
||||||
public function setFileEditValidator($fileEditValidator) {
|
*
|
||||||
$this->fileEditValidator = $fileEditValidator;
|
* @param FieldList|string
|
||||||
return $this;
|
* @return Uploadfield Self reference
|
||||||
}
|
*/
|
||||||
|
public function setFileEditActions($fileEditActions)
|
||||||
/**
|
{
|
||||||
*
|
$this->fileEditActions = $fileEditActions;
|
||||||
* @param File|AssetContainer $file
|
return $this;
|
||||||
* @return string URL to thumbnail
|
}
|
||||||
*/
|
|
||||||
protected function getThumbnailURLForFile(AssetContainer $file) {
|
/**
|
||||||
if (!$file->exists()) {
|
* Determines the validator to use for the edit form
|
||||||
return null;
|
* @example 'getCMSValidator'
|
||||||
}
|
*
|
||||||
|
* @param DataObject $file File context to generate validator from
|
||||||
// Attempt to generate image at given size
|
* @return Validator Validator object
|
||||||
$width = $this->getPreviewMaxWidth();
|
*/
|
||||||
$height = $this->getPreviewMaxHeight();
|
public function getFileEditValidator(DataObject $file)
|
||||||
if ($file->hasMethod('ThumbnailURL')) {
|
{
|
||||||
return $file->ThumbnailURL($width, $height);
|
// Empty validator
|
||||||
}
|
if (empty($this->fileEditValidator)) {
|
||||||
if ($file->hasMethod('Thumbnail')) {
|
return null;
|
||||||
return $file->Thumbnail($width, $height)->getURL();
|
}
|
||||||
}
|
|
||||||
if ($file->hasMethod('Fit')) {
|
// Validator instance
|
||||||
return $file->Fit($width, $height)->getURL();
|
if ($this->fileEditValidator instanceof Validator) {
|
||||||
}
|
return $this->fileEditValidator;
|
||||||
|
}
|
||||||
// Check if unsized icon is available
|
|
||||||
if($file->hasMethod('getIcon')) {
|
// Method to call on the given file
|
||||||
return $file->getIcon();
|
if ($file->hasMethod($this->fileEditValidator)) {
|
||||||
}
|
return $file->{$this->fileEditValidator}();
|
||||||
return null;
|
}
|
||||||
}
|
|
||||||
|
throw new InvalidArgumentException("Invalid value for UploadField::fileEditValidator");
|
||||||
public function getAttributes() {
|
}
|
||||||
return array_merge(
|
|
||||||
parent::getAttributes(),
|
/**
|
||||||
array(
|
* Validator (eg RequiredFields) or string $name (of a method on File to provide a Validator) for the EditForm
|
||||||
'type' => 'file',
|
* @example 'getCMSValidator'
|
||||||
'data-selectdialog-url' => $this->Link('select')
|
*
|
||||||
)
|
* @param Validator|string
|
||||||
);
|
* @return Uploadfield Self reference
|
||||||
}
|
*/
|
||||||
|
public function setFileEditValidator($fileEditValidator)
|
||||||
public function extraClass() {
|
{
|
||||||
if($this->isDisabled()) {
|
$this->fileEditValidator = $fileEditValidator;
|
||||||
$this->addExtraClass('disabled');
|
return $this;
|
||||||
}
|
}
|
||||||
if($this->isReadonly()) {
|
|
||||||
$this->addExtraClass('readonly');
|
/**
|
||||||
}
|
*
|
||||||
|
* @param File|AssetContainer $file
|
||||||
return parent::extraClass();
|
* @return string URL to thumbnail
|
||||||
}
|
*/
|
||||||
|
protected function getThumbnailURLForFile(AssetContainer $file)
|
||||||
public function Field($properties = array()) {
|
{
|
||||||
// Calculated config as per jquery.fileupload-ui.js
|
if (!$file->exists()) {
|
||||||
$allowedMaxFileNumber = $this->getAllowedMaxFileNumber();
|
return null;
|
||||||
$config = array(
|
}
|
||||||
'url' => $this->Link('upload'),
|
|
||||||
'urlSelectDialog' => $this->Link('select'),
|
// Attempt to generate image at given size
|
||||||
'urlAttach' => $this->Link('attach'),
|
$width = $this->getPreviewMaxWidth();
|
||||||
'urlFileExists' => $this->Link('fileexists'),
|
$height = $this->getPreviewMaxHeight();
|
||||||
'acceptFileTypes' => '.+$',
|
if ($file->hasMethod('ThumbnailURL')) {
|
||||||
// Fileupload treats maxNumberOfFiles as the max number of _additional_ items allowed
|
return $file->ThumbnailURL($width, $height);
|
||||||
'maxNumberOfFiles' => $allowedMaxFileNumber ? ($allowedMaxFileNumber - count($this->getItemIDs())) : null,
|
}
|
||||||
'replaceFile' => $this->getUpload()->getReplaceFile(),
|
if ($file->hasMethod('Thumbnail')) {
|
||||||
);
|
return $file->Thumbnail($width, $height)->getURL();
|
||||||
|
}
|
||||||
// Validation: File extensions
|
if ($file->hasMethod('Fit')) {
|
||||||
if ($allowedExtensions = $this->getAllowedExtensions()) {
|
return $file->Fit($width, $height)->getURL();
|
||||||
$config['acceptFileTypes'] = '(\.|\/)(' . implode('|', $allowedExtensions) . ')$';
|
}
|
||||||
$config['errorMessages']['acceptFileTypes'] = _t(
|
|
||||||
'File.INVALIDEXTENSIONSHORT',
|
// Check if unsized icon is available
|
||||||
'Extension is not allowed'
|
if ($file->hasMethod('getIcon')) {
|
||||||
);
|
return $file->getIcon();
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
// Validation: File size
|
}
|
||||||
if ($allowedMaxFileSize = $this->getValidator()->getAllowedMaxFileSize()) {
|
|
||||||
$config['maxFileSize'] = $allowedMaxFileSize;
|
public function getAttributes()
|
||||||
$config['errorMessages']['maxFileSize'] = _t(
|
{
|
||||||
'File.TOOLARGESHORT',
|
return array_merge(
|
||||||
'File size exceeds {size}',
|
parent::getAttributes(),
|
||||||
array('size' => File::format_size($config['maxFileSize']))
|
array(
|
||||||
);
|
'type' => 'file',
|
||||||
}
|
'data-selectdialog-url' => $this->Link('select')
|
||||||
|
)
|
||||||
// Validation: Number of files
|
);
|
||||||
if ($allowedMaxFileNumber) {
|
}
|
||||||
if($allowedMaxFileNumber > 1) {
|
|
||||||
$config['errorMessages']['maxNumberOfFiles'] = _t(
|
public function extraClass()
|
||||||
'UploadField.MAXNUMBEROFFILESSHORT',
|
{
|
||||||
'Can only upload {count} files',
|
if ($this->isDisabled()) {
|
||||||
array('count' => $allowedMaxFileNumber)
|
$this->addExtraClass('disabled');
|
||||||
);
|
}
|
||||||
} else {
|
if ($this->isReadonly()) {
|
||||||
$config['errorMessages']['maxNumberOfFiles'] = _t(
|
$this->addExtraClass('readonly');
|
||||||
'UploadField.MAXNUMBEROFFILESONE',
|
}
|
||||||
'Can only upload one file'
|
|
||||||
);
|
return parent::extraClass();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public function Field($properties = array())
|
||||||
// add overwrite warning error message to the config object sent to Javascript
|
{
|
||||||
if ($this->getOverwriteWarning()) {
|
// Calculated config as per jquery.fileupload-ui.js
|
||||||
$config['errorMessages']['overwriteWarning'] =
|
$allowedMaxFileNumber = $this->getAllowedMaxFileNumber();
|
||||||
_t('UploadField.OVERWRITEWARNING', 'File with the same name already exists');
|
$config = array(
|
||||||
}
|
'url' => $this->Link('upload'),
|
||||||
|
'urlSelectDialog' => $this->Link('select'),
|
||||||
$mergedConfig = array_merge($config, $this->ufConfig);
|
'urlAttach' => $this->Link('attach'),
|
||||||
return parent::Field(array(
|
'urlFileExists' => $this->Link('fileexists'),
|
||||||
'configString' => Convert::raw2json($mergedConfig),
|
'acceptFileTypes' => '.+$',
|
||||||
'config' => new ArrayData($mergedConfig),
|
// Fileupload treats maxNumberOfFiles as the max number of _additional_ items allowed
|
||||||
'multiple' => $allowedMaxFileNumber !== 1
|
'maxNumberOfFiles' => $allowedMaxFileNumber ? ($allowedMaxFileNumber - count($this->getItemIDs())) : null,
|
||||||
));
|
'replaceFile' => $this->getUpload()->getReplaceFile(),
|
||||||
}
|
);
|
||||||
|
|
||||||
/**
|
// Validation: File extensions
|
||||||
* Validation method for this field, called when the entire form is validated
|
if ($allowedExtensions = $this->getAllowedExtensions()) {
|
||||||
*
|
$config['acceptFileTypes'] = '(\.|\/)(' . implode('|', $allowedExtensions) . ')$';
|
||||||
* @param Validator $validator
|
$config['errorMessages']['acceptFileTypes'] = _t(
|
||||||
* @return boolean
|
'File.INVALIDEXTENSIONSHORT',
|
||||||
*/
|
'Extension is not allowed'
|
||||||
public function validate($validator) {
|
);
|
||||||
$name = $this->getName();
|
}
|
||||||
$files = $this->getItems();
|
|
||||||
|
// Validation: File size
|
||||||
// If there are no files then quit
|
if ($allowedMaxFileSize = $this->getValidator()->getAllowedMaxFileSize()) {
|
||||||
if($files->count() == 0) return true;
|
$config['maxFileSize'] = $allowedMaxFileSize;
|
||||||
|
$config['errorMessages']['maxFileSize'] = _t(
|
||||||
// Check max number of files
|
'File.TOOLARGESHORT',
|
||||||
$maxFiles = $this->getAllowedMaxFileNumber();
|
'File size exceeds {size}',
|
||||||
if($maxFiles && ($files->count() > $maxFiles)) {
|
array('size' => File::format_size($config['maxFileSize']))
|
||||||
$validator->validationError(
|
);
|
||||||
$name,
|
}
|
||||||
_t(
|
|
||||||
'UploadField.MAXNUMBEROFFILES',
|
// Validation: Number of files
|
||||||
'Max number of {count} file(s) exceeded.',
|
if ($allowedMaxFileNumber) {
|
||||||
array('count' => $maxFiles)
|
if ($allowedMaxFileNumber > 1) {
|
||||||
),
|
$config['errorMessages']['maxNumberOfFiles'] = _t(
|
||||||
"validation"
|
'UploadField.MAXNUMBEROFFILESSHORT',
|
||||||
);
|
'Can only upload {count} files',
|
||||||
return false;
|
array('count' => $allowedMaxFileNumber)
|
||||||
}
|
);
|
||||||
|
} else {
|
||||||
// Revalidate each file against nested validator
|
$config['errorMessages']['maxNumberOfFiles'] = _t(
|
||||||
$this->upload->clearErrors();
|
'UploadField.MAXNUMBEROFFILESONE',
|
||||||
foreach($files as $file) {
|
'Can only upload one file'
|
||||||
// Generate $_FILES style file attribute array for upload validator
|
);
|
||||||
$tmpFile = array(
|
}
|
||||||
'name' => $file->Name,
|
}
|
||||||
'type' => null, // Not used for type validation
|
|
||||||
'size' => $file->AbsoluteSize,
|
// add overwrite warning error message to the config object sent to Javascript
|
||||||
'tmp_name' => null, // Should bypass is_uploaded_file check
|
if ($this->getOverwriteWarning()) {
|
||||||
'error' => UPLOAD_ERR_OK,
|
$config['errorMessages']['overwriteWarning'] =
|
||||||
);
|
_t('UploadField.OVERWRITEWARNING', 'File with the same name already exists');
|
||||||
$this->upload->validate($tmpFile);
|
}
|
||||||
}
|
|
||||||
|
$mergedConfig = array_merge($config, $this->ufConfig);
|
||||||
// Check all errors
|
return parent::Field(array(
|
||||||
if($errors = $this->upload->getErrors()) {
|
'configString' => Convert::raw2json($mergedConfig),
|
||||||
foreach($errors as $error) {
|
'config' => new ArrayData($mergedConfig),
|
||||||
$validator->validationError($name, $error, "validation");
|
'multiple' => $allowedMaxFileNumber !== 1
|
||||||
}
|
));
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
return true;
|
* Validation method for this field, called when the entire form is validated
|
||||||
}
|
*
|
||||||
|
* @param Validator $validator
|
||||||
/**
|
* @return boolean
|
||||||
* @param HTTPRequest $request
|
*/
|
||||||
* @return UploadField_ItemHandler
|
public function validate($validator)
|
||||||
*/
|
{
|
||||||
public function handleItem(HTTPRequest $request) {
|
$name = $this->getName();
|
||||||
return $this->getItemHandler($request->param('ID'));
|
$files = $this->getItems();
|
||||||
}
|
|
||||||
|
// If there are no files then quit
|
||||||
/**
|
if ($files->count() == 0) {
|
||||||
* @param int $itemID
|
return true;
|
||||||
* @return UploadField_ItemHandler
|
}
|
||||||
*/
|
|
||||||
public function getItemHandler($itemID) {
|
// Check max number of files
|
||||||
return UploadField_ItemHandler::create($this, $itemID);
|
$maxFiles = $this->getAllowedMaxFileNumber();
|
||||||
}
|
if ($maxFiles && ($files->count() > $maxFiles)) {
|
||||||
|
$validator->validationError(
|
||||||
/**
|
$name,
|
||||||
* @param HTTPRequest $request
|
_t(
|
||||||
* @return UploadField_SelectHandler
|
'UploadField.MAXNUMBEROFFILES',
|
||||||
*/
|
'Max number of {count} file(s) exceeded.',
|
||||||
public function handleSelect(HTTPRequest $request) {
|
array('count' => $maxFiles)
|
||||||
if(!$this->canAttachExisting()) {
|
),
|
||||||
return $this->httpError(403);
|
"validation"
|
||||||
}
|
);
|
||||||
return UploadField_SelectHandler::create($this, $this->getFolderName());
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Revalidate each file against nested validator
|
||||||
* Safely encodes the File object with all standard fields required
|
$this->upload->clearErrors();
|
||||||
* by the front end
|
foreach ($files as $file) {
|
||||||
*
|
// Generate $_FILES style file attribute array for upload validator
|
||||||
* @param File|AssetContainer $file Object which contains a file
|
$tmpFile = array(
|
||||||
* @return array Array encoded list of file attributes
|
'name' => $file->Name,
|
||||||
*/
|
'type' => null, // Not used for type validation
|
||||||
protected function encodeFileAttributes(AssetContainer $file) {
|
'size' => $file->AbsoluteSize,
|
||||||
// Collect all output data.
|
'tmp_name' => null, // Should bypass is_uploaded_file check
|
||||||
$customised = $this->customiseFile($file);
|
'error' => UPLOAD_ERR_OK,
|
||||||
return array(
|
);
|
||||||
'id' => $file->ID,
|
$this->upload->validate($tmpFile);
|
||||||
'name' => basename($file->getFilename()),
|
}
|
||||||
'url' => $file->getURL(),
|
|
||||||
'thumbnail_url' => $customised->UploadFieldThumbnailURL,
|
// Check all errors
|
||||||
'edit_url' => $customised->UploadFieldEditLink,
|
if ($errors = $this->upload->getErrors()) {
|
||||||
'size' => $file->getAbsoluteSize(),
|
foreach ($errors as $error) {
|
||||||
'type' => File::get_file_type($file->getFilename()),
|
$validator->validationError($name, $error, "validation");
|
||||||
'buttons' => (string)$customised->UploadFieldFileButtons,
|
}
|
||||||
'fieldname' => $this->getName()
|
return false;
|
||||||
);
|
}
|
||||||
}
|
|
||||||
|
return true;
|
||||||
/**
|
}
|
||||||
* Action to handle upload of a single file
|
|
||||||
*
|
/**
|
||||||
* @param HTTPRequest $request
|
* @param HTTPRequest $request
|
||||||
* @return HTTPResponse
|
* @return UploadField_ItemHandler
|
||||||
* @return HTTPResponse
|
*/
|
||||||
*/
|
public function handleItem(HTTPRequest $request)
|
||||||
public function upload(HTTPRequest $request) {
|
{
|
||||||
if($this->isDisabled() || $this->isReadonly() || !$this->canUpload()) {
|
return $this->getItemHandler($request->param('ID'));
|
||||||
return $this->httpError(403);
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
// Protect against CSRF on destructive action
|
* @param int $itemID
|
||||||
$token = $this->getForm()->getSecurityToken();
|
* @return UploadField_ItemHandler
|
||||||
if(!$token->checkRequest($request)) return $this->httpError(400);
|
*/
|
||||||
|
public function getItemHandler($itemID)
|
||||||
// Get form details
|
{
|
||||||
$name = $this->getName();
|
return UploadField_ItemHandler::create($this, $itemID);
|
||||||
$postVars = $request->postVar($name);
|
}
|
||||||
|
|
||||||
// Extract uploaded files from Form data
|
/**
|
||||||
$uploadedFiles = $this->extractUploadedFileData($postVars);
|
* @param HTTPRequest $request
|
||||||
$return = array();
|
* @return UploadField_SelectHandler
|
||||||
|
*/
|
||||||
// Save the temporary files into a File objects
|
public function handleSelect(HTTPRequest $request)
|
||||||
// and save data/error on a per file basis
|
{
|
||||||
foreach ($uploadedFiles as $tempFile) {
|
if (!$this->canAttachExisting()) {
|
||||||
$file = $this->saveTemporaryFile($tempFile, $error);
|
return $this->httpError(403);
|
||||||
if(empty($file)) {
|
}
|
||||||
array_push($return, array('error' => $error));
|
return UploadField_SelectHandler::create($this, $this->getFolderName());
|
||||||
} else {
|
}
|
||||||
array_push($return, $this->encodeFileAttributes($file));
|
|
||||||
}
|
/**
|
||||||
$this->upload->clearErrors();
|
* Safely encodes the File object with all standard fields required
|
||||||
}
|
* by the front end
|
||||||
|
*
|
||||||
// Format response with json
|
* @param File|AssetContainer $file Object which contains a file
|
||||||
$response = new HTTPResponse(Convert::raw2json($return));
|
* @return array Array encoded list of file attributes
|
||||||
$response->addHeader('Content-Type', 'text/plain');
|
*/
|
||||||
return $response;
|
protected function encodeFileAttributes(AssetContainer $file)
|
||||||
}
|
{
|
||||||
|
// Collect all output data.
|
||||||
/**
|
$customised = $this->customiseFile($file);
|
||||||
* Retrieves details for files that this field wishes to attache to the
|
return array(
|
||||||
* client-side form
|
'id' => $file->ID,
|
||||||
*
|
'name' => basename($file->getFilename()),
|
||||||
* @param HTTPRequest $request
|
'url' => $file->getURL(),
|
||||||
* @return HTTPResponse
|
'thumbnail_url' => $customised->UploadFieldThumbnailURL,
|
||||||
*/
|
'edit_url' => $customised->UploadFieldEditLink,
|
||||||
public function attach(HTTPRequest $request) {
|
'size' => $file->getAbsoluteSize(),
|
||||||
if(!$request->isPOST()) return $this->httpError(403);
|
'type' => File::get_file_type($file->getFilename()),
|
||||||
if(!$this->canAttachExisting()) return $this->httpError(403);
|
'buttons' => (string)$customised->UploadFieldFileButtons,
|
||||||
|
'fieldname' => $this->getName()
|
||||||
// Retrieve file attributes required by front end
|
);
|
||||||
$return = array();
|
}
|
||||||
$files = File::get()->byIDs($request->postVar('ids'));
|
|
||||||
foreach($files as $file) {
|
/**
|
||||||
$return[] = $this->encodeFileAttributes($file);
|
* Action to handle upload of a single file
|
||||||
}
|
*
|
||||||
$response = new HTTPResponse(Convert::raw2json($return));
|
* @param HTTPRequest $request
|
||||||
$response->addHeader('Content-Type', 'application/json');
|
* @return HTTPResponse
|
||||||
return $response;
|
* @return HTTPResponse
|
||||||
}
|
*/
|
||||||
|
public function upload(HTTPRequest $request)
|
||||||
/**
|
{
|
||||||
* Check if file exists, both checking filtered filename and exact filename
|
if ($this->isDisabled() || $this->isReadonly() || !$this->canUpload()) {
|
||||||
*
|
return $this->httpError(403);
|
||||||
* @param string $originalFile Filename
|
}
|
||||||
* @return bool
|
|
||||||
*/
|
// Protect against CSRF on destructive action
|
||||||
protected function checkFileExists($originalFile) {
|
$token = $this->getForm()->getSecurityToken();
|
||||||
|
if (!$token->checkRequest($request)) {
|
||||||
// Check both original and safely filtered filename
|
return $this->httpError(400);
|
||||||
$nameFilter = FileNameFilter::create();
|
}
|
||||||
$filteredFile = $nameFilter->filter($originalFile);
|
|
||||||
|
// Get form details
|
||||||
// Resolve expected folder name
|
$name = $this->getName();
|
||||||
$folderName = $this->getFolderName();
|
$postVars = $request->postVar($name);
|
||||||
$folder = Folder::find_or_make($folderName);
|
|
||||||
$parentPath = $folder ? $folder->getFilename() : '';
|
// Extract uploaded files from Form data
|
||||||
|
$uploadedFiles = $this->extractUploadedFileData($postVars);
|
||||||
// check if either file exists
|
$return = array();
|
||||||
return File::find($parentPath.$originalFile) || File::find($parentPath.$filteredFile);
|
|
||||||
}
|
// Save the temporary files into a File objects
|
||||||
|
// and save data/error on a per file basis
|
||||||
/**
|
foreach ($uploadedFiles as $tempFile) {
|
||||||
* Determines if a specified file exists
|
$file = $this->saveTemporaryFile($tempFile, $error);
|
||||||
*
|
if (empty($file)) {
|
||||||
* @param HTTPRequest $request
|
array_push($return, array('error' => $error));
|
||||||
* @return HTTPResponse
|
} else {
|
||||||
*/
|
array_push($return, $this->encodeFileAttributes($file));
|
||||||
public function fileexists(HTTPRequest $request) {
|
}
|
||||||
// Assert that requested filename doesn't attempt to escape the directory
|
$this->upload->clearErrors();
|
||||||
$originalFile = $request->requestVar('filename');
|
}
|
||||||
if($originalFile !== basename($originalFile)) {
|
|
||||||
$return = array(
|
// Format response with json
|
||||||
'error' => _t('File.NOVALIDUPLOAD', 'File is not a valid upload')
|
$response = new HTTPResponse(Convert::raw2json($return));
|
||||||
);
|
$response->addHeader('Content-Type', 'text/plain');
|
||||||
} else {
|
return $response;
|
||||||
$return = array(
|
}
|
||||||
'exists' => $this->checkFileExists($originalFile)
|
|
||||||
);
|
/**
|
||||||
}
|
* Retrieves details for files that this field wishes to attache to the
|
||||||
|
* client-side form
|
||||||
// Encode and present response
|
*
|
||||||
$response = new HTTPResponse(Convert::raw2json($return));
|
* @param HTTPRequest $request
|
||||||
$response->addHeader('Content-Type', 'application/json');
|
* @return HTTPResponse
|
||||||
if (!empty($return['error'])) $response->setStatusCode(400);
|
*/
|
||||||
return $response;
|
public function attach(HTTPRequest $request)
|
||||||
}
|
{
|
||||||
|
if (!$request->isPOST()) {
|
||||||
public function performReadonlyTransformation() {
|
return $this->httpError(403);
|
||||||
$clone = clone $this;
|
}
|
||||||
$clone->addExtraClass('readonly');
|
if (!$this->canAttachExisting()) {
|
||||||
$clone->setReadonly(true);
|
return $this->httpError(403);
|
||||||
return $clone;
|
}
|
||||||
}
|
|
||||||
|
// Retrieve file attributes required by front end
|
||||||
|
$return = array();
|
||||||
|
$files = File::get()->byIDs($request->postVar('ids'));
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$return[] = $this->encodeFileAttributes($file);
|
||||||
|
}
|
||||||
|
$response = new HTTPResponse(Convert::raw2json($return));
|
||||||
|
$response->addHeader('Content-Type', 'application/json');
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if file exists, both checking filtered filename and exact filename
|
||||||
|
*
|
||||||
|
* @param string $originalFile Filename
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function checkFileExists($originalFile)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Check both original and safely filtered filename
|
||||||
|
$nameFilter = FileNameFilter::create();
|
||||||
|
$filteredFile = $nameFilter->filter($originalFile);
|
||||||
|
|
||||||
|
// Resolve expected folder name
|
||||||
|
$folderName = $this->getFolderName();
|
||||||
|
$folder = Folder::find_or_make($folderName);
|
||||||
|
$parentPath = $folder ? $folder->getFilename() : '';
|
||||||
|
|
||||||
|
// check if either file exists
|
||||||
|
return File::find($parentPath.$originalFile) || File::find($parentPath.$filteredFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if a specified file exists
|
||||||
|
*
|
||||||
|
* @param HTTPRequest $request
|
||||||
|
* @return HTTPResponse
|
||||||
|
*/
|
||||||
|
public function fileexists(HTTPRequest $request)
|
||||||
|
{
|
||||||
|
// Assert that requested filename doesn't attempt to escape the directory
|
||||||
|
$originalFile = $request->requestVar('filename');
|
||||||
|
if ($originalFile !== basename($originalFile)) {
|
||||||
|
$return = array(
|
||||||
|
'error' => _t('File.NOVALIDUPLOAD', 'File is not a valid upload')
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$return = array(
|
||||||
|
'exists' => $this->checkFileExists($originalFile)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode and present response
|
||||||
|
$response = new HTTPResponse(Convert::raw2json($return));
|
||||||
|
$response->addHeader('Content-Type', 'application/json');
|
||||||
|
if (!empty($return['error'])) {
|
||||||
|
$response->setStatusCode(400);
|
||||||
|
}
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function performReadonlyTransformation()
|
||||||
|
{
|
||||||
|
$clone = clone $this;
|
||||||
|
$clone->addExtraClass('readonly');
|
||||||
|
$clone->setReadonly(true);
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,140 +16,150 @@ use SilverStripe\Assets\Upload_Validator;
|
|||||||
*/
|
*/
|
||||||
trait UploadReceiver
|
trait UploadReceiver
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Upload object (needed for validation
|
* Upload object (needed for validation
|
||||||
* and actually moving the temporary file
|
* and actually moving the temporary file
|
||||||
* created by PHP).
|
* created by PHP).
|
||||||
*
|
*
|
||||||
* @var Upload
|
* @var Upload
|
||||||
*/
|
*/
|
||||||
protected $upload;
|
protected $upload;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Partial filesystem path relative to /assets directory.
|
* Partial filesystem path relative to /assets directory.
|
||||||
* Defaults to Upload::$uploads_folder.
|
* Defaults to Upload::$uploads_folder.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $folderName = false;
|
protected $folderName = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bootstrap Uploadable field
|
* Bootstrap Uploadable field
|
||||||
*/
|
*/
|
||||||
protected function constructUploadReceiver() {
|
protected function constructUploadReceiver()
|
||||||
// Set Upload instance
|
{
|
||||||
$this->setUpload(Upload::create());
|
// Set Upload instance
|
||||||
|
$this->setUpload(Upload::create());
|
||||||
|
|
||||||
// filter out '' since this would be a regex problem on JS end
|
// filter out '' since this would be a regex problem on JS end
|
||||||
$this->getValidator()->setAllowedExtensions(
|
$this->getValidator()->setAllowedExtensions(
|
||||||
array_filter(File::config()->allowed_extensions)
|
array_filter(File::config()->allowed_extensions)
|
||||||
);
|
);
|
||||||
|
|
||||||
// get the lower max size
|
// get the lower max size
|
||||||
$maxUpload = File::ini2bytes(ini_get('upload_max_filesize'));
|
$maxUpload = File::ini2bytes(ini_get('upload_max_filesize'));
|
||||||
$maxPost = File::ini2bytes(ini_get('post_max_size'));
|
$maxPost = File::ini2bytes(ini_get('post_max_size'));
|
||||||
$this->getValidator()->setAllowedMaxFileSize(min($maxUpload, $maxPost));
|
$this->getValidator()->setAllowedMaxFileSize(min($maxUpload, $maxPost));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the Upload handler
|
* Retrieves the Upload handler
|
||||||
*
|
*
|
||||||
* @return Upload
|
* @return Upload
|
||||||
*/
|
*/
|
||||||
public function getUpload() {
|
public function getUpload()
|
||||||
return $this->upload;
|
{
|
||||||
}
|
return $this->upload;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the upload handler
|
* Sets the upload handler
|
||||||
*
|
*
|
||||||
* @param Upload $upload
|
* @param Upload $upload
|
||||||
* @return $this Self reference
|
* @return $this Self reference
|
||||||
*/
|
*/
|
||||||
public function setUpload(Upload $upload) {
|
public function setUpload(Upload $upload)
|
||||||
$this->upload = $upload;
|
{
|
||||||
return $this;
|
$this->upload = $upload;
|
||||||
}
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Limit allowed file extensions. Empty by default, allowing all extensions.
|
* Limit allowed file extensions. Empty by default, allowing all extensions.
|
||||||
* To allow files without an extension, use an empty string.
|
* To allow files without an extension, use an empty string.
|
||||||
* See {@link File::$allowed_extensions} to get a good standard set of
|
* See {@link File::$allowed_extensions} to get a good standard set of
|
||||||
* extensions that are typically not harmful in a webserver context.
|
* extensions that are typically not harmful in a webserver context.
|
||||||
* See {@link setAllowedMaxFileSize()} to limit file size by extension.
|
* See {@link setAllowedMaxFileSize()} to limit file size by extension.
|
||||||
*
|
*
|
||||||
* @param array $rules List of extensions
|
* @param array $rules List of extensions
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setAllowedExtensions($rules) {
|
public function setAllowedExtensions($rules)
|
||||||
$this->getValidator()->setAllowedExtensions($rules);
|
{
|
||||||
return $this;
|
$this->getValidator()->setAllowedExtensions($rules);
|
||||||
}
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Limit allowed file extensions by specifying categories of file types.
|
* Limit allowed file extensions by specifying categories of file types.
|
||||||
* These may be 'image', 'image/supported', 'audio', 'video', 'archive', 'flash', or 'document'
|
* These may be 'image', 'image/supported', 'audio', 'video', 'archive', 'flash', or 'document'
|
||||||
* See {@link File::$allowed_extensions} for details of allowed extensions
|
* See {@link File::$allowed_extensions} for details of allowed extensions
|
||||||
* for each of these categories
|
* for each of these categories
|
||||||
*
|
*
|
||||||
* @param string $category Category name
|
* @param string $category Category name
|
||||||
* @param string,... $categories Additional category names
|
* @param string,... $categories Additional category names
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setAllowedFileCategories($category) {
|
public function setAllowedFileCategories($category)
|
||||||
$extensions = File::get_category_extensions(func_get_args());
|
{
|
||||||
return $this->setAllowedExtensions($extensions);
|
$extensions = File::get_category_extensions(func_get_args());
|
||||||
}
|
return $this->setAllowedExtensions($extensions);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns list of extensions allowed by this field, or an empty array
|
* Returns list of extensions allowed by this field, or an empty array
|
||||||
* if there is no restriction
|
* if there is no restriction
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getAllowedExtensions() {
|
public function getAllowedExtensions()
|
||||||
return $this->getValidator()->getAllowedExtensions();
|
{
|
||||||
}
|
return $this->getValidator()->getAllowedExtensions();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get custom validator for this field
|
* Get custom validator for this field
|
||||||
*
|
*
|
||||||
* @return Upload_Validator
|
* @return Upload_Validator
|
||||||
*/
|
*/
|
||||||
public function getValidator() {
|
public function getValidator()
|
||||||
return $this->getUpload()->getValidator();
|
{
|
||||||
}
|
return $this->getUpload()->getValidator();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set custom validator for this field
|
* Set custom validator for this field
|
||||||
*
|
*
|
||||||
* @param Upload_Validator $validator
|
* @param Upload_Validator $validator
|
||||||
* @return $this Self reference
|
* @return $this Self reference
|
||||||
*/
|
*/
|
||||||
public function setValidator(Upload_Validator $validator) {
|
public function setValidator(Upload_Validator $validator)
|
||||||
$this->getUpload()->setValidator($validator);
|
{
|
||||||
return $this;
|
$this->getUpload()->setValidator($validator);
|
||||||
}
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the upload folder name
|
* Sets the upload folder name
|
||||||
*
|
*
|
||||||
* @param string $folderName
|
* @param string $folderName
|
||||||
* @return $this Self reference
|
* @return $this Self reference
|
||||||
*/
|
*/
|
||||||
public function setFolderName($folderName) {
|
public function setFolderName($folderName)
|
||||||
$this->folderName = $folderName;
|
{
|
||||||
return $this;
|
$this->folderName = $folderName;
|
||||||
}
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the upload folder name
|
* Gets the upload folder name
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getFolderName() {
|
public function getFolderName()
|
||||||
return ($this->folderName !== false)
|
{
|
||||||
? $this->folderName
|
return ($this->folderName !== false)
|
||||||
: Upload::config()->uploads_folder;
|
? $this->folderName
|
||||||
}
|
: Upload::config()->uploads_folder;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user