Completed
Pull Request — master (#6325)
by Damian
08:47
created

AssetField   F

Complexity

Total Complexity 84

Size/Duplication

Total Lines 791
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 18

Importance

Changes 0
Metric Value
dl 0
loc 791
rs 1.263
c 0
b 0
f 0
wmc 84
lcom 1
cbo 18

38 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 23 1
A setTemplateFileButtons() 0 5 1
A getTemplateFileButtons() 0 4 1
A canPreviewFolder() 0 11 3
A setCanPreviewFolder() 0 4 1
A setDisplayFolderName() 0 5 1
A getDisplayFolderName() 0 4 1
A setRecord() 0 5 1
B getRecord() 0 11 5
C setValue() 0 35 8
A Value() 0 5 1
A saveInto() 0 16 4
A setConfig() 0 5 1
A getConfig() 0 6 2
A getAutoUpload() 0 4 1
A setAutoUpload() 0 4 1
A canUpload() 0 11 3
A setCanUpload() 0 4 1
A isActive() 0 4 2
A getPreviewMaxWidth() 0 4 1
A setPreviewMaxWidth() 0 4 1
A getPreviewMaxHeight() 0 4 1
A setPreviewMaxHeight() 0 4 1
A getUploadTemplateName() 0 4 1
A setUploadTemplateName() 0 4 1
A getDownloadTemplateName() 0 4 1
A setDownloadTemplateName() 0 4 1
A extraClass() 0 11 3
B Field() 0 40 4
B validate() 0 36 5
B extractUploadedFileData() 0 24 4
B saveTemporaryFile() 0 33 5
A encodeAssetAttributes() 0 22 1
C upload() 0 41 7
A performReadonlyTransformation() 0 7 1
B getRelationAutosetClass() 0 18 5
A getAssetStore() 0 4 1
A getAttributes() 0 7 1

How to fix   Complexity   

Complex Class

Complex classes like AssetField often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AssetField, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace SilverStripe\Forms;
4
5
use SilverStripe\Assets\Storage\AssetContainer;
6
use SilverStripe\Assets\Storage\AssetStore;
7
use SilverStripe\Assets\Storage\DBFile;
8
use SilverStripe\ORM\DataObject;
9
use SilverStripe\ORM\ValidationException;
10
use SilverStripe\ORM\DataObjectInterface;
11
use SilverStripe\Security\Permission;
12
use SilverStripe\Assets\File;
13
use SilverStripe\Core\Convert;
14
use SilverStripe\Core\Injector\Injector;
15
use SilverStripe\Control\HTTPRequest;
16
use SilverStripe\Control\HTTPResponse;
17
use Exception;
18
19
/**
20
 * Field for uploading into a DBFile instance.
21
 *
22
 * This formfield has fewer options than UploadField:
23
 *  - Assets can only be uploaded, not attached from library
24
 *  - Duplicate files will only be renamed, not allowed to overwrite existing references.
25
 *  - Only one file may be attached.
26
 *  - Files can't be edited once uploaded.
27
 *  - Attached files can only be removed, not deleted.
28
 */
29
class AssetField extends FormField
30
{
31
    use UploadReceiver;
32
33
    /**
34
     * @var array
35
     */
36
    private static $allowed_actions = array(
37
        'upload'
38
    );
39
40
    /**
41
     * @var array
42
     */
43
    private static $url_handlers = array(
44
        '$Action!' => '$Action',
45
    );
46
47
    private static $casting = array(
48
        'Value' => 'DBFile',
49
        'UploadFieldThumbnailURL' => 'Varchar'
50
    );
51
52
    /**
53
     * Template to use for the file button widget
54
     *
55
     * @var string
56
     */
57
    protected $templateFileButtons = null;
58
59
    /**
60
     * Parent data record. Will be infered from parent form or controller if blank. The destination
61
     * DBFile should be a property of the name $name on this object.
62
     *
63
     * @var DataObject
64
     */
65
    protected $record;
66
67
    /**
68
     * Config for this field used in the front-end javascript
69
     * (will be merged into the config of the javascript file upload plugin).
70
     *
71
     * @var array
72
     */
73
    protected $ufConfig = array();
74
75
    /**
76
     * Front end config defaults
77
     *
78
     * @config
79
     * @var array
80
     */
81
    private static $defaultConfig = array(
82
        /**
83
         * Automatically upload the file once selected
84
         *
85
         * @var boolean
86
         */
87
        'autoUpload' => true,
88
89
        /**
90
         * Can the user upload new files.
91
         * String values are interpreted as permission codes.
92
         *
93
         * @var boolean|string
94
         */
95
        'canUpload' => true,
96
97
        /**
98
         * Shows the target folder for new uploads in the field UI.
99
         * Disable to keep the internal filesystem structure hidden from users.
100
         *
101
         * @var boolean|string
102
         */
103
        'canPreviewFolder' => true,
104
105
        /**
106
         * Indicate a change event to the containing form if an upload
107
         * or file edit/delete was performed.
108
         *
109
         * @var boolean
110
         */
111
        'changeDetection' => true,
112
113
        /**
114
         * Maximum width of the preview thumbnail
115
         *
116
         * @var integer
117
         */
118
        'previewMaxWidth' => 80,
119
120
        /**
121
         * Maximum height of the preview thumbnail
122
         *
123
         * @var integer
124
         */
125
        'previewMaxHeight' => 60,
126
127
        /**
128
         * javascript template used to display uploading files
129
         *
130
         * @see javascript/UploadField_uploadtemplate.js
131
         * @var string
132
         */
133
        'uploadTemplateName' => 'ss-uploadfield-uploadtemplate',
134
135
        /**
136
         * javascript template used to display already uploaded files
137
         *
138
         * @see javascript/UploadField_downloadtemplate.js
139
         * @var string
140
         */
141
        'downloadTemplateName' => 'ss-uploadfield-downloadtemplate'
142
    );
143
144
    /**
145
     * Folder to display in "Select files" list.
146
     * Defaults to listing all files regardless of folder.
147
     * The folder path should be relative to the webroot.
148
     * See {@link FileField->folderName} to set the upload target instead.
149
     *
150
     * @var string
151
     * @example admin/folder/subfolder
152
     */
153
    protected $displayFolderName;
154
155
    /**
156
     * Construct a new UploadField instance
157
     *
158
     * @param string $name The internal field name, passed to forms.
159
     * @param string $title The field label.
160
     */
161
    public function __construct($name, $title = null)
162
    {
163
        $this->addExtraClass('ss-upload'); // class, used by js
164
        $this->addExtraClass('ss-uploadfield'); // class, used by css for uploadfield only
165
166
        $this->ufConfig = array_merge($this->ufConfig, self::config()->defaultConfig);
167
168
        $this->constructUploadReceiver();
169
        parent::__construct($name, $title);
170
171
        // AssetField always uses rename replacement method
172
        $this->getUpload()->setReplaceFile(false);
173
174
        // filter out '' since this would be a regex problem on JS end
175
        $this->getValidator()->setAllowedExtensions(
176
            array_filter(File::config()->allowed_extensions)
177
        );
178
179
        // get the lower max size
180
        $maxUpload = File::ini2bytes(ini_get('upload_max_filesize'));
181
        $maxPost = File::ini2bytes(ini_get('post_max_size'));
182
        $this->getValidator()->setAllowedMaxFileSize(min($maxUpload, $maxPost));
183
    }
184
185
    /**
186
     * Set name of template used for Buttons on each file (replace, edit, remove, delete) (without path or extension)
187
     *
188
     * @param string
189
     * @return $this
190
     */
191
    public function setTemplateFileButtons($template)
192
    {
193
        $this->templateFileButtons = $template;
194
        return $this;
195
    }
196
197
    /**
198
     * @return string
199
     */
200
    public function getTemplateFileButtons()
201
    {
202
        return $this->_templates($this->templateFileButtons, '_FileButtons');
203
    }
204
205
    /**
206
     * Determine if the target folder for new uploads in is visible the field UI.
207
     *
208
     * @return boolean
209
     */
210
    public function canPreviewFolder()
211
    {
212
        if (!$this->isActive()) {
213
            return false;
214
        }
215
        $can = $this->getConfig('canPreviewFolder');
216
        if (is_bool($can)) {
217
            return $can;
218
        }
219
        return Permission::check($can);
220
    }
221
222
    /**
223
     * Determine if the target folder for new uploads in is visible the field UI.
224
     * Disable to keep the internal filesystem structure hidden from users.
225
     *
226
     * @param boolean|string $canPreviewFolder Either a boolean flag, or a
227
     * required permission code
228
     * @return $this Self reference
229
     */
230
    public function setCanPreviewFolder($canPreviewFolder)
231
    {
232
        return $this->setConfig('canPreviewFolder', $canPreviewFolder);
233
    }
234
235
    /**
236
     * @param string
237
     * @return $this
238
     */
239
    public function setDisplayFolderName($name)
240
    {
241
        $this->displayFolderName = $name;
242
        return $this;
243
    }
244
245
    /**
246
     * @return string
247
     */
248
    public function getDisplayFolderName()
249
    {
250
        return $this->displayFolderName;
251
    }
252
253
    /**
254
     * Force a record to be used as "Parent" for uploaded Files (eg a Page with a has_one to File)
255
     *
256
     * @param DataObject $record
257
     * @return $this
258
     */
259
    public function setRecord($record)
260
    {
261
        $this->record = $record;
262
        return $this;
263
    }
264
265
    /**
266
     * 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
267
     * use Form->getRecord().
268
     *
269
     * @return DataObject
270
     */
271
    public function getRecord()
272
    {
273
        if (!$this->record
274
            && $this->form
275
            && ($record = $this->form->getRecord())
276
            && $record instanceof DataObject
277
        ) {
278
            $this->record = $record;
279
        }
280
        return $this->record;
281
    }
282
283
    public function setValue($value, $record = null)
284
    {
285
        // Extract value from underlying record
286
        if (empty($value) && $this->getName() && $record instanceof DataObject) {
287
            $name = $this->getName();
288
            $value = $record->$name;
289
        }
290
291
        // Convert asset container to tuple value
292
        if ($value instanceof AssetContainer) {
293
            if ($value->exists()) {
294
                $value = array(
295
                    'Filename' => $value->getFilename(),
296
                    'Hash' => $value->getHash(),
297
                    'Variant' => $value->getVariant()
298
                );
299
            } else {
300
                $value = null;
301
            }
302
        }
303
304
        // If javascript is disabled, direct file upload (non-html5 style) can
305
        // trigger a single or multiple file submission. Note that this may be
306
        // included in addition to re-submitted File IDs as above, so these
307
        // should be added to the list instead of operated on independently.
308
        if ($uploadedFile = $this->extractUploadedFileData($value)) {
309
            $value = $this->saveTemporaryFile($uploadedFile, $error);
310
            if (!$value) {
311
                throw new ValidationException($error);
312
            }
313
        }
314
315
        // Set value using parent
316
        return parent::setValue($value, $record);
0 ignored issues
show
Unused Code introduced by
The call to FormField::setValue() has too many arguments starting with $record.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
317
    }
318
319
    public function Value()
320
    {
321
        // Re-override FileField Value to use data value
322
        return $this->dataValue();
323
    }
324
325
    public function saveInto(DataObjectInterface $record)
326
    {
327
        // Check required relation details are available
328
        $name = $this->getName();
329
        if (!$name) {
330
            return $this;
331
        }
332
        $value = $this->Value();
333
        foreach (array('Filename', 'Hash', 'Variant') as $part) {
334
            $partValue = isset($value[$part])
335
                ? $value[$part]
336
                : null;
337
            $record->setField("{$name}{$part}", $partValue);
338
        }
339
        return $this;
340
    }
341
342
    /**
343
     * Assign a front-end config variable for the upload field
344
     *
345
     * @see https://github.com/blueimp/jQuery-File-Upload/wiki/Options for the list of front end options available
346
     *
347
     * @param string $key
348
     * @param mixed $val
349
     * @return $this self reference
350
     */
351
    public function setConfig($key, $val)
352
    {
353
        $this->ufConfig[$key] = $val;
354
        return $this;
355
    }
356
357
    /**
358
     * Gets a front-end config variable for the upload field
359
     *
360
     * @see https://github.com/blueimp/jQuery-File-Upload/wiki/Options for the list of front end options available
361
     *
362
     * @param string $key
363
     * @return mixed
364
     */
365
    public function getConfig($key)
366
    {
367
        if (isset($this->ufConfig[$key])) {
368
            return $this->ufConfig[$key];
369
        }
370
    }
371
372
    /**
373
     * Determine if the field should automatically upload the file.
374
     *
375
     * @return boolean
376
     */
377
    public function getAutoUpload()
378
    {
379
        return $this->getConfig('autoUpload');
380
    }
381
382
    /**
383
     * Determine if the field should automatically upload the file
384
     *
385
     * @param boolean $autoUpload
386
     * @return $this Self reference
387
     */
388
    public function setAutoUpload($autoUpload)
389
    {
390
        return $this->setConfig('autoUpload', $autoUpload);
391
    }
392
393
    /**
394
     * Determine if the user has permission to upload.
395
     *
396
     * @return boolean
397
     */
398
    public function canUpload()
399
    {
400
        if (!$this->isActive()) {
401
            return false;
402
        }
403
        $can = $this->getConfig('canUpload');
404
        if (is_bool($can)) {
405
            return $can;
406
        }
407
        return Permission::check($can);
408
    }
409
410
    /**
411
     * Specify whether the user can upload files.
412
     * String values will be treated as required permission codes
413
     *
414
     * @param bool|string $canUpload Either a boolean flag, or a required
415
     * permission code
416
     * @return $this Self reference
417
     */
418
    public function setCanUpload($canUpload)
419
    {
420
        return $this->setConfig('canUpload', $canUpload);
421
    }
422
423
    /**
424
     * Returns true if the field is neither readonly nor disabled
425
     *
426
     * @return bool
427
     */
428
    public function isActive()
429
    {
430
        return !$this->isDisabled() && !$this->isReadonly();
431
    }
432
433
    /**
434
     * Gets thumbnail width. Defaults to 80
435
     *
436
     * @return int
437
     */
438
    public function getPreviewMaxWidth()
439
    {
440
        return $this->getConfig('previewMaxWidth');
441
    }
442
443
    /**
444
     * Set thumbnail width.
445
     *
446
     * @param int $previewMaxWidth
447
     * @return $this Self reference
448
     */
449
    public function setPreviewMaxWidth($previewMaxWidth)
450
    {
451
        return $this->setConfig('previewMaxWidth', $previewMaxWidth);
452
    }
453
454
    /**
455
     * Gets thumbnail height. Defaults to 60
456
     *
457
     * @return int
458
     */
459
    public function getPreviewMaxHeight()
460
    {
461
        return $this->getConfig('previewMaxHeight');
462
    }
463
464
    /**
465
     * Set thumbnail height.
466
     *
467
     * @param int $previewMaxHeight
468
     * @return $this Self reference
469
     */
470
    public function setPreviewMaxHeight($previewMaxHeight)
471
    {
472
        return $this->setConfig('previewMaxHeight', $previewMaxHeight);
473
    }
474
475
    /**
476
     * javascript template used to display uploading files
477
     * Defaults to 'ss-uploadfield-uploadtemplate'
478
     *
479
     * @see javascript/UploadField_uploadtemplate.js
480
     * @return string
481
     */
482
    public function getUploadTemplateName()
483
    {
484
        return $this->getConfig('uploadTemplateName');
485
    }
486
487
    /**
488
     * Set javascript template used to display uploading files
489
     *
490
     * @param string $uploadTemplateName
491
     * @return $this Self reference
492
     */
493
    public function setUploadTemplateName($uploadTemplateName)
494
    {
495
        return $this->setConfig('uploadTemplateName', $uploadTemplateName);
496
    }
497
498
    /**
499
     * javascript template used to display already uploaded files
500
     * Defaults to 'ss-downloadfield-downloadtemplate'
501
     *
502
     * @see javascript/DownloadField_downloadtemplate.js
503
     * @return string
504
     */
505
    public function getDownloadTemplateName()
506
    {
507
        return $this->getConfig('downloadTemplateName');
508
    }
509
510
    /**
511
     * Set javascript template used to display already uploaded files
512
     *
513
     * @param string $downloadTemplateName
514
     * @return $this Self reference
515
     */
516
    public function setDownloadTemplateName($downloadTemplateName)
517
    {
518
        return $this->setConfig('downloadTemplateName', $downloadTemplateName);
519
    }
520
521
    public function extraClass()
522
    {
523
        if ($this->isDisabled()) {
524
            $this->addExtraClass('disabled');
525
        }
526
        if ($this->isReadonly()) {
527
            $this->addExtraClass('readonly');
528
        }
529
530
        return parent::extraClass();
531
    }
532
533
    public function Field($properties = array())
534
    {
535
        // Calculated config as per jquery.fileupload-ui.js
536
        $config = array(
537
            'allowedMaxFileNumber' => 1, // Only one file allowed for AssetField
538
            'url' => $this->Link('upload'),
539
            'urlSelectDialog' => $this->Link('select'),
540
            'urlAttach' => $this->Link('attach'),
541
            'urlFileExists' => $this->link('fileexists'),
542
            'acceptFileTypes' => '.+$',
543
            // Fileupload treats maxNumberOfFiles as the max number of _additional_ items allowed
544
            'maxNumberOfFiles' => $this->Value() ? 0 : 1,
545
            'replaceFile' => false, // Should always be false for AssetField
546
        );
547
548
        // Validation: File extensions
549
        if ($allowedExtensions = $this->getAllowedExtensions()) {
550
            $config['acceptFileTypes'] = '(\.|\/)(' . implode('|', $allowedExtensions) . ')$';
551
            $config['errorMessages']['acceptFileTypes'] = _t(
552
                'File.INVALIDEXTENSIONSHORT',
553
                'Extension is not allowed'
554
            );
555
        }
556
557
        // Validation: File size
558
        if ($allowedMaxFileSize = $this->getValidator()->getAllowedMaxFileSize()) {
559
            $config['maxFileSize'] = $allowedMaxFileSize;
560
            $config['errorMessages']['maxFileSize'] = _t(
561
                'File.TOOLARGESHORT',
562
                'Filesize exceeds {size}',
563
                array('size' => File::format_size($config['maxFileSize']))
564
            );
565
        }
566
567
        $mergedConfig = array_merge($config, $this->ufConfig);
568
        return $this->customise(array(
569
            'ConfigString' => Convert::raw2json($mergedConfig),
570
            'UploadFieldFileButtons' => $this->renderWith($this->getTemplateFileButtons())
571
        ))->renderWith($this->getTemplates());
572
    }
573
574
    /**
575
     * Validation method for this field, called when the entire form is validated
576
     *
577
     * @param Validator $validator
578
     * @return boolean
579
     */
580
    public function validate($validator)
581
    {
582
        $name = $this->getName();
583
        $value = $this->Value();
584
585
        // If there is no file then quit
586
        if (!$value) {
587
            return true;
588
        }
589
590
        // Revalidate each file against nested validator
591
        $this->getUpload()->clearErrors();
592
593
        // Generate $_FILES style file attribute array for upload validator
594
        $store = $this->getAssetStore();
595
        $mime = $store->getMimeType($value['Filename'], $value['Hash'], $value['Variant']);
596
        $metadata = $store->getMetadata($value['Filename'], $value['Hash'], $value['Variant']);
597
        $tmpFile = array(
598
            'name' => $value['Filename'],
599
            'type' => $mime,
600
            'size' => isset($metadata['size']) ? $metadata['size'] : 0,
601
            'tmp_name' => null, // Should bypass is_uploaded_file check
602
            'error' => UPLOAD_ERR_OK,
603
        );
604
        $this->getUpload()->validate($tmpFile);
605
606
        // Check all errors
607
        if ($errors = $this->getUpload()->getErrors()) {
608
            foreach ($errors as $error) {
609
                $validator->validationError($name, $error, "validation");
610
            }
611
            return false;
612
        }
613
614
        return true;
615
    }
616
617
    /**
618
     * Given an array of post variables, extract all temporary file data into an array
619
     *
620
     * @param array $postVars Array of posted form data
621
     * @return array data for uploaded file
622
     */
623
    protected function extractUploadedFileData($postVars)
624
    {
625
        // Note: Format of posted file parameters in php is a feature of using
626
        // <input name='{$Name}[Upload]' /> for multiple file uploads
627
628
        // Skip empty file
629
        if (empty($postVars['tmp_name'])) {
630
            return null;
631
        }
632
633
        // Return single level array for posted file
634
        /** @skipUpgrade */
635
        if (empty($postVars['tmp_name']['Upload'])) {
636
            return $postVars;
637
        }
638
639
        // Extract posted feedback value
640
        $tmpFile = array();
641
        foreach (array('name', 'type', 'tmp_name', 'error', 'size') as $field) {
642
            /** @skipUpgrade */
643
            $tmpFile[$field] = $postVars[$field]['Upload'];
644
        }
645
        return $tmpFile;
646
    }
647
648
    /**
649
     * Loads the temporary file data into the asset store, and return the tuple details
650
     * for the result.
651
     *
652
     * @param array $tmpFile Temporary file data
653
     * @param string $error Error message
654
     * @return array Result of saved file, or null if error
655
     */
656
    protected function saveTemporaryFile($tmpFile, &$error = null)
657
    {
658
        $error = null;
659
        if (empty($tmpFile)) {
660
            $error = _t('UploadField.FIELDNOTSET', 'File information not found');
661
            return null;
662
        }
663
664
        if ($tmpFile['error']) {
665
            $error = $tmpFile['error'];
666
            return null;
667
        }
668
669
        // Get the uploaded file into a new file object.
670
        try {
671
            $result = $this
672
                ->getUpload()
673
                ->load($tmpFile, $this->getFolderName());
674
        } catch (Exception $e) {
675
            // we shouldn't get an error here, but just in case
676
            $error = $e->getMessage();
677
            return null;
678
        }
679
680
        // Check if upload field has an error
681
        if ($this->getUpload()->isError()) {
682
            $error = implode(' ' . PHP_EOL, $this->getUpload()->getErrors());
683
            return null;
684
        }
685
686
        // return tuple array of Filename, Hash and Variant
687
        return $result;
688
    }
689
690
    /**
691
     * Safely encodes the File object with all standard fields required
692
     * by the front end
693
     *
694
     * @param string $filename
695
     * @param string $hash
696
     * @param string $variant
697
     * @return array Encoded list of file attributes
698
     */
699
    protected function encodeAssetAttributes($filename, $hash, $variant)
700
    {
701
        // Force regeneration of file thumbnail for this tuple (without saving into db)
702
        $object = DBFile::create();
703
        $object->setValue(array('Filename' => $filename, 'Hash' => $hash, 'Variant' => $variant));
704
705
        return array(
706
            'filename' => $filename,
707
            'hash' => $hash,
708
            'variant' => $variant,
709
            'name' => $object->getBasename(),
710
            'url' => $object->getURL(),
711
            'thumbnail_url' => $object->ThumbnailURL(
712
                $this->getPreviewMaxWidth(),
713
                $this->getPreviewMaxHeight()
714
            ),
715
            'size' => $object->getAbsoluteSize(),
716
            'type' => File::get_file_type($object->getFilename()),
717
            'buttons' => (string)$this->renderWith($this->getTemplateFileButtons()),
718
            'fieldname' => $this->getName()
719
        );
720
    }
721
722
    /**
723
     * Action to handle upload of a single file
724
     *
725
     * @param HTTPRequest $request
726
     * @return HTTPResponse
727
     */
728
    public function upload(HTTPRequest $request)
729
    {
730
        if ($this->isDisabled() || $this->isReadonly() || !$this->canUpload()) {
731
            return $this->httpError(403);
732
        }
733
734
        // Protect against CSRF on destructive action
735
        $token = $this
736
            ->getForm()
737
            ->getSecurityToken();
738
        if (!$token->checkRequest($request)) {
739
            return $this->httpError(400);
740
        }
741
742
        // Get form details
743
        $name = $this->getName();
744
        $postVars = $request->postVar($name);
745
746
        // Extract uploaded files from Form data
747
        $uploadedFile = $this->extractUploadedFileData($postVars);
748
        if (!$uploadedFile) {
749
            return $this->httpError(400);
750
        }
751
752
        // Save the temporary files into a File objects
753
        // and save data/error on a per file basis
754
        $result = $this->saveTemporaryFile($uploadedFile, $error);
755
        if (empty($result)) {
756
            $return = array('error' => $error);
757
        } else {
758
            $return = $this->encodeAssetAttributes($result['Filename'], $result['Hash'], $result['Variant']);
759
        }
760
        $this
761
            ->getUpload()
762
            ->clearErrors();
763
764
        // Format response with json
765
        $response = new HTTPResponse(Convert::raw2json(array($return)));
766
        $response->addHeader('Content-Type', 'text/plain');
767
        return $response;
768
    }
769
770
    public function performReadonlyTransformation()
771
    {
772
        $clone = clone $this;
773
        $clone->addExtraClass('readonly');
774
        $clone->setReadonly(true);
775
        return $clone;
776
    }
777
778
    /**
779
     * Gets the foreign class that needs to be created, or 'File' as default if there
780
     * is no relationship, or it cannot be determined.
781
     *
782
     * @param string $default Default value to return if no value could be calculated
783
     * @return string Foreign class name.
784
     */
785
    public function getRelationAutosetClass($default = 'SilverStripe\\Assets\\File')
786
    {
787
788
        // Don't autodetermine relation if no relationship between parent record
789
        if (!$this->relationAutoSetting) {
790
            return $default;
791
        }
792
793
        // Check record and name
794
        $name = $this->getName();
795
        $record = $this->getRecord();
796
        if (empty($name) || empty($record)) {
797
            return $default;
798
        } else {
799
            $class = $record->getRelationClass($name);
800
            return empty($class) ? $default : $class;
801
        }
802
    }
803
804
    /**
805
     * @return AssetStore
806
     */
807
    protected function getAssetStore()
808
    {
809
        return Injector::inst()->get('AssetStore');
810
    }
811
812
    public function getAttributes()
813
    {
814
        return array_merge(
815
            parent::getAttributes(),
816
            ['type' => 'file']
817
        );
818
    }
819
}
820