Passed
Push — master ( bf7994...5e2021 )
by Nicolaas
02:52
created

AllFilesInfo::setVerbose()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Sunnysideup\AssetsOverview\Files;
4
5
use FilesystemIterator;
6
use RecursiveDirectoryIterator;
7
use RecursiveIteratorIterator;
8
use SilverStripe\Assets\File;
9
use SilverStripe\Core\Config\Config;
10
use SilverStripe\Core\Config\Configurable;
11
use SilverStripe\Core\Injector\Injectable;
12
use SilverStripe\Core\Injector\Injector;
13
use SilverStripe\ORM\ArrayList;
14
use SilverStripe\Versioned\Versioned;
15
use SilverStripe\View\ArrayData;
16
use Sunnysideup\AssetsOverview\Interfaces\FileInfo;
17
use Sunnysideup\AssetsOverview\Traits\Cacher;
18
use Sunnysideup\AssetsOverview\Traits\FilesystemRelatedTraits;
19
use Sunnysideup\Flush\FlushNow;
20
21
class AllFilesInfo implements FileInfo
22
{
23
    use FilesystemRelatedTraits;
24
    use Injectable;
25
    use Configurable;
26
    use Cacher;
27
    use FlushNow;
28
29
    public static function inst(): AllFilesInfo
30
    {
31
        return Injector::inst()
32
            ->get(AllFilesInfo::class, true, [ASSETS_PATH]);
33
    }
34
35
    public function setVerbose(bool $b): static
36
    {
37
        $this->verbose = $b;
38
        return $this;
39
    }
40
41
    private static array $not_real_file_substrings = [
42
        DIRECTORY_SEPARATOR . '_resampled',
43
        DIRECTORY_SEPARATOR . '__',
44
        DIRECTORY_SEPARATOR . '.',
45
        '__Fit',
46
        '__Pad',
47
        '__Fill',
48
        '__Focus',
49
        '__Scale',
50
        '__ResizedImage',
51
    ];
52
53
    protected bool $verbose = false;
54
55
    /**
56
     * @var string
57
     */
58
    protected string $path = '';
59
60
    /**
61
     * @var array
62
     */
63
    protected array $dataStaging = [];
64
65
    /**
66
     * @var array
67
     */
68
    protected array $dataLive = [];
69
70
    /**
71
     * @var array
72
     */
73
    protected array $listOfFiles = [];
74
75
    /**
76
     * @var array
77
     */
78
    protected array $databaseLookupListStaging = [];
79
80
81
    /**
82
     * @var array
83
     */
84
    protected array $databaseLookupListLive = [];
85
86
87
    /**
88
     * @var ArrayList
89
     */
90
    protected ArrayList $filesAsArrayList;
91
92
    /**
93
     * @var ArrayList
94
     */
95
    protected ArrayList $filesAsSortedArrayList;
96
97
    /**
98
     * @var array
99
     */
100
    protected array $availableExtensions = [];
101
102
103
104
    /**
105
     * @var int
106
     */
107
    protected int $totalFileCountRaw = 0;
108
109
110
    /**
111
     * @var int
112
     */
113
    protected int $totalFileCountFiltered = 0;
114
115
    /**
116
     * @var int
117
     */
118
    protected int $totalFileSizeFiltered = 0;
119
120
    /**
121
     * @var int
122
     */
123
    protected int $limit = 1000;
124
125
    /**
126
     * @var int
127
     */
128
    protected int $startLimit = 0;
129
130
    /**
131
     * @var int
132
     */
133
    protected int $endLimit = 0;
134
135
    /**
136
     * @var int
137
     */
138
    protected int $pageNumber = 1;
139
140
    /**
141
     * @var string
142
     */
143
    protected string $sorter = 'byfolder';
144
    protected array $sorters = [];
145
146
    /**
147
     * @var string
148
     */
149
    protected string $filter = '';
150
151
    /**
152
     * @var string
153
     */
154
    protected array $filters = [];
155
156
    /**
157
     * @var string
158
     */
159
    protected string $displayer = 'thumbs';
160
161
    /**
162
     * @var array
163
     */
164
    protected array $allowedExtensions = [];
165
166
167
    public function __construct(?string $path = ASSETS_PATH)
168
    {
169
        $this->path = $path;
170
    }
171
172
    public function getTotalFilesCount(): int
173
    {
174
        return (int) count($this->listOfFiles);
175
    }
176
177
    /**
178
     * does the file exists in the database on staging?
179
     */
180
    public function existsOnStaging(int $id): bool
181
    {
182
        return isset($this->dataStaging[$id]);
183
    }
184
185
    /**
186
     * does the file exists in the database on live?
187
     */
188
    public function existsOnLive(int $id): bool
189
    {
190
        return isset($this->dataLive[$id]);
191
    }
192
193
    /**
194
     * get data from staging database row.
195
     */
196
    public function getAnyData(string $pathFromAssets, ?int $id = 0): array
197
    {
198
        $data = self::getStagingData($pathFromAssets, $id);
0 ignored issues
show
Bug Best Practice introduced by
The method Sunnysideup\AssetsOvervi...sInfo::getStagingData() is not static, but was called statically. ( Ignorable by Annotation )

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

198
        /** @scrutinizer ignore-call */ 
199
        $data = self::getStagingData($pathFromAssets, $id);
Loading history...
199
        if (empty($data)) {
200
            $data = self::getLiveData($pathFromAssets, $id);
0 ignored issues
show
Bug Best Practice introduced by
The method Sunnysideup\AssetsOvervi...ilesInfo::getLiveData() is not static, but was called statically. ( Ignorable by Annotation )

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

200
            /** @scrutinizer ignore-call */ 
201
            $data = self::getLiveData($pathFromAssets, $id);
Loading history...
201
        }
202
203
        return $data;
204
    }
205
206
    /**
207
     * get data from staging database row.
208
     *
209
     * @param string $pathFromAssets from the root of assets
210
     * @param int    $id
211
     */
212
    public function getStagingData(string $pathFromAssets, ?int $id = 0): array
213
    {
214
        if (! $id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type integer|null is loosely compared to false; this is ambiguous if the integer can be 0. 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 integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
215
            $id = $this->databaseLookupListStaging[$pathFromAssets] ?? 0;
216
        }
217
218
        return $this->dataStaging[$id] ?? [];
219
    }
220
221
    /**
222
     * get data from live database row.
223
     *
224
     * @param string $pathFromAssets - full lookup list
225
     */
226
    public function getLiveData(string $pathFromAssets, ?int $id = 0): array
227
    {
228
        if (! $id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type integer|null is loosely compared to false; this is ambiguous if the integer can be 0. 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 integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
229
            $id = $this->databaseLookupListLive[$pathFromAssets] ?? 0;
230
        }
231
232
        return $this->dataLive[$id] ?? [];
233
    }
234
235
    /**
236
     * find a value in a field in staging
237
     * returns ID of row.
238
     *
239
     * @param mixed $value
240
     */
241
    public function findInStagingData(string $fieldName, $value): int
242
    {
243
        return self::findInData($this->dataStaging, $fieldName, $value);
0 ignored issues
show
Bug Best Practice introduced by
The method Sunnysideup\AssetsOvervi...FilesInfo::findInData() is not static, but was called statically. ( Ignorable by Annotation )

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

243
        return self::/** @scrutinizer ignore-call */ findInData($this->dataStaging, $fieldName, $value);
Loading history...
244
    }
245
246
    /**
247
     * find a value in a field in live
248
     * returns ID of row.
249
     *
250
     * @param mixed $value
251
     */
252
    public function findInLiveData(string $fieldName, $value): int
253
    {
254
        return self::findInData($this->dataLive, $fieldName, $value);
0 ignored issues
show
Bug Best Practice introduced by
The method Sunnysideup\AssetsOvervi...FilesInfo::findInData() is not static, but was called statically. ( Ignorable by Annotation )

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

254
        return self::/** @scrutinizer ignore-call */ findInData($this->dataLive, $fieldName, $value);
Loading history...
255
    }
256
257
    public function getTotalFileSizesRaw()
258
    {
259
        $bytestotal = 0;
260
        $absoluteAssetPath = realpath(ASSETS_PATH);
261
        if (false !== $absoluteAssetPath && '' !== $absoluteAssetPath && file_exists($absoluteAssetPath)) {
262
            foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($absoluteAssetPath, FilesystemIterator::SKIP_DOTS)) as $object) {
263
                $bytestotal += $object->getSize();
264
            }
265
        }
