Completed
Push — master ( 3a9221...e627cf )
by
unknown
16s
created

UploadField::performDisabledTransformation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 1
eloc 4
nc 1
nop 0
1
<?php
2
3
namespace SilverStripe\AssetAdmin\Forms;
4
5
use SilverStripe\AssetAdmin\Controller\AssetAdmin;
6
use SilverStripe\Assets\File;
7
use SilverStripe\Assets\Folder;
8
use SilverStripe\Control\HTTPRequest;
9
use SilverStripe\Control\HTTPResponse;
10
use SilverStripe\Forms\FileHandleField;
11
use SilverStripe\Forms\FileUploadReceiver;
12
use SilverStripe\Forms\FormField;
13
use SilverStripe\ORM\DataObject;
14
use SilverStripe\ORM\SS_List;
15
16
/**
17
 * Represents a file upload field with ReactJS based frontend.
18
 *
19
 * Allows writing to a parent record with the following relation types:
20
 *   - has_one
21
 *   - has_many
22
 *   - many_many
23
 *
24
 * Additionally supports writing directly to the File table not attached
25
 * to any parent record.
26
 */
27
class UploadField extends FormField implements FileHandleField
28
{
29
    use FileUploadReceiver;
30
31
    /**
32
     * @config
33
     * @var array
34
     */
35
    private static $allowed_actions = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $allowed_actions is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
36
        'upload'
37
    ];
38
39
    /**
40
     * @config
41
     * @var int
42
     */
43
    private static $thumbnail_width = 60;
0 ignored issues
show
Unused Code introduced by
The property $thumbnail_width is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
44
45
    /**
46
     * @config
47
     * @var int
48
     */
49
    private static $thumbnail_height = 60;
0 ignored issues
show
Unused Code introduced by
The property $thumbnail_height is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
50
51
    protected $inputType = 'file';
52
53
    protected $schemaDataType = FormField::SCHEMA_DATA_TYPE_CUSTOM;
54
55
    protected $schemaComponent = 'UploadField';
56
57
    /**
58
     * @var bool|null
59
     */
60
    protected $multiUpload = null;
61
62
    /**
63
     * Create a new file field.
64
     *
65
     * @param string $name The internal field name, passed to forms.
66
     * @param string $title The field label.
67
     * @param SS_List $items Items assigned to this field
68
     */
69
    public function __construct($name, $title = null, SS_List $items = null)
70
    {
71
        $this->constructFileUploadReceiver();
72
73
        // When creating new files, rename on conflict
74
        $this->getUpload()->setReplaceFile(false);
75
76
        parent::__construct($name, $title);
77
        if ($items) {
78
            $this->setItems($items);
79
        }
80
    }
81
82
    public function getSchemaDataDefaults()
83
    {
84
        $defaults = parent::getSchemaDataDefaults();
85
        $uploadLink = $this->Link('upload');
86
        $defaults['data']['createFileEndpoint'] = [
87
            'url' => $uploadLink,
88
            'method' => 'post',
89
            'payloadFormat' => 'urlencoded',
90
        ];
91
        $defaults['data']['multi'] = $this->getIsMultiUpload();
92
        $defaults['data']['parentid'] = $this->getFolderID();
93
        return $defaults;
94
    }
95
96
    /**
97
     * Creates a single file based on a form-urlencoded upload.
98
     *
99
     * @param HTTPRequest $request
100
     * @return HTTPResponse
101
     */
102
    public function upload(HTTPRequest $request)
103
    {
104
        if ($this->isDisabled() || $this->isReadonly()) {
105
            return $this->httpError(403);
106
        }
107
108
        // CSRF check
109
        $token = $this->getForm()->getSecurityToken();
110
        if (!$token->checkRequest($request)) {
111
            return $this->httpError(400);
112
        }
113
114
        $tmpFile = $request->postVar('Upload');
115
        /** @var File $file */
116
        $file = $this->saveTemporaryFile($tmpFile, $error);
117
118
        // Prepare result
119
        if ($error) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $error of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
120
            $result = [
121
                'message' => [
122
                    'type' => 'error',
123
                    'value' => $error,
124
                ]
125
            ];
126
            $this->getUpload()->clearErrors();
127
            return (new HTTPResponse(json_encode($result), 400))
128
                ->addHeader('Content-Type', 'application/json');
129
        }
130
131
        // Return success response
132
        $result = [
133
            AssetAdmin::singleton()->getObjectFromData($file)
134
        ];
135
136
        // Don't discard pre-generated client side canvas thumbnail
137
        if ($result[0]['category'] === 'image') {
138
            unset($result[0]['thumbnail']);
139
        }
140
        $this->getUpload()->clearErrors();
141
        return (new HTTPResponse(json_encode($result)))
142
            ->addHeader('Content-Type', 'application/json');
143
    }
144
145
    /**
146
     * Get ID of target parent folder
147
     *
148
     * @return int
149
     */
150
    protected function getFolderID()
151
    {
152
        $folderName = $this->getFolderName();
153
        if (!$folderName) {
154
            return 0;
155
        }
156
        $folder = Folder::find_or_make($folderName);
157
        return $folder ? $folder->ID : 0;
158
    }
159
160
    public function getSchemaStateDefaults()
161
    {
162
        $state = parent::getSchemaStateDefaults();
163
        $state['data']['files'] = $this->getEncodedItems();
164
        $state['value'] = $this->Value() ?: [ 'Files' => []];
165
        return $state;
166
    }
167
168
    /**
169
     * Encode selected values for react
170
     *
171
     * @return array
172
     */
173
    protected function getEncodedItems()
174
    {
175
        $assetAdmin = AssetAdmin::singleton();
176
        $fileData = [];
177
        foreach ($this->getItems() as $file) {
178
            $fileData[] = $assetAdmin->getObjectFromData($file);
179
        }
180
        return $fileData;
181
    }
182
183
    /**
184
     * Check if allowed to upload more than one file
185
     *
186
     * @return bool
187
     */
188
    public function getIsMultiUpload()
189
    {
190
        if (isset($this->multiUpload)) {
191
            return $this->multiUpload;
192
        }
193
        // Guess from record
194
        $record = $this->getRecord();
195
        $name = $this->getName();
196
197
        // Disabled for has_one components
198
        if ($record && DataObject::getSchema()->hasOneComponent(get_class($record), $name)) {
199
            return false;
200
        }
201
        return true;
202
    }
203
204
    /**
205
     * Set upload type to multi / single
206
     *
207
     * @param $multi
208
     * @return $this
209
     */
210
    public function setIsMultiUpload($multi)
211
    {
212
        $this->multiUpload = $multi;
213
        return $this;
214
    }
215
216
    public function getAttributes()
217
    {
218
        $attributes = array(
219
            'class' => $this->extraClass(),
220
            'type' => 'file',
221
            'multiple' => $this->getIsMultiUpload(),
222
            'id' => $this->ID(),
223
            'data-schema' => json_encode($this->getSchemaData()),
224
            'data-state' => json_encode($this->getSchemaState()),
225
        );
226
227
        $attributes = array_merge($attributes, $this->attributes);
228
229
        $this->extend('updateAttributes', $attributes);
230
231
        return $attributes;
232
    }
233
234
    public function Type()
235
    {
236
        return 'entwine-uploadfield uploadfield';
237
    }
238
239
    public function performReadonlyTransformation()
240
    {
241
        $clone = clone $this;
242
        $clone->setReadonly(true);
243
        return $clone;
244
    }
245
246
    public function performDisabledTransformation()
247
    {
248
        $clone = clone $this;
249
        $clone->setDisabled(true);
250
        return $clone;
251
    }
252
}
253