Passed
Push — master ( 803230...a7e4c9 )
by Thomas
02:31
created

AbstractUploadField   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 358
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 108
c 1
b 0
f 0
dl 0
loc 358
rs 9.1199
wmc 41

22 Methods

Rating   Name   Duplication   Size   Complexity  
A Type() 0 3 1
A validate() 0 18 3
A getFolderName() 0 5 2
A performReadonlyTransformation() 0 5 1
A setUploadEnabled() 0 4 1
A getFolderID() 0 8 3
A setAllowedMaxFileNumber() 0 5 1
A getSchemaStateDefaults() 0 6 2
A getIsMultiUpload() 0 14 4
A Link() 0 11 2
A setAttachEnabled() 0 4 1
A getSchemaDataDefaults() 0 22 2
A setIsMultiUpload() 0 4 1
A Field() 0 21 6
A performDisabledTransformation() 0 5 1
A getUploadEnabled() 0 3 1
A getAllowedMaxFileSize() 0 3 1
A getAttributes() 0 16 1
A getAttachEnabled() 0 3 1
A isDefaultMaxFileSize() 0 5 2
A getAllowedMaxFileNumber() 0 3 1
A __construct() 0 18 3

How to fix   Complexity   

Complex Class

Complex classes like AbstractUploadField 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.

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 AbstractUploadField, and based on these observations, apply Extract Interface, too.

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