266
267
        return $bytestotal;
268
    }
269
270
    public function toArray(): array
271
    {
272
        if ([] === $this->listOfFiles) {
273
            $cachekey = $this->getCacheKey();
274
            if ($this->hasCacheKey($cachekey)) {
275
                $this->listOfFiles = $this->getCacheValue($cachekey);
276
                $this->availableExtensions = $this->getCacheValue($cachekey . 'availableExtensions');
277
                $this->dataStaging = $this->getCacheValue($cachekey . 'dataStaging');
278
                $this->dataLive = $this->getCacheValue($cachekey . 'dataLive');
279
                $this->databaseLookupListStaging = $this->getCacheValue($cachekey . 'databaseLookupStaging');
280
                $this->databaseLookupListLive = $this->getCacheValue($cachekey . 'databaseLookupLive');
281
            } else {
282
                //disk
283
                $diskArray = $this->getArrayOfFilesOnDisk();
284
                foreach ($diskArray as $path) {
285
                    $this->registerFile($path, true);
286
                }
287
288
                //database
289
                $databaseArray = $this->getArrayOfFilesInDatabase();
290
                foreach ($databaseArray as $path) {
291
                    $location = trim(str_replace(ASSETS_PATH, '', $path), '/');
292
                    $this->registerFile($location, false);
293
                }
294
295
                asort($this->listOfFiles);
296
                asort($this->availableExtensions);
297
                $this->setCacheValue($cachekey, $this->listOfFiles);
298
                $this->setCacheValue($cachekey . 'availableExtensions', $this->availableExtensions);
299
                $this->setCacheValue($cachekey . 'dataStaging', $this->dataStaging);
300
                $this->setCacheValue($cachekey . 'dataLive', $this->dataLive);
301
                $this->setCacheValue($cachekey . 'databaseLookupStaging', $this->databaseLookupListStaging);
302
                $this->setCacheValue($cachekey . 'databaseLookupLive', $this->databaseLookupListLive);
303
            }
304
        }
305
306
        return $this->listOfFiles;
307
    }
