AbstractUploadField::Field()   A
last analyzed

Complexity

Conditions 6
Paths 7

Size

Total Lines 22
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 10
c 1
b 0
f 0
nc 7
nop 1
dl 0
loc 22
rs 9.2222
1
<?php
2
3
namespace LeKoala\FilePond;
4
5
use LogicException;
6
use SilverStripe\Forms\Form;
7
use SilverStripe\ORM\SS_List;
8
use SilverStripe\Assets\Image;
9
use SilverStripe\Assets\Folder;
10
use SilverStripe\ORM\DataObject;
11
use SilverStripe\Forms\FormField;
12
use SilverStripe\Forms\Validator;
13
use SilverStripe\Control\Controller;
14
use SilverStripe\Control\HTTPRequest;
15
use SilverStripe\Control\HTTPResponse;
16
use SilverStripe\Forms\FileHandleField;
17
use SilverStripe\Control\NullHTTPRequest;
18
use SilverStripe\Forms\FileUploadReceiver;
19
use SilverStripe\ORM\FieldType\DBHTMLText;
20
use SilverStripe\AssetAdmin\Forms\UploadField;
0 ignored issues
show
Bug introduced by
The type SilverStripe\AssetAdmin\Forms\UploadField was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
22
/**
23
 * An abstract class that serve as a base to implement dedicated uploaders
24
 *
25
 * This follows roughly the same pattern as the UploadField class
26
 * but does not depends on asset admin
27
 *
28
 * Copy pasted functions that were adapted are using NEW: comments on top of
29
 * the lines that are changed/added
30
 */
