Browse files

API UploadField functions on new records

Fixed regression from 1e5d404 (UploadField::canPreviewFolder).
Merged in pull request #2009 - (6018bdd).
Merged pull request #1259 (34bfc86).
  • Loading branch information...
1 parent 2b9a44e commit 7f057ce3431a917d4631a5ea04a66bf11c1fadaa @tractorcow tractorcow committed May 27, 2013
View
7 _config/uploadfield.yml
@@ -6,12 +6,9 @@ UploadField:
allowedMaxFileNumber:
canUpload: true
canAttachExisting: 'CMS_ACCESS_AssetAdmin'
- replaceExistingFile: false
+ canPreviewFolder: true
previewMaxWidth: 80
previewMaxHeight: 60
uploadTemplateName: 'ss-uploadfield-uploadtemplate'
downloadTemplateName: 'ss-uploadfield-downloadtemplate'
- fileEditFields:
- fileEditActions:
- fileEditValidator:
- overwriteWarning: true # Warning before overwriting existing file (only relevant when Upload: replaceFile is true)
+ overwriteWarning: true # Warning before overwriting existing file (only relevant when Upload: replaceFile is true)
View
10 css/GridField.css
@@ -44,11 +44,11 @@ Used in side panels and action tabs
.cms table.ss-gridfield-table tbody td.col-listChildrenLink .list-children-link { background: transparent url(../images/sitetree_ss_default_icons.png) no-repeat 3px -4px; display: block; }
.cms table.ss-gridfield-table tbody td.col-getTreeTitle span.item { color: #0073c1; }
.cms table.ss-gridfield-table tbody td.col-getTreeTitle span.badge { clear: both; text-transform: uppercase; display: inline-block; padding: 0px 3px; font-size: 0.75em; line-height: 1em; margin-left: 10px; margin-right: 6px; margin-top: -1px; -webkit-border-radius: 2px 2px; -moz-border-radius: 2px / 2px; border-radius: 2px / 2px; }
-.cms table.ss-gridfield-table tbody td.col-getTreeTitle span.badge.modified { color: #7E7470; border: 1px solid #C9B800; background-color: #FFF0BC; }
-.cms table.ss-gridfield-table tbody td.col-getTreeTitle span.badge.addedtodraft { color: #7E7470; border: 1px solid #C9B800; background-color: #FFF0BC; }
-.cms table.ss-gridfield-table tbody td.col-getTreeTitle span.badge.deletedonlive { color: #636363; border: 1px solid #E49393; background-color: #F2DADB; }
-.cms table.ss-gridfield-table tbody td.col-getTreeTitle span.badge.removedfromdraft { color: #636363; border: 1px solid #E49393; background-color: #F2DADB; }
-.cms table.ss-gridfield-table tbody td.col-getTreeTitle span.badge.workflow-approval { color: #56660C; border: 1px solid #7C8816; background-color: #DAE79A; }
+.cms table.ss-gridfield-table tbody td.col-getTreeTitle span.badge.status-modified { color: #7E7470; border: 1px solid #C9B800; background-color: #FFF0BC; }
+.cms table.ss-gridfield-table tbody td.col-getTreeTitle span.badge.status-addedtodraft { color: #7E7470; border: 1px solid #C9B800; background-color: #FFF0BC; }
+.cms table.ss-gridfield-table tbody td.col-getTreeTitle span.badge.status-deletedonlive { color: #636363; border: 1px solid #E49393; background-color: #F2DADB; }
+.cms table.ss-gridfield-table tbody td.col-getTreeTitle span.badge.status-removedfromdraft { color: #636363; border: 1px solid #E49393; background-color: #F2DADB; }
+.cms table.ss-gridfield-table tbody td.col-getTreeTitle span.badge.status-workflow-approval { color: #56660C; border: 1px solid #7C8816; background-color: #DAE79A; }
.cms table.ss-gridfield-table tbody td button { border: none; background: none; margin: 0 0 0 2px; padding: 1px 0; width: auto; text-shadow: none; }
.cms table.ss-gridfield-table tbody td button.ui-state-hover { background: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; }
.cms table.ss-gridfield-table tbody td button.ui-state-active { border: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; }
View
16 css/UploadField.css
@@ -18,26 +18,26 @@ Used in side panels and action tabs
.ss-uploadfield .ss-uploadfield-item { margin: 0; padding: 15px; overflow: auto; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-preview { height: 60px; line-height: 60px; width: 80px; text-align: center; font-weight: bold; float: left; overflow: hidden; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-preview.ss-uploadfield-dropzone { -webkit-box-shadow: gray 0 0 4px 0 inset; -moz-box-shadow: gray 0 0 4px 0 inset; box-shadow: gray 0 0 4px 0 inset; border: 2px dashed gray; background: #d0d3d5; display: none; margin-right: 15px; }
-.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info { float: left; margin-left: 15px; }
+.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info { margin-left: 95px; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name { display: block; line-height: 13px; height: 26px; margin: 0; text-align: left; }
-.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name b { font-weight: bold; padding: 0 5px 0 0; }
-.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .name { font-size: 11px; color: #848484; width: 290px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; display: inline; float: left; }
-.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .ss-uploadfield-item-status { float: right; padding: 0 0 0 5px; width: 100px; text-align: right; }
-.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .ss-uploadfield-item-status.ui-state-error-text { color: red; font-weight: bold; }
+.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .name { max-width: 240px; font-weight: bold; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; display: inline; float: left; }
+.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .size { color: #848484; padding: 0 0 0 5px; display: inline; float: left; }
+.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .ss-uploadfield-item-status { float: right; padding: 0 0 0 5px; text-align: right; max-width: 75%; }
+.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .ss-uploadfield-item-status.ui-state-error-text { color: red; font-weight: bold; width: 150px; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .ss-uploadfield-item-status.ui-state-warning-text { color: #b7a403; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .ss-uploadfield-item-status.ui-state-success-text { color: #1f9433; }
-.ss-uploadfield .ss-ui-button { display: block; float: left; margin: 0 10px 0 0; }
+.ss-uploadfield .ss-ui-button { display: block; float: left; margin: 0 10px 6px 0; }
.ss-uploadfield .ss-ui-button.ss-uploadfield-fromcomputer { position: relative; overflow: hidden; }
.ss-uploadfield .ss-uploadfield-files { margin: 0; padding: 0; overflow: auto; position: relative; }
.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item, .ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item.ui-state-error { border: 0; border-bottom: 1px solid #b3b3b3; background: none; color: #444444; }
.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item:last-child, .ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item.ui-state-error:last-child { border-bottom: 0; }
-.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-actions { height: 28px; margin: 6px 0 0; position: relative; }
+.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-actions { min-height: 28px; overflow: hidden; margin: 6px 0 -6px 0; position: relative; }
.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-progress { position: absolute; left: 0; right: 42px; width: auto; margin: 11px 0 0; height: 15px; }
.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-progress div { -webkit-border-radius: 25px; -moz-border-radius: 25px; -ms-border-radius: 25px; -o-border-radius: 25px; border-radius: 25px; height: 13px; padding: 0; margin: 0; overflow: hidden; }
.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-progressbar { border: 1px solid gray; background-color: #92a6b3; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #92a6b3), color-stop(11%, #90aab8), color-stop(22%, #96b1bf), color-stop(33%, #9eb4c1), color-stop(44%, #a7bac7), color-stop(100%, #c1d5dc)); background-image: -webkit-linear-gradient(top, #92a6b3 0%, #90aab8 11%, #96b1bf 22%, #9eb4c1 33%, #a7bac7 44%, #c1d5dc 100%); background-image: -moz-linear-gradient(top, #92a6b3 0%, #90aab8 11%, #96b1bf 22%, #9eb4c1 33%, #a7bac7 44%, #c1d5dc 100%); background-image: -o-linear-gradient(top, #92a6b3 0%, #90aab8 11%, #96b1bf 22%, #9eb4c1 33%, #a7bac7 44%, #c1d5dc 100%); background-image: linear-gradient(top, #92a6b3 0%, #90aab8 11%, #96b1bf 22%, #9eb4c1 33%, #a7bac7 44%, #c1d5dc 100%); }
.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-progressbarvalue { border: 0; width: 0%; background: #60b3dd url(../images/progressbar_blue.gif) repeat-x left center; }
.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-cancel, .ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-start { position: absolute; top: 10px; right: 0; }
-.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-cancel button, .ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-start button { display: block; overflow: hidden; text-indent: -9999px; padding: 0; margin: 0; border: 0; width: 16px; height: 16px; cursor: pointer; -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; background: none; position: relative; }
+.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-cancel button, .ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-start button { display: block; overflow: hidden; text-indent: -9999px; padding: 0; margin: 0; border: 0; width: 16px; height: 16px; cursor: pointer; -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; position: relative; }
.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-cancel button span, .ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-start button span { position: absolute; left: 0; top: 0; margin: 0; }
.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-cancel button span.ui-button-text, .ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-start button span.ui-button-text { display: none; }
.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-start { right: 20px; }
View
1 docs/en/changelogs/3.1.0.md
@@ -415,6 +415,7 @@ you can enable those warnings and future-proof your code already.
* Removed `SQLMap::map()`: Use DataList::("Member")->map()
* Removed `SQLMap::mapInGroups()`: Use Member::map_in_groups()
* Removed `PasswordEncryptor::register()/unregister()`: Use config system instead
+ * `UploadField::setConfig()` and `UploadField::getConfig()` are no longer public: Use set{Property} or get{Property} to configure an `UploadField` instance
* Methods on DataList and ArrayList that used to both modify the existing list & return a new version now just return a new version. Make sure you change statements like `$list->filter(...)` to $`list = $list->filter(...)` for these methods:
- `ArrayList#reverse`
- `ArrayList#sort`
View
210 docs/en/reference/uploadfield.md
@@ -10,8 +10,19 @@ like for instance in creating and managing a simple gallery.
## Usage
-The field can be used in two ways: To upload a single file into a `has_one` relationship,
-or allow multiple files into a fixed folder (or relationship).
+The field can be used in three ways: To upload a single file into a `has_one` relationship,
+or allow multiple files into a `has_many` or `many_many` relationship, or to act as a stand
+alone uploader into a folder with no underlying relation.
+
+## Validation
+
+Although images are uploaded and stored on the filesystem immediately after selection,
+the value (or values) of this field will not be written to any related record until
+the record is saved and successfully validated. However, any invalid records will still
+persist across form submissions until explicitly removed or replaced by the user.
+
+Care should be taken as invalid files may remain within the filesystem until explicitly
+removed.
### Single fileupload
@@ -42,13 +53,13 @@ based on a has_one relation:
The UploadField will autodetect the relation based on it's `name` property, and
save it into the GalleyPages' `SingleImageID` field. Setting the
-`allowedMaxFileNumber` to 1 will make sure that only one image can ever be
+`setAllowedMaxFileNumber` to 1 will make sure that only one image can ever be
uploaded and linked to the relation.
### Multiple fileupload
-Enable multiple fileuploads by using a many_many relation. Again, the
-UploadField will detect the relation based on its $name property value:
+Enable multiple fileuploads by using a many_many (or has_many) relation. Again,
+the `UploadField` will detect the relation based on its $name property value:
:::php
class GalleryPage extends Page {
@@ -68,22 +79,33 @@ UploadField will detect the relation based on its $name property value:
$title = 'Upload one or more images (max 10 in total)'
)
);
- $uploadField->setConfig('allowedMaxFileNumber', 10);
+ $uploadField->setAllowedMaxFileNumber(10);
return $fields;
}
}
class GalleryPage_Controller extends Page_Controller {
}
-
-WARNING: Currently the UploadField doesn't fully support has_many relations, so use a many_many relation instead!
+
+ class GalleryImageExtension extends DataExtension {
+ private static $belongs_many_many = array('Galleries' => 'GalleryPage);
+ }
+
+ Image::add_extension('GalleryImageExtension');
+
+<div class="notice" markdown='1'>
+In order to link both ends of the relationship together it's usually advisable to
+extend Image with the necessary $has_one, $belongs_to, $has_many or $belongs_many_many.
+In particular, a DataObject with $has_many Images will not work without this specified explicitly.
+</div>
## Configuration
### Overview
-The field can either be configured on an instance level through `setConfig(<key>, <value>)`,
-or globally by overriding the YAML defaults. See the [Configuration Reference](uploadfield#configuration-reference) section for possible values.
+The field can either be configured on an instance level with the various
+getProperty and setProperty functions, or globally by overriding the YAML defaults.
+See the [Configuration Reference](uploadfield#configuration-reference) section for possible values.
Example: mysite/_config/uploadfield.yml
@@ -93,7 +115,6 @@ Example: mysite/_config/uploadfield.yml
defaultConfig:
canUpload: false
-
### Set a custom folder
This example will save all uploads in the `/assets/customfolder/` folder. If
@@ -108,13 +129,22 @@ the folder doesn't exist, it will be created.
);
$uploadField->setFolderName('customfolder');
-## Limit the allowed filetypes
+### Limit the allowed filetypes
`AllowedExtensions` defaults to the `File.allowed_extensions` configuration setting,
but can be overwritten for each UploadField:
:::php
- $uploadField->getValidator()->setAllowedExtensions(array('jpg', 'jpeg', 'png', 'gif'));
+ $uploadField->setAllowedExtensions(array('jpg', 'jpeg', 'png', 'gif'));
+
+Entire groups of file extensions can be specified in order to quickly limit types
+to known file categories.
+
+ :::php
+ // This will limit files to the following extensions:
+ // "bmp" ,"gif" ,"jpg" ,"jpeg" ,"pcx" ,"tif" ,"png" ,"alpha","als" ,"cel" ,"icon" ,"ico" ,"ps"
+ // 'doc','docx','txt','rtf','xls','xlsx','pages', 'ppt','pptx','pps','csv', 'html','htm','xhtml', 'xml','pdf'
+ $uploadField->setAllowedFileCategories('image', 'doc');
### Limit the maximum file size
@@ -135,8 +165,24 @@ Set the dimensions of the image preview. By default the max width is set to 80
and the max height is set to 60.
:::php
- $uploadField->setConfig('previewMaxWidth', 100);
- $uploadField->setConfig('previewMaxHeight', 100);
+ $uploadField->setPreviewMaxWidth(100);
+ $uploadField->setPreviewMaxHeight(100);
+
+### Disable attachment of existing files
+
+This can force the user to upload a new file, rather than link to the already
+existing file librarry
+
+ :::php
+ $uploadField->setCanAttachExisting(false);
+
+### Disable uploading of new files
+
+Alternatively, you can force the user to only specify already existing files
+in the file library
+
+ :::php
+ $uploadField->setCanUpload(false);
### Automatic or manual upload
@@ -145,8 +191,8 @@ Setting the `autoUpload` property to false, will present you with a list of
selected files that you can then upload manually one by one:
:::php
- $uploadField->setConfig('autoUpload', false);
-
+ $uploadField->setAutoUpload(false);
+
### Build a simple gallery
A gallery most times needs more then simple images. You might want to add a
@@ -171,8 +217,14 @@ Now register the DataExtension for the Image class in your _config.php:
:::php
Image::add_extension('GalleryImage');
-
-NOTE: although you can subclass the Image class instead of using a DataExtension, this is not advisable. For instance: when using a subclass, the 'From files' button will only return files that were uploaded for that subclass, it won't recognize any other images!
+
+<div class="notice" markdown='1'>
+Note: Although you can subclass the Image class instead of using a DataExtension,
+this is not advisable. For instance: when using a subclass, the 'From files'
+button will only return files that were uploaded for that subclass, it won't
+recognize any other images!
+</div>
+
### Edit uploaded images
By default the UploadField will let you edit the following fields: *Title,
@@ -195,40 +247,128 @@ you you alter these settings. One way to go about this is create a
Then, in your GalleryPage, tell the UploadField to use this function:
:::php
- $uploadField->setConfig('fileEditFields', 'getCustomFields');
+ $uploadField->setFileEditFields('getCustomFields');
-In a similar fashion you can use 'fileEditActions' to set the actions for the
+In a similar fashion you can use 'setFileEditActions' to set the actions for the
editform, or 'fileEditValidator' to determine the validator (eg RequiredFields).
### Configuration Reference
- - `autoUpload`: (boolean)
- - `allowedMaxFileNumber`: (int) php validation of allowedMaxFileNumber
+ - `setAllowedMaxFileNumber`: (int) php validation of allowedMaxFileNumber
only works when a db relation is available, set to null to allow
unlimited if record has a has_one and allowedMaxFileNumber is null, it will be set to 1
- - `canUpload`: (boolean) Can the user upload new files, or just select from existing files.
+ - `setAllowedFileExtensions`: (array) List of file extensions allowed
+ - `setAllowedFileCategories`: (array|string) List of types of files allowed.
+ May be any of 'image', 'audio', 'mov', 'zip', 'flash', or 'doc'
+ - `setAutoUpload`: (boolean) Should the field automatically trigger an upload once
+ a file is selected?
+ - `setCanAttachExisting`: (boolean|string) Can the user attach existing files from the library.
String values are interpreted as permission codes.
- - `previewMaxWidth`: (int)
- - `previewMaxHeight`: (int)
- - `uploadTemplateName`: (string) javascript template used to display uploading
- files, see javascript/UploadField_uploadtemplate.js
- - `downloadTemplateName`: (string) javascript template used to display already
+ - `setCanPreviewFolder`: (boolean|string) Can the user preview the folder files will be saved into?
+ String values are interpreted as permission codes.
+ - `setCanUpload`: (boolean|string) Can the user upload new files, or just select from existing files.
+ String values are interpreted as permission codes.
+ - `setDownloadTemplateName`: (string) javascript template used to display already
uploaded files, see javascript/UploadField_downloadtemplate.js
- - `fileEditFields`: (FieldList|string) FieldList $fields or string $name
+ - `setFileEditFields`: (FieldList|string) FieldList $fields or string $name
(of a method on File to provide a fields) for the EditForm (Example: 'getCMSFields')
- - `fileEditActions`: (FieldList|string) FieldList $actions or string $name
+ - `setFileEditActions`: (FieldList|string) FieldList $actions or string $name
(of a method on File to provide a actions) for the EditForm (Example: 'getCMSActions')
- - `fileEditValidator`: (string) Validator (eg RequiredFields) or string $name
+ - `setFileEditValidator`: (string) Validator (eg RequiredFields) or string $name
(of a method on File to provide a Validator) for the EditForm (Example: 'getCMSValidator')
+ - `setOverwriteWarning`: (boolean) Show a warning when overwriting a file.
+ - `setPreviewMaxWidth`: (int)
+ - `setPreviewMaxHeight`: (int)
+ - `setTemplateFileButtons`: (string) Template name to use for the file buttons
+ - `setTemplateFileEdit`: (string) Template name to use for the file edit form
+ - `setUploadTemplateName`: (string) javascript template used to display uploading
+ files, see javascript/UploadField_uploadtemplate.js
+ - `setCanPreviewFolder`: (boolean|string) Is the upload folder visible to uploading users?
+ String values are interpreted as permission codes.
+
+Certain default values for the above can be configured using the YAML config system.
+
+ :::yaml
+ UploadField:
+ defaultConfig:
+ autoUpload: true
+ allowedMaxFileNumber:
+ canUpload: true
+ canAttachExisting: 'CMS_ACCESS_AssetAdmin'
+ canPreviewFolder: true
+ previewMaxWidth: 80
+ previewMaxHeight: 60
+ uploadTemplateName: 'ss-uploadfield-uploadtemplate'
+ downloadTemplateName: 'ss-uploadfield-downloadtemplate'
+ overwriteWarning: true # Warning before overwriting existing file (only relevant when Upload: replaceFile is true)
+
+The above settings can also be set on a per-instance basis by using `setConfig` with the appropriate key.
You can also configure the underlying `[api:Upload]` class, by using the YAML config system.
:::yaml
Upload:
# Globally disables automatic renaming of files and displays a warning before overwriting an existing file
replaceFile: true
+ uploads_folder: 'Uploads'
+
+## Using the UploadField in a frontend form
+
+The UploadField can be used in a frontend form, given that sufficient attention is given
+to the permissions granted to non-authorised users.
+
+By default Image::canDelete and Image::canEdit do not require admin privileges, so
+make sure you override the methods in your Image extension class.
+
+For instance, to generate an upload form suitable for saving images into a user-defined
+gallery the below code could be used:
+
+ :::php
+
+ // In GalleryPage.php
+ class GalleryPage extends Page {}
+ class GalleryPage_Controller extends Page_Controller {
+ public function Form() {
+ $fields = new FieldList(
+ new TextField('Title', 'Title', null, 255),
+ $field = new UploadField('Images', 'Upload Images')
+ );
+ $field->setCanAttachExisting(false); // Block access to Silverstripe assets library
+ $field->setCanPreviewFolder(false); // Don't show target filesystem folder on upload field
+ $field->relationAutoSetting = false; // Prevents the form thinking the GalleryPage is the underlying object
+ $actions = new FieldList(new FormAction('submit', 'Save Images'));
+ return new Form($this, 'Form', $fields, $actions, null);
+ }
-## TODO: Using the UploadField in a frontend form
+ public function submit($data, Form $form) {
+ $gallery = new Gallery();
+ $form->saveInto($gallery);
+ $gallery->write();
+ return $this;
+ }
+ }
+
+ // In Gallery.php
+ class Gallery extends DataObject {
+ private static $db = array(
+ 'Title' => 'Varchar(255)'
+ );
+
+ private static $many_many = array(
+ 'Images' => 'Image'
+ );
+ }
-*At this moment the UploadField not yet fully supports being used on a frontend
-form.*
+ // In ImageExtension.php
+ class ImageExtension extends DataExtension {
+
+ private static $belongs_many_many = array(
+ 'Gallery' => 'Gallery'
+ );
+
+ function canEdit($member) {
+ // This part is important!
+ return Permission::check('ADMIN');
+ }
+ }
+ Image::add_extension('ImageExtension');
View
8 filesystem/File.php
@@ -340,10 +340,10 @@ public function getCMSFields() {
}
// Upload
- $uploadField = new UploadField('UploadField','Upload Field');
- $uploadField->setConfig('previewMaxWidth', 40);
- $uploadField->setConfig('previewMaxHeight', 30);
- $uploadField->setConfig('allowedMaxFileNumber', 1);
+ $uploadField = UploadField::create('UploadField','Upload Field')
+ ->setPreviewMaxWidth(40)
+ ->setPreviewMaxHeight(30)
+ ->setAllowedMaxFileNumber(1);
//$uploadField->setTemplate('FileEditUploadField');
if ($this->ParentID) {
$parent = $this->Parent();
View
13 filesystem/Upload.php
@@ -28,12 +28,14 @@ class Upload extends Controller {
/**
* A File object
+ *
* @var File
*/
protected $file;
/**
- * An instance of Upload_Validator
+ * Validator for this upload field
+ *
* @var Upload_Validator
*/
protected $validator;
@@ -48,7 +50,8 @@ class Upload extends Controller {
/**
* Replace an existing file rather than renaming the new one.
- * @var Boolean
+ *
+ * @var boolean
*/
protected $replaceFile;
@@ -72,13 +75,13 @@ class Upload extends Controller {
public function __construct() {
parent::__construct();
$this->validator = new Upload_Validator();
- $this->replaceFile = $this->config()->replaceFile;
+ $this->replaceFile = self::config()->replaceFile;
}
/**
* Get current validator
*
- * @return object $validator
+ * @return Upload_Validator $validator
*/
public function getValidator() {
return $this->validator;
@@ -277,7 +280,7 @@ public function isError() {
* @return array
*/
public function getErrors() {
- return $this->errors;
+ return $this->errors;
}
}
View
19 forms/FileField.php
@@ -132,7 +132,7 @@ public function Value() {
/**
* Get custom validator for this field
*
- * @param object $validator
+ * @param Upload_Validator $validator
*/
public function getValidator() {
return $this->upload->getValidator();
@@ -141,22 +141,28 @@ public function getValidator() {
/**
* Set custom validator for this field
*
- * @param object $validator
+ * @param Upload_Validator $validator
+ * @return FileField Self reference
*/
public function setValidator($validator) {
$this->upload->setValidator($validator);
return $this;
}
/**
+ * Sets the upload folder name
+ *
* @param string $folderName
+ * @return FileField Self reference
*/
public function setFolderName($folderName) {
$this->folderName = $folderName;
return $this;
}
/**
+ * Gets the upload folder name
+ *
* @return string
*/
public function getFolderName() {
@@ -181,14 +187,23 @@ public function validate($validator) {
}
/**
+ * Retrieves the Upload handler
+ *
* @return Upload
*/
public function getUpload() {
return $this->upload;
}
+ /**
+ * Sets the upload handler
+ *
+ * @param Upload $upload
+ * @return FileField Self reference
+ */
public function setUpload(Upload $upload) {
$this->upload = $upload;
+ return $this;
}
}
View
6 forms/FormField.php
@@ -208,6 +208,8 @@ public function Value() {
/**
* Method to save this form field into the given data object.
* By default, makes use of $this->dataValue()
+ *
+ * @param DataObjectInterface $record DataObject to save data into
*/
public function saveInto(DataObjectInterface $record) {
if($this->name) {
@@ -403,7 +405,9 @@ public function attrValue() {
/**
* Set the field value.
- * Returns $this.
+ *
+ * @param mixed $value
+ * @return FormField Self reference
*/
public function setValue($value) {
$this->value = $value;
View
1,326 forms/UploadField.php
@@ -2,24 +2,23 @@
/**
* Field for uploading single or multiple files of all types, including images.
- * <b>NOTE: this Field will call write() on the supplied record</b>
*
* <b>Features (some might not be available to old browsers):</b>
*
* - File Drag&Drop support
* - Progressbar
* - Image thumbnail/file icons even before upload finished
- * - Saving into relations
+ * - Saving into relations on form submit
* - Edit file
* - allowedExtensions is by default File::$allowed_extensions<li>maxFileSize the value of min(upload_max_filesize,
* post_max_size) from php.ini
*
* <>Usage</b>
*
* @example <code>
- * $UploadField = new UploadField('myFiles', 'Please upload some images <span>(max. 5 files)</span>');
- * $UploadField->getValidator()->setAllowedExtensions(array('jpg', 'jpeg', 'png', 'gif'));
- * $UploadField->setConfig('allowedMaxFileNumber', 5);
+ * $UploadField = new UploadField('AttachedImages', 'Please upload some images <span>(max. 5 files)</span>');
+ * $UploadField->setAllowedFileCategories('image');
+ * $UploadField->setAllowedMaxFileNumber(5);
* </code>
*
* @author Zauberfisch
@@ -36,6 +35,7 @@ class UploadField extends FileField {
'attach',
'handleItem',
'handleSelect',
+ 'fileexists'
);
/**
@@ -48,119 +48,154 @@ class UploadField extends FileField {
);
/**
- * @var String
+ * Template to use for the file button widget
+ *
+ * @var string
*/
protected $templateFileButtons = 'UploadField_FileButtons';
/**
- * @var String
+ * Template to use for the edit form
+ *
+ * @var string
*/
protected $templateFileEdit = 'UploadField_FileEdit';
/**
+ * Parent data record. Will be infered from parent form or controller if blank.
+ *
* @var DataObject
*/
protected $record;
/**
+ * Items loaded into this field. May be a RelationList, or any other SS_List
+ *
* @var SS_List
*/
protected $items;
/**
- * @var array Config for this field used in both, php and javascript
+ * Config for this field used in the front-end javascript
* (will be merged into the config of the javascript file upload plugin).
* See framework/_config/uploadfield.yml for configuration defaults and documentation.
+ *
+ * @var array
*/
protected $ufConfig = array(
/**
+ * Automatically upload the file once selected
+ *
* @var boolean
*/
'autoUpload' => true,
/**
- * php validation of allowedMaxFileNumber only works when a db relation is available, set to null to allow
- * unlimited if record has a has_one and allowedMaxFileNumber is null, it will be set to 1
- * @var int
+ * 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.
+ * The resulting value will be set to maxNumberOfFiles
+ *
+ * @var integer
*/
'allowedMaxFileNumber' => null,
/**
- * @var boolean|string 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.
+ *
+ * @var boolean|string
*/
'canUpload' => true,
/**
- * @var boolean|string 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.
+ *
+ * @var boolean|string
*/
'canAttachExisting' => "CMS_ACCESS_AssetAdmin",
/**
- * @var boolean 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.
+ *
+ * @var boolean|string
*/
'canPreviewFolder' => true,
/**
- * @var boolean If a second file is uploaded, should it replace the existing one rather than throwing an errror?
- * This only applies for has_one relationships, and only replaces the association
- * rather than the actual file database record or filesystem entry.
- */
- 'replaceExistingFile' => false,
- /**
- * @var int
+ * Maximum width of the preview thumbnail
+ *
+ * @var integer
*/
'previewMaxWidth' => 80,
/**
- * @var int
+ * Maximum height of the preview thumbnail
+ *
+ * @var integer
*/
'previewMaxHeight' => 60,
/**
* javascript template used to display uploading files
+ *
* @see javascript/UploadField_uploadtemplate.js
* @var string
*/
'uploadTemplateName' => 'ss-uploadfield-uploadtemplate',
/**
* javascript template used to display already uploaded files
+ *
* @see javascript/UploadField_downloadtemplate.js
* @var string
*/
'downloadTemplateName' => 'ss-uploadfield-downloadtemplate',
/**
- * FieldList $fields or string $name (of a method on File to provide a fields) for the EditForm
- * @example 'getCMSFields'
- * @var FieldList|string
- */
- 'fileEditFields' => null,
- /**
- * FieldList $actions or string $name (of a method on File to provide a actions) for the EditForm
- * @example 'getCMSActions'
- * @var FieldList|string
- */
- 'fileEditActions' => null,
- /**
- * Validator (eg RequiredFields) or string $name (of a method on File to provide a Validator) for the EditForm
- * @example 'getCMSValidator'
- * @var string
- */
- 'fileEditValidator' => null,
- /**
- * 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
+ * files will be renamed instead of overwritten (although the warning will
+ * still be displayed)
+ *
+ * @see Upload
+ * @var boolean
*/
- 'overwriteWarning' => true,
+ 'overwriteWarning' => true
);
/**
+ * FieldList $fields or string $name (of a method on File to provide a fields) for the EditForm
+ * @example 'getCMSFields'
+ *
+ * @var FieldList|string
+ */
+ protected $fileEditFields = null;
+
+ /**
+ * FieldList $actions or string $name (of a method on File to provide a actions) for the EditForm
+ * @example 'getCMSActions'
+ *
+ * @var FieldList|string
+ */
+ protected $fileEditActions = null;
+
+ /**
+ * Validator (eg RequiredFields) or string $name (of a method on File to provide a Validator) for the EditForm
+ * @example 'getCMSValidator'
+ *
+ * @var RequiredFields|string
+ */
+ protected $fileEditValidator = null;
+
+ /**
+ * Construct a new UploadField instance
+ *
* @param string $name The internal field name, passed to forms.
* @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
* @link $record}, with the same name as the field name.
* @param Form $form Reference to the container form
*/
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
$this->addExtraClass('ss-uploadfield'); // class, used by css for uploadfield only
- $this->ufConfig = array_merge($this->ufConfig, Config::inst()->get('UploadField', 'defaultConfig'));
+ $this->ufConfig = array_merge($this->ufConfig, self::config()->defaultConfig);
parent::__construct($name, $title);
@@ -170,23 +205,25 @@ public function __construct($name, $title = null, SS_List $items = null) {
$this->getValidator()->setAllowedExtensions(
array_filter(Config::inst()->get('File', 'allowed_extensions'))
);
+
// get the lower max size
- $this->getValidator()->setAllowedMaxFileSize(min(File::ini2bytes(ini_get('upload_max_filesize')),
- File::ini2bytes(ini_get('post_max_size'))));
+ $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
+ * @param string
*/
public function setTemplateFileButtons($template) {
$this->templateFileButtons = $template;
return $this;
}
/**
- * @return String
+ * @return string
*/
public function getTemplateFileButtons() {
return $this->templateFileButtons;
@@ -195,21 +232,69 @@ public function getTemplateFileButtons() {
/**
* Set name of template used for the edit (inline & popup) of a file file (without path or extension)
*
- * @param String
+ * @param string
*/
public function setTemplateFileEdit($template) {
$this->templateFileEdit = $template;
return $this;
}
/**
- * @return String
+ * @return string
*/
public function getTemplateFileEdit() {
return $this->templateFileEdit;
}
/**
+ * Determine if the target folder for new uploads in is visible the field UI.
+ *
+ * @return boolean
+ */
+ public function canPreviewFolder() {
+ if(!$this->isActive()) return false;
+ $can = $this->getConfig('canPreviewFolder');
+ return (is_bool($can)) ? $can : Permission::check($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.
+ *
+ * @param boolean|string $canPreviewFolder Either a boolean flag, or a
+ * required permission code
+ * @return UploadField Self reference
+ */
+ public function setCanPreviewFolder($canPreviewFolder) {
+ return $this->setConfig('canPreviewFolder', $canPreviewFolder);
+ }
+
+ /**
+ * 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)
+ *
+ * @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)
+ *
+ * @param boolean $overwriteWarning
+ * @return UploadField Self reference
+ */
+ public function setOverwriteWarning($overwriteWarning) {
+ return $this->setConfig('overwriteWarning', $overwriteWarning);
+ }
+
+ /**
* Force a record to be used as "Parent" for uploaded Files (eg a Page with a has_one to File)
* @param DataObject $record
*/
@@ -220,71 +305,190 @@ public function setRecord($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
* use Form->getRecord() or Form->Controller()->data()
+ *
* @return DataObject
*/
public function getRecord() {
if (!$this->record && $this->form) {
- if ($this->form->getRecord() && is_a($this->form->getRecord(), 'DataObject')) {
- $this->record = $this->form->getRecord();
- } elseif ($this->form->Controller() && $this->form->Controller()->hasMethod('data')
- && $this->form->Controller()->data() && is_a($this->form->Controller()->data(), 'DataObject')) {
- $this->record = $this->form->Controller()->data();
+ if (($record = $this->form->getRecord()) && ($record instanceof DataObject)) {
+ $this->record = $record;
+ } elseif (($controller = $this->form->Controller())
+ && $controller->hasMethod('data')
+ && ($record = $controller->data())
+ && ($record instanceof DataObject)
+ ) {
+ $this->record = $record;
}
}
return $this->record;
}
+
+ /**
+ * Loads the related record values into this field. UploadField can be uploaded
+ * in one of three ways:
+ *
+ * - By passing in a list of file IDs in the $value parameter (an array with a single
+ * key 'Files', with the value being the actual array of IDs).
+ * - By passing in an explicit list of File objects in the $record parameter, and
+ * leaving $value blank.
+ * - By passing in a dataobject in the $record parameter, from which file objects
+ * will be extracting using the field name as the relation field.
+ *
+ * Each of these methods will update both the items (list of File objects) and the
+ * field value (list of file ID values).
+ *
+ * @param array $value Array of submitted form data, if submitting from a form
+ * @param array|DataObject|SS_List $record Full source record, either as a DataObject,
+ * SS_List of items, or an array of submitted form data
+ * @return UploadField Self reference
+ */
+ public function setValue($value, $record = null) {
+
+ // If we're not passed a value directly, we can attempt to infer the field
+ // value from the second parameter by inspecting its relations
+ $items = new ArrayList();
+
+ // Determine format of presented data
+ if(empty($value) && $record) {
+ // If a record is given as a second parameter, but no submitted values,
+ // then we should inspect this instead for the form values
+
+ if(($record instanceof DataObject) && $record->hasMethod($this->getName())) {
+ // If given a dataobject use reflection to extract details
+
+ $data = $record->{$this->getName()}();
+ if($data instanceof DataObject) {
+ // If has_one, add sole item to default list
+ $items->push($data);
+ } elseif($data instanceof SS_List) {
+ // For many_many and has_many relations we can use the relation list directly
+ $items = $data;
+ }
+ } elseif($record instanceof SS_List) {
+ // If directly passing a list then save the items directly
+ $items = $record;
+ }
+ } elseif(!empty($value['Files'])) {
+ // If value is given as an array (such as a posted form), extract File IDs from this
+ $class = $this->getRelationAutosetClass();
+ $items = DataObject::get($class)->byIDs($value['Files']);
+ }
+
+ // If javascript is disabled, direct file upload (non-html5 style) can
+ // trigger a single or multiple file submission. Note that this may be
+ // included in addition to re-submitted File IDs as above, so these
+ // should be added to the list instead of operated on independently.
+ if($uploadedFiles = $this->extractUploadedFileData($value)) {
+ foreach($uploadedFiles as $tempFile) {
+ $file = $this->saveTemporaryFile($tempFile, $error);
+ if($file) {
+ $items->add($file);
+ } else {
+ throw new ValidationException($error);
+ }
+ }
+ }
+
+ // Filter items by what's allowed to be viewed
+ $filteredItems = new ArrayList();
+ $fileIDs = array();
+ foreach($items as $file) {
+ if($file->exists() && $file->canView()) {
+ $filteredItems->push($file);
+ $fileIDs[] = $file->ID;
+ }
+ }
+
+ // Filter and cache updated item list
+ $this->items = $filteredItems;
+ // Same format as posted form values for this field. Also ensures that
+ // $this->setValue($this->getValue()); is non-destructive
+ $value = $fileIDs ? array('Files' => $fileIDs) : null;
+
+ // Set value using parent
+ return parent::setValue($value, $record);
+ }
/**
+ * Sets the items assigned to this field as an SS_List of File objects.
+ * Calling setItems will also update the value of this field, as well as
+ * updating the internal list of File items.
+ *
* @param SS_List $items
+ * @return UploadField self reference
*/
public function setItems(SS_List $items) {
- $this->items = $items;
- return $this;
+ return $this->setValue(null, $items);
}
/**
+ * Retrieves the current list of files
+ *
* @return SS_List
*/
public function getItems() {
- $name = $this->getName();
- if (!$this->items || !$this->items->exists()) {
- $record = $this->getRecord();
- $this->items = array();
- // Try to auto-detect relationship
- if ($record && $record->exists()) {
- if ($record->has_many($name) || $record->many_many($name)) {
- // Ensure relationship is cast to an array, as we can't alter the items of a DataList/RelationList
- // (see below)
- $this->items = $record->{$name}()->toArray();
- } elseif($record->has_one($name)) {
- $item = $record->{$name}();
- if ($item && $item->exists())
- $this->items = array($record->{$name}());
- }
- }
- $this->items = new ArrayList($this->items);
- // hack to provide $UploadFieldThumbnailURL, $hasRelation and $UploadFieldEditLink in template for each
- // file
- if ($this->items->exists()) {
- foreach ($this->items as $i=>$file) {
- $this->items[$i] = $this->customiseFile($file);
- if(!$file->canView()) unset($this->items[$i]); // Respect model permissions
- }
- }
+ return $this->items ? $this->items : new ArrayList();
+ }
+
+ /**
+ * Retrieves a customised list of all File records to ensure they are
+ * properly viewable when rendered in the field template.
+ *
+ * @return SS_List[ViewableData_Customised]
+ */
+ public function getCustomisedItems() {
+ $customised = new ArrayList();
+ foreach($this->getItems() as $file) {
+ $customised->push($this->customiseFile($file));
}
- return $this->items;
+ return $customised;
+ }
+
+ /**
+ * Retrieves the list of selected file IDs
+ *
+ * @return array
+ */
+ public function getItemIDs() {
+ $value = $this->Value();
+ return empty($value['Files']) ? array() : $value['Files'];
+ }
+
+ public function Value() {
+ // Re-override FileField Value to use data value
+ return $this->dataValue();
+ }
+
+ public function saveInto(DataObjectInterface $record) {
+ // Check required relation details are available
+ $fieldname = $this->getName();
+ if(!$fieldname) return $this;
+
+ // Get details to save
+ $idList = $this->getItemIDs();
+
+ // Check type of relation
+ $relation = $record->hasMethod($fieldname) ? $record->$fieldname() : null;
+ if($relation && ($relation instanceof RelationList || $relation instanceof UnsavedRelationList)) {
+ // has_many or many_many
+ $relation->setByIDList($idList);
+ } elseif($record->has_one($fieldname)) {
+ // has_one
+ $record->{"{$fieldname}ID"} = $idList ? reset($idList) : 0;
+ }
+ return $this;
}
/**
- * Hack to add some Variables and a dynamic template to a File
+ * Customises a file with additional details suitable for rendering in the
+ * UploadField.ss template
+ *
* @param File $file
* @return ViewableData_Customised
*/
protected function customiseFile(File $file) {
$file = $file->customise(array(
- 'UploadFieldHasRelation' => $this->managesRelation(),
'UploadFieldThumbnailURL' => $this->getThumbnailURLForFile($file),
- 'UploadFieldRemoveLink' => $this->getItemHandler($file->ID)->RemoveLink(),
'UploadFieldDeleteLink' => $this->getItemHandler($file->ID)->DeleteLink(),
'UploadFieldEditLink' => $this->getItemHandler($file->ID)->EditLink(),
'UploadField' => $this
@@ -296,44 +500,409 @@ protected function customiseFile(File $file) {
}
/**
+ * Assign a front-end config variable for the upload field
+ *
* @param string $key
* @param mixed $val
+ * @return UploadField self reference
*/
public function setConfig($key, $val) {
+ if(!array_key_exists($key, $this->ufConfig)) {
+ user_error("UploadField->setConfig called with invalid option: '$key'", E_USER_ERROR);
+ }
$this->ufConfig[$key] = $val;
return $this;
}
/**
+ * Gets a front-end config variable for the upload field
+ *
* @param string $key
* @return mixed
*/
public function getConfig($key) {
+ if(!array_key_exists($key, $this->ufConfig)) {
+ user_error("UploadField->getConfig called with invalid option: '$key'", E_USER_ERROR);
+ }
return $this->ufConfig[$key];
}
-
+
/**
- * Used to get config in the template
+ * Determine if the field should automatically upload the file.
+ *
+ * @return boolean
*/
public function getAutoUpload() {
return $this->getConfig('autoUpload');
}
/**
+ * Determine if the field should automatically upload the file
+ *
+ * @param boolean $autoUpload
+ * @return UploadField Self reference
+ */
+ public function setAutoUpload($autoUpload) {
+ return $this->setConfig('autoUpload', $autoUpload);
+ }
+
+ /**
+ * 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
+ */
+ public function getAllowedMaxFileNumber() {
+ $allowedMaxFileNumber = $this->getConfig('allowedMaxFileNumber');
+
+ // if there is a has_one relation with that name on the record and
+ // allowedMaxFileNumber has not been set, it's wanted to be 1
+ if(empty($allowedMaxFileNumber)) {
+ $record = $this->getRecord();
+ $name = $this->getName();
+ if($record && $record->has_one($name)) {
+ return 1; // Default for has_one
+ } else {
+ return null; // Default for has_many and many_many
+ }
+ } else {
+ return $allowedMaxFileNumber;
+ }
+ }
+
+
+ /**
+ * Limit allowed file extensions. Empty by default, allowing all extensions.
+ * To allow files without an extension, use an empty string.
+ * See {@link File::$allowed_extensions} to get a good standard set of
+ * extensions that are typically not harmful in a webserver context.
+ * See {@link setAllowedMaxFileSize()} to limit file size by extension.
+ *
+ * @param array $rules List of extensions
+ * @return UploadField Self reference
+ */
+ public function setAllowedExtensions($rules) {
+ $this->getValidator()->setAllowedExtensions($rules);
+ return $this;
+ }
+
+ /**
+ * Limit allowed file extensions by specifying categories of file types.
+ * These may be 'image', 'audio', 'mov', 'zip', 'flash', or 'doc'
+ * See {@link File::$allowed_extensions} for details of allowed extensions
+ * for each of these categories
+ *
+ * @param string $category Category name
+ * @param string,... $categories Additional category names
+ * @return UploadField Self reference
+ */
+ public function setAllowedFileCategories($category) {
+ $extensions = array();
+ $knownCategories = File::config()->app_categories;
+
+ // Parse arguments
+ $categories = func_get_args();
+ if(func_num_args() === 1 && is_array(reset($categories))) {
+ $categories = reset($categories);
+ }
+
+ // Merge all categories into list of extensions
+ foreach(array_filter($categories) as $category) {
+ if(isset($knownCategories[$category])) {
+ $extensions = array_merge($extensions, $knownCategories[$category]);
+ } else {
+ user_error("Unknown file category: $category", E_USER_ERROR);
+ }
+ }
+ return $this->setAllowedExtensions($extensions);
+ }
+
+ /**
+ * Returns list of extensions allowed by this field, or an empty array
+ * if there is no restriction
+ *
+ * @return array
+ */
+ public function getAllowedExtensions() {
+ return $this->getValidator()->getAllowedExtensions();
+ }
+
+ /**
+ * Determine maximum number of files allowed to be attached.
+ *
+ * @param integer|null $allowedMaxFileNumber Maximum limit. 0 or null will be treated as unlimited
+ * @return UploadField Self reference
+ */
+ public function setAllowedMaxFileNumber($allowedMaxFileNumber) {
+ return $this->setConfig('allowedMaxFileNumber', $allowedMaxFileNumber);
+ }
+
+ /**
+ * Determine if the user has permission to upload.
+ *
+ * @return boolean
+ */
+ public function canUpload() {
+ if(!$this->isActive()) return false;
+ $can = $this->getConfig('canUpload');
+ return (is_bool($can)) ? $can : Permission::check($can);
+ }
+
+ /**
+ * Specify whether the user can upload files.
+ * String values will be treated as required permission codes
+ *
+ * @param boolean|string $canUpload Either a boolean flag, or a required
+ * permission code
+ * @return UploadField Self reference
+ */
+ public function setCanUpload($canUpload) {
+ return $this->setConfig('canUpload', $canUpload);
+ }
+
+ /**
+ * 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
+ */
+ public function canAttachExisting() {
+ if(!$this->isActive()) return false;
+ $can = $this->getConfig('canAttachExisting');
+ return (is_bool($can)) ? $can : Permission::check($can);
+ }
+
+ /**
+ * Returns true if the field is neither readonly nor disabled
+ *
+ * @return boolean
+ */
+ public function isActive() {
+ return !$this->isDisabled() && !$this->isReadonly();
+ }
+
+ /**
+ * Specify whether the user can attach existing files
+ * String values will be treated as required permission codes
+ *
+ * @param boolean|string $canAttachExisting Either a boolean flag, or a
+ * required permission code
+ * @return UploadField Self reference
+ */
+ public function setCanAttachExisting($canAttachExisting) {
+ return $this->setConfig('canAttachExisting', $canAttachExisting);
+ }
+
+ /**
+ * Gets thumbnail width. Defaults to 80
+ *
+ * @return integer
+ */
+ public function getPreviewMaxWidth() {
+ return $this->getConfig('previewMaxWidth');
+ }
+
+ /**
+ * @see UploadField::getPreviewMaxWidth()
+ *
+ * @param integer $previewMaxWidth
+ * @return UploadField Self reference
+ */
+ public function setPreviewMaxWidth($previewMaxWidth) {
+ return $this->setConfig('previewMaxWidth', $previewMaxWidth);
+ }
+
+ /**
+ * Gets thumbnail height. Defaults to 60
+ *
+ * @return integer
+ */
+ public function getPreviewMaxHeight() {
+ return $this->getConfig('previewMaxHeight');
+ }
+
+ /**
+ * @see UploadField::getPreviewMaxHeight()
+ *
+ * @param integer $previewMaxHeight
+ * @return UploadField Self reference
+ */
+ public function setPreviewMaxHeight($previewMaxHeight) {
+ return $this->setConfig('previewMaxHeight', $previewMaxHeight);
+ }
+
+ /**
+ * javascript template used to display uploading files
+ * Defaults to 'ss-uploadfield-uploadtemplate'
+ *
+ * @see javascript/UploadField_uploadtemplate.js
+ * @var string
+ */
+ public function getUploadTemplateName() {
+ return $this->getConfig('uploadTemplateName');
+ }
+
+ /**
+ * @see UploadField::getUploadTemplateName()
+ *
+ * @param string $uploadTemplateName
+ * @return UploadField Self reference
+ */
+ public function setUploadTemplateName($uploadTemplateName) {
+ return $this->setConfig('uploadTemplateName', $uploadTemplateName);
+ }
+
+ /**
+ * javascript template used to display already uploaded files
+ * Defaults to 'ss-downloadfield-downloadtemplate'
+ *
+ * @see javascript/DownloadField_downloadtemplate.js
+ * @var string
+ */
+ public function getDownloadTemplateName() {
+ return $this->getConfig('downloadTemplateName');
+ }
+
+ /**
+ * @see Uploadfield::getDownloadTemplateName()
+ *
+ * @param string $downloadTemplateName
+ * @return Uploadfield Self reference
+ */
+ public function setDownloadTemplateName($downloadTemplateName) {
+ return $this->setConfig('downloadTemplateName', $downloadTemplateName);
+ }
+
+ /**
+ * FieldList $fields for the EditForm
+ * @example 'getCMSFields'
+ *
+ * @param File $file File context to generate fields for
+ * @return FieldList List of form fields
+ */
+ public function getFileEditFields(File $file) {
+
+ // Empty actions, generate default
+ if(empty($this->fileEditFields)) {
+ $fields = $file->getCMSFields();
+ // Only display main tab, to avoid overly complex interface
+ if($fields->hasTabSet() && ($mainTab = $fields->findOrMakeTab('Root.Main'))) {
+ $fields = $mainTab->Fields();
+ }
+ return $fields;
+ }
+
+ // Fields instance
+ if ($this->fileEditFields instanceof FieldList) return $this->fileEditFields;
+
+ // Method to call on the given file
+ if($file->hasMethod($this->fileEditFields)) {
+ return $file->{$this->fileEditFields}();
+ }
+
+ user_error("Invalid value for UploadField::fileEditFields", E_USER_ERROR);
+ }
+
+ /**
+ * FieldList $fields or string $name (of a method on File to provide a fields) for the EditForm
+ * @example 'getCMSFields'
+ *
+ * @param FieldList|string
+ * @return Uploadfield Self reference
+ */
+ public function setFileEditFields($fileEditFields) {
+ $this->fileEditFields = $fileEditFields;
+ return $this;
+ }
+
+ /**
+ * FieldList $actions or string $name (of a method on File to provide a actions) for the EditForm
+ * @example 'getCMSActions'
+ *
+ * @param File $file File context to generate form actions for
+ * @return FieldList Field list containing FormAction
+ */
+ public function getFileEditActions(File $file) {
+
+ // Empty actions, generate default
+ if(empty($this->fileEditActions)) {
+ $actions = new FieldList($saveAction = new FormAction('doEdit', _t('UploadField.DOEDIT', 'Save')));
+ $saveAction->addExtraClass('ss-ui-action-constructive icon-accept');
+ return $actions;
+ }
+
+ // Actions instance
+ if ($this->fileEditActions instanceof FieldList) return $this->fileEditActions;
+
+ // Method to call on the given file
+ if($file->hasMethod($this->fileEditActions)) {
+ return $file->{$this->fileEditActions}();
+ }
+
+ user_error("Invalid value for UploadField::fileEditActions", E_USER_ERROR);
+ }
+
+ /**
+ * FieldList $actions or string $name (of a method on File to provide a actions) for the EditForm
+ * @example 'getCMSActions'
+ *
+ * @param FieldList|string
+ * @return Uploadfield Self reference
+ */
+ public function setFileEditActions($fileEditActions) {
+ $this->fileEditActions = $fileEditActions;
+ return $this;
+ }
+
+ /**
+ * Determines the validator to use for the edit form
+ * @example 'getCMSValidator'
+ *
+ * @param File $file File context to generate validator from
+ * @return Validator Validator object
+ */
+ public function getFileEditValidator(File $file) {
+ // Empty validator
+ if(empty($this->fileEditValidator)) return null;
+
+ // Validator instance
+ if($this->fileEditValidator instanceof Validator) return $this->fileEditValidator;
+
+ // Method to call on the given file
+ if($file->hasMethod($this->fileEditValidator)) {
+ return $file->{$this->fileEditValidator}();
+ }
+
+ user_error("Invalid value for UploadField::fileEditValidator", E_USER_ERROR);
+ }
+
+ /**
+ * Validator (eg RequiredFields) or string $name (of a method on File to provide a Validator) for the EditForm
+ * @example 'getCMSValidator'
+ *
+ * @param Validator|string
+ * @return Uploadfield Self reference
+ */
+ public function setFileEditValidator($fileEditValidator) {
+ $this->fileEditValidator = $fileEditValidator;
+ return $this;
+ }
+
+ /**
* @param File $file
* @return string
*/
protected function getThumbnailURLForFile(File $file) {
- if ($file && $file->exists() && file_exists(Director::baseFolder() . '/' . $file->getFilename())) {
+ if ($file->exists() && file_exists(Director::baseFolder() . '/' . $file->getFilename())) {
+ $width = $this->getPreviewMaxWidth();
+ $height = $this->getPreviewMaxHeight();
if ($file->hasMethod('getThumbnail')) {
- return $file->getThumbnail($this->getConfig('previewMaxWidth'),
- $this->getConfig('previewMaxHeight'))->getURL();
+ return $file->getThumbnail($width, $height)->getURL();
} elseif ($file->hasMethod('getThumbnailURL')) {
- return $file->getThumbnailURL($this->getConfig('previewMaxWidth'),
- $this->getConfig('previewMaxHeight'));
+ return $file->getThumbnailURL($width, $height);
} elseif ($file->hasMethod('SetRatioSize')) {
- return $file->SetRatioSize($this->getConfig('previewMaxWidth'),
- $this->getConfig('previewMaxHeight'))->getURL();
+ return $file->SetRatioSize($width, $height)->getURL();
} else {
return $file->Icon();
}
@@ -355,26 +924,15 @@ public function extraClass() {
}
public function Field($properties = array()) {
- $record = $this->getRecord();
- $name = $this->getName();
-
- // if there is a has_one relation with that name on the record and
- // allowedMaxFileNumber has not been set, it's wanted to be 1
- if(
- $record && $record->exists()
- && $record->has_one($name) && !$this->getConfig('allowedMaxFileNumber')
- ) {
- $this->setConfig('allowedMaxFileNumber', 1);
- }
-
Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js');
Requirements::javascript(THIRDPARTY_DIR . '/jquery-ui/jquery-ui.js');
Requirements::javascript(THIRDPARTY_DIR . '/jquery-entwine/dist/jquery.entwine-dist.js');
Requirements::javascript(FRAMEWORK_DIR . '/javascript/i18n.js');
Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/javascript/ssui.core.js');
Requirements::combine_files('uploadfield.js', array(
- THIRDPARTY_DIR . '/javascript-templates/tmpl.js',
+ // @todo jquery templates is a project no longer maintained and should be retired at some point.
+ THIRDPARTY_DIR . '/javascript-templates/tmpl.js',
THIRDPARTY_DIR . '/javascript-loadimage/load-image.js',
THIRDPARTY_DIR . '/jquery-fileupload/jquery.iframe-transport.js',
THIRDPARTY_DIR . '/jquery-fileupload/cors/jquery.xdr-transport.js',
@@ -387,69 +945,120 @@ public function Field($properties = array()) {
Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css'); // TODO hmmm, remove it?
Requirements::css(FRAMEWORK_DIR . '/css/UploadField.css');
+ // Calculated config as per jquery.fileupload-ui.js
+ $allowedMaxFileNumber = $this->getAllowedMaxFileNumber();
$config = array(
'url' => $this->Link('upload'),
'urlSelectDialog' => $this->Link('select'),
'urlAttach' => $this->Link('attach'),
+ 'urlFileExists' => $this->link('fileexists'),
'acceptFileTypes' => '.+$',
- 'maxNumberOfFiles' => $this->getConfig('allowedMaxFileNumber')
+ // Fileupload treats maxNumberOfFiles as the max number of _additional_ items allowed
+ 'maxNumberOfFiles' => $allowedMaxFileNumber ? ($allowedMaxFileNumber - count($this->getItemIDs())) : null
);
- if (count($this->getValidator()->getAllowedExtensions())) {
- $allowedExtensions = $this->getValidator()->getAllowedExtensions();
+
+ // Validation: File extensions
+ if ($allowedExtensions = $this->getAllowedExtensions()) {
$config['acceptFileTypes'] = '(\.|\/)(' . implode('|', $allowedExtensions) . ')$';
$config['errorMessages']['acceptFileTypes'] = _t(
'File.INVALIDEXTENSIONSHORT',
'Extension is not allowed'
);
}
- if ($this->getValidator()->getAllowedMaxFileSize()) {
- $config['maxFileSize'] = $this->getValidator()->getAllowedMaxFileSize();
+
+ // Validation: File size
+ if ($allowedMaxFileSize = $this->getValidator()->getAllowedMaxFileSize()) {
+ $config['maxFileSize'] = $allowedMaxFileSize;
$config['errorMessages']['maxFileSize'] = _t(
'File.TOOLARGESHORT',
'Filesize exceeds {size}',
array('size' => File::format_size($config['maxFileSize']))
);
}
- if ($config['maxNumberOfFiles'] > 1) {
- $config['errorMessages']['maxNumberOfFiles'] = _t(
- 'UploadField.MAXNUMBEROFFILESSHORT',
- 'Can only upload {count} files',
- array('count' => $config['maxNumberOfFiles'])
- );
- }
- $configOverwrite = array();
- if (is_numeric($config['maxNumberOfFiles']) && $this->getItems()->count()) {
- $configOverwrite['maxNumberOfFiles'] = $config['maxNumberOfFiles'] - $this->getItems()->count();
+
+ // Validation: Number of files
+ if ($allowedMaxFileNumber) {
+ if($allowedMaxFileNumber > 1) {
+ $config['errorMessages']['maxNumberOfFiles'] = _t(
+ 'UploadField.MAXNUMBEROFFILESSHORT',
+ 'Can only upload {count} files',
+ array('count' => $allowedMaxFileNumber)
+ );
+ } else {
+ $config['errorMessages']['maxNumberOfFiles'] = _t(
+ 'UploadField.MAXNUMBEROFFILESONE',
+ 'Can only upload one file'
+ );
+ }
}
//get all the existing files in the current folder
- if ($this->getConfig('overwriteWarning')) {
- $folder = Folder::find_or_make($this->getFolderName());
- $files = glob( $folder->getFullPath() . '/*' );
- $config['existingFiles'] = array_map("basename", $files);;
-
+ if ($this->getOverwriteWarning()) {
//add overwrite warning error message to the config object sent to Javascript
$config['errorMessages']['overwriteWarning'] =
- _t('UploadField.OVERWRITEWARNING','File with the same name already exists');
+ _t('UploadField.OVERWRITEWARNING', 'File with the same name already exists');
}
- $config = array_merge($config, $this->ufConfig, $configOverwrite);
-
+ $mergedConfig = array_merge($config, $this->ufConfig);
return $this->customise(array(
- 'configString' => str_replace('"', "'", Convert::raw2json($config)),
- 'config' => new ArrayData($config),
- 'multiple' => $config['maxNumberOfFiles'] !== 1,
- 'displayInput' => (!isset($configOverwrite['maxNumberOfFiles']) || $configOverwrite['maxNumberOfFiles'])
+ 'configString' => str_replace('"', "'", Convert::raw2json($mergedConfig)),
+ 'config' => new ArrayData($mergedConfig),
+ 'multiple' => $allowedMaxFileNumber !== 1
))->renderWith($this->getTemplates());
}
/**
* Validation method for this field, called when the entire form is validated
*
- * @param $validator
- * @return Boolean
+ * @param Validator $validator
+ * @return boolean
*/
public function validate($validator) {
+
+ // @todo Test compatibility with RequiredFields
+ $name = $this->getName();
+ $files = $this->getItems();
+
+ // If there are no files then quit
+ if($files->count() == 0) return true;
+
+ // Check max number of files
+ $maxFiles = $this->getAllowedMaxFileNumber();
+ if($maxFiles && ($files->count() > $maxFiles)) {
+ $validator->validationError(
+ $name,
+ _t(
+ 'UploadField.MAXNUMBEROFFILES',
+ 'Max number of {count} file(s) exceeded.',
+ array('count' => $maxFiles)
+ ),
+ "validation"
+ );
+ return false;
+ }
+
+ // Revalidate each file against nested validator
+ $this->upload->clearErrors();
+ foreach($files as $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,
+ 'tmp_name' => null, // Should bypass is_uploaded_file check
+ 'error' => UPLOAD_ERR_OK,
+ );
+ $this->upload->validate($tmpFile);
+ }
+
+ // Check all errors
+ if($errors = $this->upload->getErrors()) {
+ foreach($errors as $error) {
+ $validator->validationError($name, $error, "validation");
+ }
+ return false;
+ }
+
return true;
}
@@ -476,12 +1085,117 @@ public function getItemHandler($itemID) {
public function handleSelect(SS_HTTPRequest $request) {
return UploadField_SelectHandler::create($this, $this->getFolderName());
}
+
+ /**
+ * Given an array of post variables, extract all temporary file data into an array
+ *
+ * @param array $postVars Array of posted form data
+ * @return array List of temporary file data
+ */
+ protected function extractUploadedFileData($postVars) {
+
+ // Note: Format of posted file parameters in php is a feature of using
+ // <input name='{$Name}[Uploads][]' /> for multiple file uploads
+ $tmpFiles = array();
+ if( !empty($postVars['tmp_name'])
+ && is_array($postVars['tmp_name'])
+ && !empty($postVars['tmp_name']['Uploads'])
+ ) {
+ for($i = 0; $i < count($postVars['tmp_name']['Uploads']); $i++) {
+ // Skip if "empty" file
+ if(empty($postVars['tmp_name']['Uploads'][$i])) continue;
+ $tmpFile = array();
+ foreach(array('name', 'type', 'tmp_name', 'error', 'size') as $field) {
+ $tmpFile[$field] = $postVars[$field]['Uploads'][$i];
+ }
+ $tmpFiles[] = $tmpFile;
+ }
+ } elseif(!empty($postVars['tmp_name'])) {
+ // Fallback to allow single file uploads (method used by AssetUploadField)
+ $tmpFiles[] = $postVars;
+ }
+
+ return $tmpFiles;
+ }
+
+ /**
+ * Loads the temporary file data into a File object
+ *
+ * @param array $tmpFile Temporary file data
+ * @param string $error Error message
+ * @return File File object, or null if error
+ */
+ protected function saveTemporaryFile($tmpFile, &$error = null) {
+
+ // Determine container object
+ $error = null;
+ $fileObject = null;
+
+ if (empty($tmpFile)) {
+ $error = _t('UploadField.FIELDNOTSET', 'File information not found');
+ return null;
+ }
+
+ if($tmpFile['error']) {
+ $error = $tmpFile['error'];
+ return null;
+ }
+
+ // Search for relations that can hold the uploaded files, but don't fallback
+ // to default if there is no automatic relation
+ if ($relationClass = $this->getRelationAutosetClass(null)) {
+ // Create new object explicitly. Otherwise rely on Upload::load to choose the class.
+ $fileObject = Object::create($relationClass);
+ }
+
+ // Get the uploaded file into a new file object.
+ try {
+ $this->upload->loadIntoFile($tmpFile, $fileObject, $this->getFolderName());
+ } catch (Exception $e) {
+ // we shouldn't get an error here, but just in case
+ $error = $e->getMessage();
+ return null;
+ }
+
+ // Check if upload field has an error
+ if ($this->upload->isError()) {
+ $error = implode(' ' . PHP_EOL, $this->upload->getErrors());
+ return null;
+ }
+
+ // return file
+ return $this->upload->getFile();
+ }
+
+ /**
+ * Safely encodes the File object with all standard fields required
+ * by the front end
+ *
+ * @param File $file
+ * @return array Array encoded list of file attributes
+ */
+ protected function encodeFileAttributes(File $file) {
+
+ // Collect all output data.
+ $file = $this->customiseFile($file);
+ return array(
+ 'id' => $file->ID,
+ 'name' => $file->Name,
+ 'url' => $file->URL,
+ 'thumbnail_url' => $file->UploadFieldThumbnailURL,
+ 'edit_url' => $file->UploadFieldEditLink,
+ 'size' => $file->AbsoluteSize,
+ 'type' => $file->FileType,
+ 'buttons' => $file->UploadFieldFileButtons,
+ 'fieldname' => $this->getName()
+ );
+ }
/**
* Action to handle upload of a single file
*
* @param SS_HTTPRequest $request
- * @return string json
+ * @return SS_HTTPResponse
*/
public function upload(SS_HTTPRequest $request) {
if($this->isDisabled() || $this->isReadonly() || !$this->canUpload()) {
@@ -491,148 +1205,75 @@ public function upload(SS_HTTPRequest $request) {
// Protect against CSRF on destructive action
$token = $this->getForm()->getSecurityToken();
if(!$token->checkRequest($request)) return $this->httpError(400);
-
- $name = $this->getName();
- $tmpfile = $request->postVar($name);
- $record = $this->getRecord();
- // Check if the file has been uploaded into the temporary storage.
- if (!$tmpfile) {
- $return = array('error' => _t('UploadField.FIELDNOTSET', 'File information not found'));
+ // Get form details
+ $name = $this->getName();
+ $postVars = $request->postVar($name);
+
+ // Save the temporary file into a File object
+ $uploadedFiles = $this->extractUploadedFileData($postVars);
+ $firstFile = reset($uploadedFiles);
+ $file = $this->saveTemporaryFile($firstFile, $error);
+ if(empty($file)) {
+ $return = array('error' => $error);
} else {
- $return = array(
- 'name' => $tmpfile['name'],
- 'size' => $tmpfile['size'],
- 'type' => $tmpfile['type'],
- 'error' => $tmpfile['error']
- );
- }
-
- // Check for constraints on the record to which the file will be attached.
- if (!$return['error'] && $this->relationAutoSetting && $record && $record->exists()) {
- $tooManyFiles = false;
- // Some relationships allow many files to be attached.
- if ($this->getConfig('allowedMaxFileNumber') && ($record->has_many($name) || $record->many_many($name))) {
- if(!$record->isInDB()) $record->write();
- $tooManyFiles = $record->{$name}()->count() >= $this->getConfig('allowedMaxFileNumber');
- // has_one only allows one file at any given time.
- } elseif($record->has_one($name)) {
- // If we're allowed to replace an existing file, clear out the old one
- if($record->$name && $this->getConfig('replaceExistingFile')) {
- $record->$name = null;
- }
- $tooManyFiles = $record->{$name}() && $record->{$name}()->exists();
- }
-
- // Report the constraint violation.
- if ($tooManyFiles) {
- if(!$this->getConfig('allowedMaxFileNumber')) $this->setConfig('allowedMaxFileNumber', 1);
- $return['error'] = _t(
- 'UploadField.MAXNUMBEROFFILES',
- 'Max number of {count} file(s) exceeded.',
- array('count' => $this->getConfig('allowedMaxFileNumber'))
- );
- }
- }
-
- // Process the uploaded file
- if (!$return['error']) {
- $fileObject = null;
-
- if ($this->relationAutoSetting) {
- // Search for relations that can hold the uploaded files.
- if ($relationClass = $this->getRelationAutosetClass()) {
- // Create new object explicitly. Otherwise rely on Upload::load to choose the class.
- $fileObject = Object::create($relationClass);
- }
- }
-
- // Get the uploaded file into a new file object.
- try {
- $this->upload->loadIntoFile($tmpfile, $fileObject, $this->getFolderName());
- } catch (Exception $e) {
- // we shouldn't get an error here, but just in case
- $return['error'] = $e->getMessage();
- }
-
- if (!$return['error']) {
- if ($this->upload->isError()) {
- $return['error'] = implode(' '.PHP_EOL, $this->upload->getErrors());
- } else {
- $file = $this->upload->getFile();
-
- // Attach the file to the related record.
- if ($this->relationAutoSetting) {
- $this->attachFile($file);
- }
-
- // Collect all output data.
- $file = $this->customiseFile($file);
- $return = array_merge($return, array(
- 'id' => $file->ID,
- 'name' => $file->getTitle() . '.' . $file->getExtension(),
- 'url' => $file->getURL(),
- 'thumbnail_url' => $file->UploadFieldThumbnailURL,
- 'edit_url' => $file->UploadFieldEditLink,
- 'size' => $file->getAbsoluteSize(),
- 'buttons' => $file->UploadFieldFileButtons
- ));
- }
- }
+ $return = $this->encodeFileAttributes($file);
}
+
+ // Format response with json
$response = new SS_HTTPResponse(Convert::raw2json(array($return)));
$response->addHeader('Content-Type', 'text/plain');
return $response;
}
/**
- * Add existing {@link File} records to the relationship.
+ * Retrieves details for files that this field wishes to attache to the
+ * client-side form
+ *
+ * @param SS_HTTPRequest $request
+ * @return SS_HTTPResponse
*/
- public function attach($request) {
+ public function attach(SS_HTTPRequest $request) {
if(!$request->isPOST()) return $this->httpError(403);
- if(!$this->managesRelation()) return $this->httpError(403);
if(!$this->canAttachExisting()) return $this->httpError(403);
-
+
+ // Retrieve file attributes required by front end
$return = array();
-
$files = File::get()->byIDs($request->postVar('ids'));
foreach($files as $file) {
- $this->attachFile($file);
- $file = $this->customiseFile($file);
- $return[] = array(
- 'id' => $file->ID,
- 'name' => $file->getTitle() . '.' . $file->getExtension(),
- 'url' => $file->getURL(),
<