308
309
    public function getFilesAsArrayList(): ArrayList
310
    {
311
        if (!isset($this->filesAsArrayList)) {
312
            $rawArray = $this->toArray();
313
            //prepare loop
314
            $this->totalFileCountRaw = self::getTotalFilesCount();
0 ignored issues
show
Bug Best Practice introduced by
The method Sunnysideup\AssetsOvervi...o::getTotalFilesCount() is not static, but was called statically. ( Ignorable by Annotation )

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

314
            /** @scrutinizer ignore-call */ 
315
            $this->totalFileCountRaw = self::getTotalFilesCount();
Loading history...
315
            $this->filesAsArrayList = ArrayList::create();
316
            $filterFree = true;
317
            $filterField = null;
318
            $filterValues = [];
319
            if (isset($this->filters[$this->filter])) {
320
                $filterFree = false;
321
                $filterField = $this->filters[$this->filter]['Field'];
322
                $filterValues = $this->filters[$this->filter]['Values'];
323
            }
324
325
            foreach ($rawArray as $location => $fileExists) {
326
                if ($this->isPathWithAllowedExtension($this->allowedExtensions, $location)) {
327
                    $intel = $this->getDataAboutOneFile($location, $fileExists);
328
                    if ($filterFree || in_array($intel[$filterField], $filterValues, 1)) {
329
                        ++$this->totalFileCountFiltered;
330
                        $this->totalFileSizeFiltered += $intel['PathFileSize'];
331
                        $this->filesAsArrayList->push(
332
                            ArrayData::create($intel)
333
                        );
334
                    }
335
                }
336
            }
337
        }
338
339
        return $this->filesAsArrayList;
340
    }
341
342
343
    public function getFilesAsSortedArrayList(): ArrayList
