Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Passed
Push — crud-uploads ( 044930 )
by Pedro
11:38
created

Uploader::fileNameFrom()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 2
eloc 3
c 1
b 0
f 1
nc 2
nop 1
dl 0
loc 7
rs 10
1
<?php
2
3
namespace Backpack\CRUD\app\Library\CrudPanel\Uploads\Uploaders;
4
5
use Backpack\CRUD\app\Library\CrudPanel\CrudColumn;
6
use Backpack\CRUD\app\Library\CrudPanel\CrudField;
7
use Backpack\CRUD\app\Library\CrudPanel\CrudPanelFacade as CRUD;
8
use Backpack\CRUD\app\Library\CrudPanel\Uploads\Interfaces\UploaderInterface;
9
use Backpack\CRUD\app\Library\CrudPanel\Uploads\Traits\HasCrudObjectType;
10
use Backpack\CRUD\app\Library\CrudPanel\Uploads\Traits\HasName;
11
use Closure;
12
use Illuminate\Database\Eloquent\Model;
13
use Illuminate\Http\UploadedFile;
14
use Illuminate\Support\Facades\Storage;
15
use Illuminate\Support\Str;
16
17
abstract class Uploader implements UploaderInterface
18
{
19
    use HasCrudObjectType, HasName;
20
21
    /**
22
     * Indicates the uploaded file should be deleted when entry is deleted
23
     *
24
     * @var bool
25
     */
26
    public $deleteWhenEntryIsDeleted = true;
27
28
    /**
29
     * Indicates if this uploader instance is inside a repeatable container
30
     *
31
     * @var bool
32
     */
33
    public $isRepeatable = false;
34
35
    /**
36
     * When inside a repeatable container, indicates the container name
37
     *
38
     * @var string|null
39
     */
40
    public $repeatableContainerName = null;
41
42
    /**
43
     * Developer provided filename
44
     *
45
     * @var null|string|Closure
46
     */
47
    public $fileName = null;
48
49
    /**
50
     * The disk where upload will be stored. By default `public`
51
     *
52
     * @var string
53
     */
54
    public $disk = 'public';
55
56
    /**
57
     * Indicates if the upload handles multiple files
58
     *
59
     * @var bool
60
     */
61
    public $isMultiple = false;
62
63
    /**
64
     * The class of the entry where uploads will be attached to
65
     *
66
     * @var string
67
     */
68
    public $entryClass;
69
70
    /**
71
     * The path inside the disk to store the uploads
72
     *
73
     * @var string
74
     */
75
    public $path = '';
76
77
    /**
78
     * Should the url to the object be a temporary one (eg: s3)
79
     *
80
     * @var bool
81
     */
82
    public $temporary = false;
83
84
    /**
85
     * When using temporary urls, defines the time that the url
86
     * should be available in minutes.
87
     *
88
     * By default 1 minute
89
     *
90
     * @var int
91
     */
92
    public $expiration = 1;
93
94
    /**
95
     * Indicates if the upload is relative to a relationship field/column
96
     *
97
     * @var bool
98
     */
99
    public $isRelationship = false;
100
101
    public function __construct(array $crudObject, array $configuration)
102
    {
103
        $this->name = $crudObject['name'];
104
        $this->disk = $configuration['disk'] ?? $crudObject['disk'] ?? $this->disk;
105
        $this->temporary = $configuration['temporary'] ?? $this->temporary;
106
        $this->expiration = $configuration['expiration'] ?? $this->expiration;
107
        $this->entryClass = $crudObject['entryClass'];
108
        $this->path = $configuration['path'] ?? $crudObject['prefix'] ?? $this->path;
109
        $this->path = empty($this->path) ? $this->path : Str::of($this->path)->finish('/')->value;
110
        $this->crudObjectType = $crudObject['crudObjectType'];
111
        $this->fileName = $configuration['fileName'] ?? $this->fileName;
112
        $this->deleteWhenEntryIsDeleted = $configuration['whenDelete'] ?? $this->deleteWhenEntryIsDeleted;
113
    }
114
115
    /**
116
     * An abstract function that all uploaders must implement with the saving process.
117
     *
118
     * @param Model $entry
119
     * @param mixed $values
120
     * @return mixed
121
     */
122
    abstract public function save(Model $entry, $values = null);
123
124
    /**
125
     * The function called in the saving event that starts the upload process.
126
     *
127
     * @param Model $entry
128
     * @return Model
129
     */
130
    public function processFileUpload(Model $entry)
131
    {
132
        $entry->{$this->name} = $this->save($entry);
133
134
        return $entry;
135
    }
136
137
    /**
138
     * Return the uploader name
139
     *
140
     * @return string
141
     */
142
    public function getName()
143
    {
144
        return $this->name;
145
    }
146
147
    /**
148
     * Return the uploader disk
149
     *
150
     * @return string
151
     */
152
    public function getDisk()
153
    {
154
        return $this->disk;
155
    }
156
157
    /**
158
     * Return the uploader path
159
     *
160
     * @return string
161
     */
162
    public function getPath()
163
    {
164
        return $this->path;
165
    }
166
167
    /**
168
     * Return the uploader temporary option
169
     *
170
     * @return bool
171
     */
172
    public function getTemporary()
173
    {
174
        return $this->temporary;
175
    }
176
177
    /**
178
     * Return the uploader expiration time in minutes
179
     *
180
     * @return int
181
     */
182
    public function getExpiration()
183
    {
184
        return $this->expiration;
185
    }
186
187
    /**
188
     * The function called in the retrieved event that handles the display of uploaded values
189
     *
190
     * @param Model $entry
191
     * @return Model
192
     */
193
    public function retrieveUploadedFile(Model $entry)
194
    {
195
        $value = $entry->{$this->name};
196
197
        if ($this->isMultiple && ! isset($entry->getCasts()[$this->name]) && is_string($value)) {
198
            $entry->{$this->name} = json_decode($value, true);
199
        } else {
200
            $entry->{$this->name} = Str::after($value, $this->path);
201
        }
202
203
        return $entry;
204
    }
205
206
    /**
207
     * The function called in the deleting event to delete the uploaded files upon entry deletion
208
     *
209
     * @param Model $entry
210
     * @return void
211
     */
212
    public function deleteUploadedFile(Model $entry)
213
    {
214
        $values = $entry->{$this->name};
215
216
        if ($this->isMultiple) {
217
            if (! isset($entry->getCasts()[$this->name]) && is_string($values)) {
218
                $values = json_decode($values, true);
219
            }
220
        } else {
221
            $values = (array) Str::after($values, $this->path);
222
        }
223
224
        foreach ($values as $value) {
225
            Storage::disk($this->disk)->delete($this->path.$value);
226
        }
227
    }
228
229
    /**
230
     * Build an uploader instance.
231
     *
232
     * @param array $crudObject
233
     * @param array $definition
234
     * @return self
235
     */
236
    public static function for(array $crudObject, array $definition)
237
    {
238
        return new static($crudObject, $definition);
239
    }
240
241
    /**
242
     * Set multiple attribute to true in the uploader.
243
     *
244
     * @return self
245
     */
246
    protected function multiple()
247
    {
248
        $this->isMultiple = true;
249
250
        return $this;
251
    }
252
253
    /**
254
     * Set relationship attribute in uploader.
255
     * When true, it also removes the repeatable in case the relationship is handled
256
     * by repeatable interface.
257
     * This is because the uploads are only repeatable on the "main model", but they represent
258
     * one entry per row. (not repeatable in the "relationship model")
259
     *
260
     * @param bool $isRelationship
261
     * @return self
262
     */
263
    public function relationship(bool $isRelationship): self
264
    {
265
        if ($isRelationship) {
266
            $this->isRepeatable = false;
267
        }
268
        $this->isRelationship = $isRelationship;
269
270
        return $this;
271
    }
272
273
    /**
274
     * Set the repeatable attribute to true in the uploader and the
275
     * corresponding container name.
276
     *
277
     * @param string $repeatableContainerName
278
     * @return self
279
     */
280
    public function repeats(string $repeatableContainerName): self
281
    {
282
        $this->isRepeatable = true;
283
284
        $this->repeatableContainerName = $repeatableContainerName;
285
286
        return $this;
287
    }
288
289
    /**
290
     * Repeatable items send _order_ parameter in the request.
291
     * This olds the information for uploads inside repeatable containers.
292
     *
293
     * @return array
294
     */
295
    protected function getFileOrderFromRequest()
296
    {
297
        $items = CRUD::getRequest()->input('_order_'.$this->repeatableContainerName) ?? [];
0 ignored issues
show
Bug introduced by
The method getRequest() does not exist on Backpack\CRUD\app\Librar...udPanel\CrudPanelFacade. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

297
        $items = CRUD::/** @scrutinizer ignore-call */ getRequest()->input('_order_'.$this->repeatableContainerName) ?? [];
Loading history...
298
299
        array_walk($items, function (&$key, $value) {
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

299
        array_walk($items, function (&$key, /** @scrutinizer ignore-unused */ $value) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
300
            $requestValue = $key[$this->name] ?? null;
301
            $key = $this->isMultiple ? (is_string($requestValue) ? explode(',', $requestValue) : $requestValue) : $requestValue;
302
        });
303
304
        return $items;
305
    }
306
307
    /**
308
     * Return a new instance of the entry class for the uploader
309
     *
310
     * @return Model
311
     */
312
    protected function modelInstance()
313
    {
314
        return new $this->entryClass;
315
    }
316
317
    /**
318
     * Return the uploader stored values when in a repeatable container
319
     *
320
     * @param Model $entry
321
     * @return array
322
     */
323
    protected function getPreviousRepeatableValues(Model $entry)
324
    {
325
        $previousValues = json_decode($entry->getOriginal($this->repeatableContainerName), true);
0 ignored issues
show
Bug introduced by
It seems like $entry->getOriginal($thi...epeatableContainerName) can also be of type array; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

325
        $previousValues = json_decode(/** @scrutinizer ignore-type */ $entry->getOriginal($this->repeatableContainerName), true);
Loading history...
326
        if (! empty($previousValues)) {
327
            $previousValues = array_column($previousValues, $this->name);
328
        }
329
330
        return $previousValues ?? [];
331
    }
332
333
    /**
334
     * Return the file extension
335
     *
336
     * @param mixed $file
337
     * @return string
338
     */
339
    protected function getExtensionFromFile($file)
340
    {
341
        return is_a($file, UploadedFile::class, true) ? $file->extension() : Str::after(mime_content_type($file), '/');
342
    }
343
344
    /**
345
     * Return the file name built by Backpack or by the developer in `fileName` configuration.
346
     *
347
     * @param mixed $file
348
     * @return string
349
     */
350
    protected function getFileName($file)
351
    {
352
        if (is_file($file)) {
353
            return Str::of($this->fileNameFrom($file) ?? Str::of($file->getClientOriginalName())->beforeLast('.')->slug()->append('-'.Str::random(4)));
354
        }
355
356
        return Str::of($this->fileNameFrom($file) ?? Str::random(40));
357
    }
358
359
    /**
360
     * Return the complete filename and extension
361
     *
362
     * @param mixed $file
363
     * @return string
364
     */
365
    protected function getFileNameWithExtension($file)
366
    {
367
        if (is_file($file)) {
368
            return $this->getFileName($file).'.'.$this->getExtensionFromFile($file);
369
        }
370
371
        return Str::of($this->fileNameFrom($file) ?? Str::random(40)).'.'.$this->getExtensionFromFile($file);
372
    }
373
374
    /**
375
     * Allow developer to override the default Backpack fileName
376
     *
377
     * @param mixed $file
378
     * @return string|null
379
     */
380
    private function fileNameFrom($file)
381
    {
382
        if (is_callable($this->fileName)) {
383
            return ($this->fileName)($file, $this);
384
        }
385
386
        return $this->fileName;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->fileName also could return the type Closure which is incompatible with the documented return type null|string.
Loading history...
387
    }
388
}
389