31
abstract class AbstractUploadField extends FormField implements FileHandleField
32
{
33
    use FileUploadReceiver;
0 ignored issues
show
introduced by
The trait SilverStripe\Forms\FileUploadReceiver requires some properties which are not provided by LeKoala\FilePond\AbstractUploadField: $ID, $allowed_extensions, $uploads_folder
Loading history...
34
    use ImprovedUploader;
0 ignored issues
show
introduced by
The trait LeKoala\FilePond\ImprovedUploader requires some properties which are not provided by LeKoala\FilePond\AbstractUploadField: $uploads_folder, $image_sizes
Loading history...
35
36
    /**
37
     * Schema needs to be something else than custom otherwise it fails on ajax load because
38
     * we don't have a proper react component
39
     * @var string
40
     */
41
    protected $schemaDataType = FormField::SCHEMA_DATA_TYPE_HIDDEN;
42
43
    /**
44
     * @var string
45
     */
46
    protected $schemaComponent;
47
48
    /**
49
     * Set if uploading new files is enabled.
50
     * If false, only existing files can be selected
51
     *
52
     * @var bool
53
     */
54
    protected $uploadEnabled = true;
55
56
    /**
57
     * Set if selecting existing files is enabled.
58
     * If false, only new files can be selected.
59
     *
60
     * @var bool
61
     */
62
    protected $attachEnabled = true;
63
64
    /**
65
     * The number of files allowed for this field
66
     *
67
     * @var null|int
68
     */
69
    protected $allowedMaxFileNumber = null;
70
71
    /**
72
     * @var string
73
     */
74
    protected $inputType = 'file';
75
76
    /**
77
     * @var bool|null
78
     */
79
    protected $multiUpload = null;
80
81
    /**
82
     * Create a new file field.
83
     *
84
     * @param string $name The internal field name, passed to forms.
85
     * @param string $title The field label.
86
     * @param SS_List $items Items assigned to this field
87
     */
88
    public function __construct($name, $title = null, SS_List $items = null)
89
    {
90
        $this->constructFileUploadReceiver();
91
92
        // NEW : Reset default size to allow our default config to work properly
93
        $this->getUpload()->getValidator()->allowedMaxFileSize = [];
94
95
        // When creating new files, rename on conflict
96
        $this->getUpload()->setReplaceFile(false);
97
98
        parent::__construct($name, $title);
99
        if ($items) {
100
            $this->setItems($items);
101
        }
102
103
        // NEW : Fix null request
104
        if ($this->request instanceof NullHTTPRequest) {
105
            $this->request = Controller::curr()->getRequest();
106
        }
107
    }
108
109
    /**
110
     * @return array<mixed>
111
     */
112
    public function getSchemaDataDefaults()
113
    {
114
        $defaults = parent::getSchemaDataDefaults();
115
116
        /** @var Form|null $form */
117
        $form = $this->form;
118
119
        // NEW : wrap conditionnaly to avoid errors if not linked to a form
120
        if ($form) {
0 ignored issues
show
introduced by
$form is of type SilverStripe\Forms\Form, thus it always evaluated to true.
Loading history...
121
            $uploadLink = $this->Link('upload');
122
            $defaults['data']['createFileEndpoint'] = [
123
                'url' => $uploadLink,
124
                'method' => 'post',
125
                'payloadFormat' => 'urlencoded',
126
            ];
127
        }
128
129
        $defaults['data']['maxFilesize'] = $this->getAllowedMaxFileSize() / 1024 / 1024;
130
        $defaults['data']['maxFiles'] = $this->getAllowedMaxFileNumber();
131
        $defaults['data']['multi'] = $this->getIsMultiUpload();
132
        $defaults['data']['parentid'] = $this->getFolderID();
133
        $defaults['data']['canUpload'] = $this->getUploadEnabled();
134
        $defaults['data']['canAttach'] = $this->getAttachEnabled();
135
136
        return $defaults;
137
    }
138
139
140
    /**
141
     * Handles file uploading
142
     *
143
     * @param HTTPRequest $request
144
     * @return HTTPResponse
145
     */
146
    abstract public function upload(HTTPRequest $request);
147
148
    /**
149
     * Get ID of target parent folder
150
     *
151
     * @return int
152
     */
153
    protected function getFolderID()
154
    {
155
        $folderName = $this->getFolderName();
156
        if (!$folderName) {
157
            return 0;
158
        }
159
        $folder = Folder::find_or_make($folderName);
160
        return $folder ? $folder->ID : 0;
0 ignored issues
show
introduced by
$folder is of type SilverStripe\Assets\Folder, thus it always evaluated to true.
Loading history...
161
    }
162
163
    /**
164
     * @return array<mixed>
165
     */
166
    public function getSchemaStateDefaults()
167
    {
168
        $state = parent::getSchemaStateDefaults();
169
        $state['data']['files'] = $this->getItemIDs();
170
        $state['value'] = $this->Value() ?: ['Files' => []];
171
        return $state;
172
    }
173
174
    /**
175
     * Check if allowed to upload more than one file
176
     *
177
     * @return bool
178
     */
179
    public function getIsMultiUpload()
180
    {
181
        if (isset($this->multiUpload)) {
182
            return $this->multiUpload;
183
        }
184
        // Guess from record
185
        /** @var DataObject|null $record */
186
        $record = $this->getRecord();
187
        $name = $this->getName();
188
189
        // Disabled for has_one components
190
        if ($record && DataObject::getSchema()->hasOneComponent(get_class($record), $name)) {
191
            return false;
192
        }
193
        return true;
194
    }
195
196
    /**
197
     * Set upload type to multiple or single
198
     *
199
     * @param bool $bool True for multiple, false for single
200
     * @return $this
201
     */
202
    public function setIsMultiUpload($bool)
203
    {
204
        $this->multiUpload = $bool;
205
        return $this;
206
    }
207
208
    /**
209
     * Gets the number of files allowed for this field
210
     *
211
     * @return null|int
212
     */
213
    public function getAllowedMaxFileNumber()
214
    {
215
        return $this->allowedMaxFileNumber;
216
    }
217
218
    /**
219
     * Returns the max allowed filesize
220
     *
221
     * @return null|int
222
     */
223
    public function getAllowedMaxFileSize()
224
    {
225
        return $this->getValidator()->getLargestAllowedMaxFileSize();
226
    }
227
228
    /**
229
     * @return boolean
230
     */
231
    public function isDefaultMaxFileSize()
232
    {
233
        // This returns null until getAllowedMaxFileSize is called
234
        $current = $this->getValidator()->getLargestAllowedMaxFileSize();
235
        return $current ? false : true;
236
    }
237
238
    /**
239
     * Sets the number of files allowed for this field
240
     * @param int $count
241
     * @return $this
242
     */
243
    public function setAllowedMaxFileNumber($count)
244
    {
245
        $this->allowedMaxFileNumber = $count;
246
247
        return $this;
248
    }
249
250
    /**
251
     * @return array<string,mixed>
252
     */
253
    public function getAttributes()
254
    {
255
        $attributes = array(
256
            'class' => $this->extraClass(),
257
            'type' => 'file',
258
            'multiple' => $this->getIsMultiUpload(),
259
            'id' => $this->ID(),
260
            'data-schema' => json_encode($this->getSchemaData()),
261
            'data-state' => json_encode($this->getSchemaState()),
262
        );
263
264
        $attributes = array_merge($attributes, $this->attributes);
265
266
        $this->extend('updateAttributes', $attributes);
267
268
        return $attributes;
269
    }
270
271
    /**
272
     * @return string
273
     */
274
    public function Type()
275
    {
276
        return 'file';
277
    }
278
279
    public function performReadonlyTransformation()
280
    {
281
        $clone = clone $this;
282
        $clone->setReadonly(true);
283
        return $clone;
284
    }
285
286
    public function performDisabledTransformation()
287
    {
288
        $clone = clone $this;
289
        $clone->setDisabled(true);
290
        return $clone;
291
    }
292
293
    /**
294
     * Checks if the number of files attached adheres to the $allowedMaxFileNumber defined
295
     *
296
     * @param Validator $validator
297
     * @return bool
298
     */
299
    public function validate($validator)
300
    {
301
        $maxFiles = $this->getAllowedMaxFileNumber();
302
        $count = count($this->getItems());
303
304
        if ($maxFiles < 1 || $count <= $maxFiles) {
305
            return true;
306
        }
307
308
        $validator->validationError($this->getName(), _t(
309
            'FilePondField.ErrorMaxFilesReached',
310
            'You can only upload {count} file.|You can only upload {count} files.',
311
            [
312
                'count' => $maxFiles,
313
            ]
314
        ));
315
316
        return false;
317
    }
318
319
    /**
320
     * Check if uploading files is enabled
321
     *
322
     * @return bool
323
     */
324
    public function getUploadEnabled()
325
    {
326
        return $this->uploadEnabled;
327
    }
328
329
    /**
330
     * Set if uploading files is enabled
331
     *
332
     * @param bool $uploadEnabled
333
     * @return $this
334
     */
335
    public function setUploadEnabled($uploadEnabled)
336
    {
337
        $this->uploadEnabled = $uploadEnabled;
338
        return $this;
339
    }
340
341
    /**
342
     * Check if attaching files is enabled
343
     *
344
     * @return bool
345
     */
346
    public function getAttachEnabled()
347
    {
348
        return $this->attachEnabled;
349
    }
350
351
    /**
352
     * Set if attaching files is enabled
353
     *
354
     * @param bool $attachEnabled
355
     * @return AbstractUploadField
356
     */
357
    public function setAttachEnabled($attachEnabled)
358
    {
359
        $this->attachEnabled = $attachEnabled;
360
        return $this;
361
    }
362
363
    /**
364
     * @param array<mixed> $properties
365
     * @return DBHTMLText
366
     */
367
    public function Field($properties = array())
368
    {
369
        /** @var DataObject|null $record */
370
        $record = $this->getRecord();
371
        if ($record) {
372
            $relation = $record->getRelationClass($this->name);
373
374
            // Make sure images do not accept default stuff
375
            if ($relation == Image::class) {
376
                $allowedExtensions = $this->getAllowedExtensions();
377
                if (in_array('zip', $allowedExtensions)) {
378
                    // Only allow processable file types for images by default
379
                    $this->setAllowedExtensions(['jpg', 'jpeg', 'png']);
380
                }
381
            }
382
383
            // Set a default description if none set
384
            if (!$this->description && static::config()->enable_default_description) {
385
                $this->setDefaultDescription($relation, $record, $this->name);
386
            }
387
        }
388
        return parent::Field($properties);
389
    }
390
391
    /**
392
     * Gets the upload folder name
393
     *
394
     * Replaces method from UploadReceiver to provide a more flexible default
395
     *
396
     * @return string
397
     */
398
    public function getFolderName()
399
    {
400
        /** @var bool $hasFolder */
401
        $hasFolder = ($this->folderName !== false);
402
        return $hasFolder ? $this->folderName : $this->getDefaultFolderName();
0 ignored issues
show
introduced by
The condition $hasFolder is always true.
Loading history...
403
    }
404
405
    /**
406
     * @inheritDoc
407
     */
408
    public function Link($action = null)
409
    {
410
        /** @var Form|null $form */
411
        $form = $this->form;
412
413
        if (!$form) {
0 ignored issues
show
introduced by
$form is of type SilverStripe\Forms\Form, thus it always evaluated to true.
Loading history...
414
            throw new LogicException(
415
                'Field must be associated with a form to call Link(). Please use $field->setForm($form);'
416
            );
417
        }
418
        $name = $this->getSafeName();
419
        $link = Controller::join_links($form->FormAction(), 'field/' . $name, $action);
420
        $this->extend('updateLink', $link, $action);
421
        return $link;
422
    }
423
}
424