344
    {
345
        if (!isset($this->filesAsSortedArrayList)) {
346
            $sortField = $this->sorters[$this->sorter]['Sort'];
347
            $headerField = $this->sorters[$this->sorter]['Group'];
348
            $this->filesAsSortedArrayList = ArrayList::create();
349
            $this->filesAsArrayList = $this->filesAsArrayList->Sort($sortField);
350
351
            $count = 0;
352
353
            $innerArray = ArrayList::create();
354
            $prevHeader = 'nothing here....';
355
            $newHeader = '';
356
            foreach ($this->filesAsArrayList as $file) {
357
                $newHeader = $file->{$headerField};
358
                if ($newHeader !== $prevHeader) {
359
                    $this->addTofilesAsSortedArrayList(
360
                        $prevHeader, //correct! important ...
361
                        $innerArray
362
                    );
363
                    $prevHeader = $newHeader;
364
                    unset($innerArray);
365
                    $innerArray = ArrayList::create();
366
                }
367
368
                if ($count >= $this->startLimit && $count < $this->endLimit) {
369
                    $innerArray->push($file);
370
                } elseif ($count >= $this->endLimit) {
371
                    break;
372
                }
373
374
                ++$count;
375
            }
376
377
            //last one!
378
            $this->addTofilesAsSortedArrayList(
379
                $newHeader,
380
                $innerArray
381
            );
382
        }
383
384
        return $this->filesAsSortedArrayList;
385
    }
386
387
388
    public function getAvailableExtensions(): array
389
    {
390
        return $this->availableExtensions;
391
    }
392
393
394
    public function getTotalFileCountRaw(): int
395
    {
396
        return $this->totalFileCountRaw;
397
    }
398
399
400
    public function getTotalFileCountFiltered(): int
401
    {
402
        return $this->totalFileCountFiltered;
403
    }
404
405
406
    public function getTotalFileSizeFiltered(): int
407
    {
408
        return $this->totalFileSizeFiltered;
409
    }
410
411
    public function setAvailableExtensions(array $availableExtensions): static
412
    {
413
        $this->availableExtensions = $availableExtensions;
414
        return $this;
415
    }
416
417
    public function setFilters(array $filters): static
418
    {
419
        $this->filters = $filters;
0 ignored issues
show
Documentation Bug introduced by
It seems like $filters of type array is incompatible with the declared type string of property $filters.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
420
        return $this;
421
    }
422
423
    public function setSorters(array $sorters): static
424
    {
425
        $this->sorters = $sorters;
426
        return $this;
427
    }
428
429
    public function setLimit(int $limit): static
430
    {
431
        $this->limit = $limit;
432
        return $this;
433
    }
434
435
    public function setStartLimit(int $startLimit): static
436
    {
437
        $this->startLimit = $startLimit;
438
        return $this;
439
    }
440
441
    public function setEndLimit(int $endLimit): static
442
    {
443
        $this->endLimit = $endLimit;
444
        return $this;
445
    }
446
447
    public function setPageNumber(int $pageNumber): static
448
    {
449
        $this->pageNumber = $pageNumber;
450
        return $this;
451
    }
452
453
    public function setSorter(string $sorter): static
454
    {
455
        $this->sorter = $sorter;
456
        return $this;
457
    }
458
459
    public function setFilter(string $filter): static
460
    {
461
        $this->filter = $filter;
462
        return $this;
463
    }
464
465
    public function setDisplayer(string $displayer): static
466
    {
467
        $this->displayer = $displayer;
468
        return $this;
469
    }
470
471
    public function setAllowedExtensions(array $allowedExtensions): static
472
    {
473
        $this->allowedExtensions = $allowedExtensions;
474
        return $this;
475
    }
476
477
478
    protected function addTofilesAsSortedArrayList(string $header, ArrayList $arrayList)
479
    {
480
        if ($arrayList->exists()) {
481
            $count = $this->filesAsSortedArrayList->count();
482
            $this->filesAsSortedArrayList->push(
483
                ArrayData::create(
484
                    [
485
                        'Number' => $count,
486
                        'SubTitle' => $header,
487
                        'Items' => $arrayList,
488
                    ]
489
                )
490
            );
491
        }
492
    }
493
494
    protected function getDataAboutOneFile(string $location, ?bool $fileExists): array
495
    {
496
        return OneFileInfo::inst($location, $fileExists)
497
            ->toArray();
498
    }
499
500
    /**
501
     * @param string $path - does not have to be full path
502
     */
503
    protected function isPathWithAllowedExtension(array $allowedExtensions, string $path): bool
504
    {
505
        $count = count($allowedExtensions);
506
        if (0 === $count) {
507
            return true;
508
        }
509
510
        $extension = strtolower($this->getExtension($path));
511
        if ($extension === '') {
512
            $extension = 'n/a';
513
        }
514
515
        return in_array($extension, $allowedExtensions, true);
516
    }
517
518
    protected function registerFile($path, ?bool $inFileSystem = true)
519
    {
520
        if ($path) {
521
            if (! isset($this->listOfFiles[$path])) {
522
                $this->listOfFiles[$path] = $inFileSystem;
523
                if ($this->verbose) {
524
                    echo $inFileSystem ? '✓ ' : 'x ';
525
                }
526
527
                $extension = strtolower($this->getExtension($path));
528
                $this->availableExtensions[$extension] = $extension;
529
            }
530
        }
531
    }
532
533
534
    /**
535
     * @param mixed $value
536
     */
537
    protected function findInData(array $data, string $fieldName, $value): int
538
    {
539
        foreach ($data as $id => $row) {
540
            if (isset($row[$fieldName])) {
541
                if ($row[$fieldName] === $value) {
542
                    return (int) $id;
543
                }
544
            }
545
        }
546
547
        return 0;
548
    }
549
550
    protected function isRealFile(string $absolutePath): bool
551
    {
552
        $listOfItemsToSearchFor = Config::inst()->get(self::class, 'not_real_file_substrings');
553
        foreach ($listOfItemsToSearchFor as $test) {
554
            if (strpos($absolutePath, $test)) {
555
                return false;
556
            }
557
        }
558
559
        $fileName = basename($absolutePath);
560
        $isErrorPage = ('error' === substr((string) $fileName, 0, 5) && '.html' === substr((string) $fileName, -5));
561
        return ! $isErrorPage;
562
    }
563
564
    protected function getArrayOfFilesOnDisk(): array
565
    {
566
        $finalArray = [];
567
        $arrayRaw = new RecursiveIteratorIterator(
568
            new RecursiveDirectoryIterator($this->path),
569
            RecursiveIteratorIterator::SELF_FIRST
570
        );
571
        foreach ($arrayRaw as $src) {
572
            $absolutePath = $src->getPathName();
573
            if (false === $this->isRealFile($absolutePath)) {
574
                continue;
575
            }
576
            $location = trim(str_replace(ASSETS_PATH, '', $absolutePath), '/');
577
            $finalArray[$location] = $location;
578
        }
579
580
        return $finalArray;
581
    }
582
583
    /**
584
     *
585
     * returns all the files in the database except for folders.
586
     * @return array
587
     */
588
    protected function getArrayOfFilesInDatabase(): array
589
    {
590
        $finalArray = [];
591
        foreach (['Stage', 'Live'] as $stage) {
592
            Versioned::set_stage($stage);
593
            $files = File::get();
594
            foreach ($files as $file) {
595
                $row = $file->toMap();
596
                $location = trim($file->getFilename(), '/');
597
                if (!$location) {
598
                    $location = $file->generateFilename();
599
                }
600
                if ('Stage' === $stage) {
601
                    $this->dataStaging[$row['ID']] = $row;
602
                    $this->databaseLookupListStaging[$location] = $row['ID'];
603
                } elseif ('Live' === $stage) {
604
                    $this->dataLive[$row['ID']] = $row;
605
                    $this->databaseLookupListLive[$location] = $row['ID'];
606
                } else {
607
                    user_error('Can not find stage');
608
                }
609
610
                $finalArray[$location] = $location;
611
            }
612
        }
613
        Versioned::set_stage('Stage');
614
        return $finalArray;
615
    }
616
617
    //#############################################
618
    // CACHE
619
    //#############################################
620
621
    protected function getCacheKey(): string
622
    {
623
        return 'allfiles';
624
    }
625
}
626