Completed
Push — master ( e7a0ae...db4c66 )
by Cedric
01:38
created

FlysystemDriver::getFolderInfoByIdentifier()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2.1922

Importance

Changes 5
Bugs 0 Features 2
Metric Value
c 5
b 0
f 2
dl 0
loc 16
ccs 7
cts 11
cp 0.6364
rs 9.4285
cc 2
eloc 10
nc 2
nop 1
crap 2.1922
1
<?php
2
3
namespace CedricZiel\FalFlysystem\Fal;
4
5
/***************************************************************
6
 *  Copyright notice
7
 *
8
 *  (c) 2016 Cedric Ziel <[email protected]>
9
 *
10
 *  All rights reserved
11
 *
12
 *  This script is part of the TYPO3 project. The TYPO3 project is
13
 *  free software; you can redistribute it and/or modify
14
 *  it under the terms of the GNU General Public License as published by
15
 *  the Free Software Foundation; either version 3 of the License, or
16
 *  (at your option) any later version.
17
 *
18
 *  The GNU General Public License can be found at
19
 *  http://www.gnu.org/copyleft/gpl.html.
20
 *
21
 *  This script is distributed in the hope that it will be useful,
22
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 *  GNU General Public License for more details.
25
 *
26
 *  This copyright notice MUST APPEAR in all copies of the script!
27
 ***************************************************************/
28
29
use League\Flysystem\Adapter\Local;
30
use League\Flysystem\AdapterInterface;
31
use League\Flysystem\Config;
32
use League\Flysystem\FilesystemInterface;
33
use TYPO3\CMS\Core\Resource\Driver\AbstractHierarchicalFilesystemDriver;
34
use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException;
35
use TYPO3\CMS\Core\Resource\Exception\FileOperationErrorException;
36
use TYPO3\CMS\Core\Resource\Exception\FolderDoesNotExistException;
37
use TYPO3\CMS\Core\Resource\Exception\InvalidFileNameException;
38
use TYPO3\CMS\Core\Resource\ResourceStorage;
39
use TYPO3\CMS\Core\Utility\GeneralUtility;
40
use TYPO3\CMS\Core\Utility\PathUtility;
41
42
/**
43
 * Class FlysystemDriver
44
 * @package CedricZiel\FalFlysystem\Fal
45
 */
46
abstract class FlysystemDriver extends AbstractHierarchicalFilesystemDriver
47
{
48
    /**
49
     * @var FilesystemInterface
50
     */
51
    protected $filesystem;
52
53
    /**
54
     * @var AdapterInterface
55
     */
56
    protected $adapter;
57
58
    /**
59
     * @var string
60
     */
61
    protected $entryPath;
62
63
    /**
64
     * FlysystemDriver constructor.
65
     * @param array $configuration
66
     */
67 60
    public function __construct(array $configuration = [])
68
    {
69 60
        parent::__construct($configuration);
70
        // The capabilities default of this driver. See CAPABILITY_* constants for possible values
71 60
        $this->capabilities =
72 20
            ResourceStorage::CAPABILITY_BROWSABLE
73 60
            | ResourceStorage::CAPABILITY_PUBLIC
74 60
            | ResourceStorage::CAPABILITY_WRITABLE;
75 60
    }
76
77
    /**
78
     * Processes the configuration for this driver.
79
     * @return void
80
     */
81
    public function processConfiguration()
82
    {
83
        $this->entryPath = $this->configuration['path'];
84
    }
85
86
    /**
87
     * Merges the capabilities merged by the user at the storage
88
     * configuration into the actual capabilities of the driver
89
     * and returns the result.
90
     *
91
     * @param int $capabilities
92
     * @return int
93
     */
94
    public function mergeConfigurationCapabilities($capabilities)
95
    {
96
        $this->capabilities &= $capabilities;
97
        return $this->capabilities;
98
    }
99
100
    /**
101
     * Returns the identifier of the root level folder of the storage.
102
     *
103
     * @return string
104
     */
105
    public function getRootLevelFolder()
106
    {
107
        return '/';
108
    }
109
110
    /**
111
     * Returns the identifier of the default folder new files should be put into.
112
     *
113
     * @return string
114
     */
115 3
    public function getDefaultFolder()
116
    {
117 3
        $identifier = '/user_upload/';
118 3
        $createFolder = !$this->folderExists($identifier);
119 3
        if (true === $createFolder) {
120 3
            $identifier = $this->createFolder('user_upload');
121 2
        }
122 3
        return $identifier;
123
    }
124
125
    /**
126
     * Checks if a folder exists.
127
     *
128
     * @param string $folderIdentifier
129
     * @return bool
130
     */
131 9
    public function folderExists($folderIdentifier)
132
    {
133 9
        $normalizedIdentifier = $this->canonicalizeAndCheckFolderIdentifier($folderIdentifier);
134 9
        $normalizedIdentifier = ltrim(rtrim($normalizedIdentifier, '/'), '/');
135
136 9
        if ('/' === $folderIdentifier) {
137
            return true;
138
        } else {
139
            return (
140 9
                $this->filesystem->has($normalizedIdentifier)
141 9
                && $this->filesystem->get($normalizedIdentifier)->isDir()
142 6
            );
143
        }
144
    }
145
146
    /**
147
     * Creates a folder, within a parent folder.
148
     * If no parent folder is given, a root level folder will be created
149
     *
150
     * @param string $newFolderName
151
     * @param string $parentFolderIdentifier
152
     * @param bool $recursive
153
     * @return string the Identifier of the new folder
154
     */
155 9
    public function createFolder($newFolderName, $parentFolderIdentifier = '', $recursive = false)
156
    {
157 9
        $parentFolderIdentifier = $this->canonicalizeAndCheckFolderIdentifier($parentFolderIdentifier);
158 9
        $newFolderName = trim($newFolderName, '/');
159
160 9
        $newFolderName = $this->sanitizeFileName($newFolderName);
161 9
        $newIdentifier = $parentFolderIdentifier . $newFolderName . '/';
162 9
        $this->filesystem->createDir($newIdentifier);
163
164 9
        return $newIdentifier;
165
    }
166
167
    /**
168
     * Returns the public URL to a file.
169
     * Either fully qualified URL or relative to PATH_site (rawurlencoded).
170
     *
171
     * @param string $identifier
172
     * @return string
173
     */
174
    public function getPublicUrl($identifier)
175
    {
176
        return '/';
177
    }
178
179
    /**
180
     * Renames a folder in this storage.
181
     *
182
     * @param string $folderIdentifier
183
     * @param string $newName
184
     * @return array A map of old to new file identifiers of all affected resources
185
     */
186 3
    public function renameFolder($folderIdentifier, $newName)
187
    {
188 3
        $renameResult = $this->filesystem->rename($folderIdentifier, $newName);
189
190 3
        if (true === $renameResult) {
191 3
            return [$folderIdentifier => $newName];
192
        } else {
193
            return [$folderIdentifier => $folderIdentifier];
194
        }
195
    }
196
197
    /**
198
     * Removes a folder in filesystem.
199
     *
200
     * @param string $folderIdentifier
201
     * @param bool $deleteRecursively
202
     * @return bool
203
     * @throws FileOperationErrorException
204
     */
205
    public function deleteFolder($folderIdentifier, $deleteRecursively = false)
206
    {
207
        $folderIdentifier = ltrim($folderIdentifier, '/');
208
        $result = $this->filesystem->deleteDir(rtrim($folderIdentifier, '/'));
209
        if (false === $result) {
210
            throw new FileOperationErrorException(
211
                'Deleting folder "' . $folderIdentifier . '" failed.',
212
                1330119451
213
            );
214
        }
215
        return $result;
216
    }
217
218
    /**
219
     * Checks if a file exists.
220
     *
221
     * @param string $fileIdentifier
222
     * @return bool
223
     */
224 12
    public function fileExists($fileIdentifier)
225
    {
226 12
        if ($this->filesystem->has($fileIdentifier) && !$this->filesystem->get($fileIdentifier)->isDir()) {
227 12
            return true;
228
        }
229 9
        return false;
230
    }
231
232
    /**
233
     * Checks if a folder contains files and (if supported) other folders.
234
     *
235
     * @param string $folderIdentifier
236
     * @return bool TRUE if there are no files and folders within $folder
237
     */
238 3
    public function isFolderEmpty($folderIdentifier)
239
    {
240 3
        return 0 === count($this->filesystem->listContents($folderIdentifier));
241
    }
242
243
    /**
244
     * Adds a file from the local server hard disk to a given path in TYPO3s
245
     * virtual file system. This assumes that the local file exists, so no
246
     * further check is done here! After a successful the original file must
247
     * not exist anymore.
248
     *
249
     * @param string $localFilePath (within PATH_site)
250
     * @param string $targetFolderIdentifier
251
     * @param string $newFileName optional, if not given original name is used
252
     * @param bool $removeOriginal if set the original file will be removed
253
     *                                after successful operation
254
     * @return string the identifier of the new file
255
     */
256 3
    public function addFile($localFilePath, $targetFolderIdentifier, $newFileName = '', $removeOriginal = true)
257
    {
258 3
        $localFilePath = $this->canonicalizeAndCheckFilePath($localFilePath);
259 3
        $newFileName = $this->sanitizeFileName($newFileName !== '' ? $newFileName : PathUtility::basename($localFilePath));
260 3
        $newFileIdentifier = $this->canonicalizeAndCheckFolderIdentifier($targetFolderIdentifier) . $newFileName;
261
262 3
        $targetPath = ltrim($newFileIdentifier, '/');
263
264 3
        $content = file_get_contents($localFilePath);
265
266 3
        if ($removeOriginal) {
267
            if (is_uploaded_file($localFilePath)) {
268
                $result = $this->filesystem->put($targetPath, $content);
269
            } else {
270 2
                $result = rename($localFilePath, $targetPath);
271
            }
272
            unlink($localFilePath);
273
        } else {
274 3
            $result = $this->filesystem->put($targetPath, $content);
275
        }
276 3
        if ($result === false || !$this->filesystem->has($targetPath)) {
277
            throw new \RuntimeException('Adding file ' . $localFilePath . ' at ' . $newFileIdentifier . ' failed.');
278
        }
279 3
        clearstatcache();
280 3
        return $newFileIdentifier;
281
    }
282
283
    /**
284
     * Creates a new (empty) file and returns the identifier.
285
     *
286
     * @param string $fileName
287
     * @param string $parentFolderIdentifier
288
     * @return string
289
     * @throws InvalidFileNameException
290
     */
291 3
    public function createFile($fileName, $parentFolderIdentifier)
292
    {
293 3
        if (!$this->isValidFilename($fileName)) {
294
            throw new InvalidFileNameException(
295
                'Invalid characters in fileName "' . $fileName . '"',
296
                1320572272
297
            );
298
        }
299
300 3
        $parentFolderIdentifier = $this->canonicalizeAndCheckFolderIdentifier($parentFolderIdentifier);
301 3
        $fileIdentifier = $this->canonicalizeAndCheckFileIdentifier(
302 3
            $parentFolderIdentifier . $this->sanitizeFileName(ltrim($fileName, '/'))
303 2
        );
304
305 3
        $path = ltrim($parentFolderIdentifier . $fileName, '/');
306 3
        $result = $this->filesystem->put($path, '');
307
308 3
        if ($result !== true) {
309
            throw new \RuntimeException('Creating file ' . $fileIdentifier . ' failed.', 1320569854);
310
        }
311
312 3
        return $fileIdentifier;
313
    }
314
315
    /**
316
     * Copies a file *within* the current storage.
317
     * Note that this is only about an inner storage copy action,
318
     * where a file is just copied to another folder in the same storage.
319
     *
320
     * @param string $fileIdentifier
321
     * @param string $targetFolderIdentifier
322
     * @param string $fileName
323
     * @return string the Identifier of the new file
324
     */
325
    public function copyFileWithinStorage($fileIdentifier, $targetFolderIdentifier, $fileName)
326
    {
327
        // TODO: Implement copyFileWithinStorage() method.
328
        DebuggerUtility::var_dump([
329
            '$fileIdentifier' => $fileIdentifier,
330
            '$targetFolderIdentifier' => $targetFolderIdentifier,
331
            '$fileName' => $fileName
332
        ], 'copyFileWithinStorage');
333
    }
334
335
    /**
336
     * Renames a file in this storage.
337
     *
338
     * @param string $fileIdentifier
339
     * @param string $newName The target path (including the file name!)
340
     * @return string The identifier of the file after renaming
341
     */
342
    public function renameFile($fileIdentifier, $newName)
343
    {
344
        // TODO: Implement renameFile() method.
345
        DebuggerUtility::var_dump([
346
            '$fileIdentifier' => $fileIdentifier,
347
            '$newName' => $newName
348
        ], 'renameFile');
349
    }
350
351
    /**
352
     * Replaces a file with file in local file system.
353
     *
354
     * @param string $fileIdentifier
355
     * @param string $localFilePath
356
     * @return bool TRUE if the operation succeeded
357
     */
358 2
    public function replaceFile($fileIdentifier, $localFilePath)
359
    {
360
        // TODO: Implement replaceFile() method.
361
        DebuggerUtility::var_dump([
362 2
            '$fileIdentifier' => $fileIdentifier,
363
            '$localFilePath' => $localFilePath
364
        ], 'replaceFile');
365
    }
366
367
    /**
368
     * Removes a file from the filesystem. This does not check if the file is
369
     * still used or if it is a bad idea to delete it for some other reason
370
     * this has to be taken care of in the upper layers (e.g. the Storage)!
371
     *
372
     * @param string $fileIdentifier
373
     * @return bool TRUE if deleting the file succeeded
374
     */
375 3
    public function deleteFile($fileIdentifier)
376
    {
377 3
        return $this->filesystem->delete($fileIdentifier);
378
    }
379
380
    /**
381
     * Creates a hash for a file.
382
     *
383
     * @param string $fileIdentifier
384
     * @param string $hashAlgorithm The hash algorithm to use
385
     * @return string
386
     */
387 9
    public function hash($fileIdentifier, $hashAlgorithm)
388
    {
389 9
        if (!in_array($hashAlgorithm, ['sha1', 'md5'])) {
390 3
            throw new \InvalidArgumentException(
391 3
                'Hash algorithm "' . $hashAlgorithm . '" is not supported.',
392 1
                1304964032
393 2
            );
394
        }
395 6
        $propertiesToHash = ['name', 'size', 'mtime', 'identifier'];
396
        switch ($hashAlgorithm) {
397 6
            case 'sha1':
398 3
                $hash = sha1(implode('-', $this->getFileInfoByIdentifier($fileIdentifier, $propertiesToHash)));
399 3
                break;
400 2
            case 'md5':
401 3
                $hash = md5(implode('-', $this->getFileInfoByIdentifier($fileIdentifier, $propertiesToHash)));
402 3
                break;
403
            default:
404
                throw new \RuntimeException('Hash algorithm ' . $hashAlgorithm . ' is not implemented.', 1329644451);
405
        }
406 6
        return $hash;
407
    }
408
409
    /**
410
     * Moves a file *within* the current storage.
411
     * Note that this is only about an inner-storage move action,
412
     * where a file is just moved to another folder in the same storage.
413
     *
414
     * @param string $fileIdentifier
415
     * @param string $targetFolderIdentifier
416
     * @param string $newFileName
417
     * @return string
418
     */
419
    public function moveFileWithinStorage($fileIdentifier, $targetFolderIdentifier, $newFileName)
420
    {
421
        // TODO: Implement moveFileWithinStorage() method.
422
    }
423
424
    /**
425
     * Folder equivalent to moveFileWithinStorage().
426
     *
427
     * @param string $sourceFolderIdentifier
428
     * @param string $targetFolderIdentifier
429
     * @param string $newFolderName
430
     * @return array All files which are affected, map of old => new file identifiers
431
     */
432
    public function moveFolderWithinStorage($sourceFolderIdentifier, $targetFolderIdentifier, $newFolderName)
433
    {
434
        // TODO: Implement moveFolderWithinStorage() method.
435
    }
436
437
    /**
438
     * Folder equivalent to copyFileWithinStorage().
439
     *
440
     * @param string $sourceFolderIdentifier
441
     * @param string $targetFolderIdentifier
442
     * @param string $newFolderName
443
     * @return bool
444
     */
445
    public function copyFolderWithinStorage($sourceFolderIdentifier, $targetFolderIdentifier, $newFolderName)
446
    {
447
        // TODO: Implement copyFolderWithinStorage() method.
448
    }
449
450
    /**
451
     * Returns the contents of a file. Beware that this requires to load the
452
     * complete file into memory and also may require fetching the file from an
453
     * external location. So this might be an expensive operation (both in terms
454
     * of processing resources and money) for large files.
455
     *
456
     * @param string $fileIdentifier
457
     * @return string The file contents
458
     */
459 3
    public function getFileContents($fileIdentifier)
460
    {
461 3
        return $this->filesystem->read($fileIdentifier);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression $this->filesystem->read($fileIdentifier); of type string|false adds false to the return on line 461 which is incompatible with the return type declared by the interface TYPO3\CMS\Core\Resource\...erface::getFileContents of type string. It seems like you forgot to handle an error condition.
Loading history...
462
    }
463
464
    /**
465
     * Sets the contents of a file to the specified value.
466
     *
467
     * @param string $fileIdentifier
468
     * @param string $contents
469
     * @return int The number of bytes written to the file
470
     */
471 3
    public function setFileContents($fileIdentifier, $contents)
472
    {
473 3
        $this->filesystem->put($fileIdentifier, $contents);
474
475 3
        return $this->filesystem->getSize($fileIdentifier);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression $this->filesystem->getSize($fileIdentifier); of type integer|false adds false to the return on line 475 which is incompatible with the return type declared by the interface TYPO3\CMS\Core\Resource\...erface::setFileContents of type integer. It seems like you forgot to handle an error condition.
Loading history...
476
    }
477
478
    /**
479
     * Checks if a file inside a folder exists
480
     *
481
     * @param string $fileName
482
     * @param string $folderIdentifier
483
     * @return bool
484
     */
485 3
    public function fileExistsInFolder($fileName, $folderIdentifier)
486
    {
487 3
        $identifier = $folderIdentifier . '/' . $fileName;
488 3
        $identifier = $this->canonicalizeAndCheckFileIdentifier($identifier);
489 3
        return $this->fileExists($identifier);
490
    }
491
492
    /**
493
     * Checks if a folder inside a folder exists.
494
     *
495
     * @param string $folderName
496
     * @param string $folderIdentifier
497
     * @return bool
498
     */
499 3
    public function folderExistsInFolder($folderName, $folderIdentifier)
500
    {
501 3
        $identifier = $folderIdentifier . '/' . $folderName;
502 3
        $identifier = $this->canonicalizeAndCheckFolderIdentifier($identifier);
503 3
        return $this->folderExists($identifier);
504
    }
505
506
    /**
507
     * Returns a path to a local copy of a file for processing it. When changing the
508
     * file, you have to take care of replacing the current version yourself!
509
     *
510
     * @param string $fileIdentifier
511
     * @param bool $writable Set this to FALSE if you only need the file for read
512
     *                       operations. This might speed up things, e.g. by using
513
     *                       a cached local version. Never modify the file if you
514
     *                       have set this flag!
515
     * @return string The path to the file on the local disk
516
     */
517
    public function getFileForLocalProcessing($fileIdentifier, $writable = true)
518
    {
519
        // TODO: Implement getFileForLocalProcessing() method.
520
        DebuggerUtility::var_dump([
521
            '$fileIdentifier' => $fileIdentifier,
522
            '$writable' => $writable,
523
        ], 'getFileForLocalProcessing');
524
    }
525
526
    /**
527
     * Returns the permissions of a file/folder as an array
528
     * (keys r, w) of boolean flags
529
     *
530
     * @param string $identifier
531
     * @return array
532
     */
533
    public function getPermissions($identifier)
534
    {
535
        return array(
536
            'r' => true,
537
            'w' => true,
538
        );
539
    }
540
541
    /**
542
     * Directly output the contents of the file to the output
543
     * buffer. Should not take care of header files or flushing
544
     * buffer before. Will be taken care of by the Storage.
545
     *
546
     * @param string $identifier
547
     * @return void
548
     */
549
    public function dumpFileContents($identifier)
550
    {
551
        // TODO: Implement dumpFileContents() method.
552
        DebuggerUtility::var_dump([
553
            '$identifier' => $identifier,
554
        ], 'dumpFileContents');
555
    }
556
557
    /**
558
     * Checks if a given identifier is within a container, e.g. if
559
     * a file or folder is within another folder.
560
     * This can e.g. be used to check for web-mounts.
561
     *
562
     * Hint: this also needs to return TRUE if the given identifier
563
     * matches the container identifier to allow access to the root
564
     * folder of a filemount.
565
     *
566
     * @param string $folderIdentifier
567
     * @param string $identifier identifier to be checked against $folderIdentifier
568
     * @return bool TRUE if $content is within or matches $folderIdentifier
569
     */
570
    public function isWithin($folderIdentifier, $identifier)
571
    {
572
        $folderIdentifier = $this->canonicalizeAndCheckFileIdentifier($folderIdentifier);
573
        $entryIdentifier = $this->canonicalizeAndCheckFileIdentifier($identifier);
574
        if ($folderIdentifier === $entryIdentifier) {
575
            return true;
576
        }
577
        // File identifier canonicalization will not modify a single slash so
578
        // we must not append another slash in that case.
579
        if ($folderIdentifier !== '/') {
580
            $folderIdentifier .= '/';
581
        }
582
        return GeneralUtility::isFirstPartOfStr($entryIdentifier, $folderIdentifier);
583
    }
584
585
    /**
586
     * Returns information about a file.
587
     *
588
     * @param string $fileIdentifier
589
     * @param array $propertiesToExtract Array of properties which are be extracted
590
     *                                   If empty all will be extracted
591
     * @return array
592
     * @throws FileDoesNotExistException
593
     */
594 6
    public function getFileInfoByIdentifier($fileIdentifier, array $propertiesToExtract = [])
595
    {
596 6
        $relativeDriverPath = ltrim($fileIdentifier, '/');
597 6
        if (!$this->filesystem->has($relativeDriverPath) || !$this->filesystem->get($relativeDriverPath)->isFile()) {
598
            throw new FileDoesNotExistException('File ' . $fileIdentifier . ' does not exist.', 1314516809);
599
        }
600 6
        $dirPath = PathUtility::dirname($fileIdentifier);
601 6
        $dirPath = $this->canonicalizeAndCheckFolderIdentifier($dirPath);
602 6
        return $this->extractFileInformation($relativeDriverPath, $dirPath, $propertiesToExtract);
603
    }
604
605
    /**
606
     * Returns information about a file.
607
     *
608
     * @param string $folderIdentifier
609
     * @return array
610
     * @throws FolderDoesNotExistException
611
     */
612 3
    public function getFolderInfoByIdentifier($folderIdentifier)
613
    {
614 3
        $folderIdentifier = $this->canonicalizeAndCheckFolderIdentifier($folderIdentifier);
615
616 3
        if (!$this->folderExists($folderIdentifier)) {
617
            throw new FolderDoesNotExistException(
618
                'Folder "' . $folderIdentifier . '" does not exist.',
619
                1314516810
620
            );
621
        }
622
        return [
623 3
            'identifier' => $folderIdentifier,
624 3
            'name' => PathUtility::basename($folderIdentifier),
625 3
            'storage' => $this->storageUid
626 2
        ];
627
    }
628
629
    /**
630
     * Returns the identifier of a file inside the folder
631
     *
632
     * @param string $fileName
633
     * @param string $folderIdentifier
634
     * @return string file identifier
635
     */
636
    public function getFileInFolder($fileName, $folderIdentifier)
637
    {
638
        return $this->canonicalizeAndCheckFileIdentifier($folderIdentifier . '/' . $fileName);
639
    }
640
641
    /**
642
     * Returns a list of files inside the specified path
643
     *
644
     * @param string $folderIdentifier
645
     * @param int $start
646
     * @param int $numberOfItems
647
     * @param bool $recursive
648
     * @param array $filenameFilterCallbacks callbacks for filtering the items
649
     * @param string $sort Property name used to sort the items.
650
     *                     Among them may be: '' (empty, no sorting), name,
651
     *                     fileext, size, tstamp and rw.
652
     *                     If a driver does not support the given property, it
653
     *                     should fall back to "name".
654
     * @param bool $sortRev TRUE to indicate reverse sorting (last to first)
655
     * @return array of FileIdentifiers
656
     */
657 3
    public function getFilesInFolder(
658
        $folderIdentifier,
659
        $start = 0,
660
        $numberOfItems = 0,
661
        $recursive = false,
662
        array $filenameFilterCallbacks = [],
663
        $sort = '',
664
        $sortRev = false
665
    ) {
666 3
        $calculatedFolderIdentifier = ltrim($this->canonicalizeAndCheckFolderIdentifier($folderIdentifier), '/');
667 3
        $contents = $this->filesystem->listContents($calculatedFolderIdentifier);
668 3
        $files = [];
669
670
        /*
671
         * Filter directories
672
         */
673 3
        foreach ($contents as $directoryItem) {
674 3
            if ('file' === $directoryItem['type']) {
675 3
                $files['/' . $directoryItem['path']] = '/' . $directoryItem['path'];
676 2
            }
677 2
        }
678
679 3
        return $files;
680
    }
681
682
    /**
683
     * Returns the identifier of a folder inside the folder
684
     *
685
     * @param string $folderName The name of the target folder
686
     * @param string $folderIdentifier
687
     * @return string folder identifier
688
     */
689 3
    public function getFolderInFolder($folderName, $folderIdentifier)
690
    {
691 3
        $folderIdentifier = $this->canonicalizeAndCheckFolderIdentifier($folderIdentifier . '/' . $folderName);
692 3
        return $folderIdentifier;
693
    }
694
695
    /**
696
     * Returns a list of folders inside the specified path
697
     *
698
     * @param string $folderIdentifier
699
     * @param int $start
700
     * @param int $numberOfItems
701
     * @param bool $recursive
702
     * @param array $folderNameFilterCallbacks callbacks for filtering the items
703
     * @param string $sort Property name used to sort the items.
704
     *                     Among them may be: '' (empty, no sorting), name,
705
     *                     fileext, size, tstamp and rw.
706
     *                     If a driver does not support the given property, it
707
     *                     should fall back to "name".
708
     * @param bool $sortRev TRUE to indicate reverse sorting (last to first)
709
     * @return array of Folder Identifier
710
     * @TODO: Implement pagination with $start and $numberOfItems
711
     * @TODO: Implement directory filter callbacks
712
     * @TODO: Implement sorting
713
     */
714 3
    public function getFoldersInFolder(
715
        $folderIdentifier,
716
        $start = 0,
717
        $numberOfItems = 0,
718
        $recursive = false,
719
        array $folderNameFilterCallbacks = [],
720
        $sort = '',
721
        $sortRev = false
722
    ) {
723 3
        $calculatedFolderIdentifier = ltrim($this->canonicalizeAndCheckFolderIdentifier($folderIdentifier), '/');
724 3
        $contents = $this->filesystem->listContents($calculatedFolderIdentifier);
725 3
        $directories = [];
726
727
        /*
728
         * Filter directories
729
         */
730 3
        foreach ($contents as $directoryItem) {
731 3
            if ('dir' === $directoryItem['type']) {
732 3
                $directories['/' . $directoryItem['path']]
733 3
                    = '/' . $directoryItem['path'];
734 2
            }
735 2
        }
736
737 3
        return $directories;
738
    }
739
740
    /**
741
     * Returns the number of files inside the specified path
742
     *
743
     * @param string $folderIdentifier
744
     * @param bool $recursive
745
     * @param array $filenameFilterCallbacks callbacks for filtering the items
746
     * @return int Number of files in folder
747
     * @TODO: Implement recursive count
748
     * @TODO: Implement filename filtering
749
     */
750
    public function countFilesInFolder($folderIdentifier, $recursive = false, array $filenameFilterCallbacks = [])
751
    {
752
753
        return count($this->getFilesInFolder($folderIdentifier, 0, 0, $recursive, $filenameFilterCallbacks));
754
    }
755
756
    /**
757
     * Returns the number of folders inside the specified path
758
     *
759
     * @param string $folderIdentifier
760
     * @param bool $recursive
761
     * @param array $folderNameFilterCallbacks callbacks for filtering the items
762
     * @return int Number of folders in folder
763
     */
764 3
    public function countFoldersInFolder($folderIdentifier, $recursive = false, array $folderNameFilterCallbacks = [])
765
    {
766 3
        $count = 0;
767 3
        $filesystemRelativeIdentifier = ltrim($folderIdentifier, '/');
768 3
        $directoryListing = $this->filesystem->listContents($filesystemRelativeIdentifier);
769 3
        foreach ($directoryListing as $entry) {
770 3
            if ('dir' === $entry['type']) {
771 3
                $count++;
772 2
            }
773 2
        }
774
775 3
        return $count;
776
    }
777
778
    /**
779
     * Extracts information about a file from the filesystem.
780
     *
781
     * @param string $filePath The absolute path to the file
782
     * @param string $containerPath The relative path to the file's container
783
     * @param array $propertiesToExtract array of properties which should be returned, if empty all will be extracted
784
     * @return array
785
     */
786 6
    protected function extractFileInformation($filePath, $containerPath, array $propertiesToExtract = array())
787
    {
788 6
        if (empty($propertiesToExtract)) {
789
            $propertiesToExtract = array(
790
                'size',
791
                'atime',
792
                'atime',
793
                'mtime',
794
                'ctime',
795
                'mimetype',
796
                'name',
797
                'identifier',
798
                'identifier_hash',
799
                'storage',
800
                'folder_hash'
801
            );
802
        }
803 6
        $fileInformation = array();
804 6
        foreach ($propertiesToExtract as $property) {
805 6
            $fileInformation[$property] = $this->getSpecificFileInformation($filePath, $containerPath, $property);
806 4
        }
807 6
        return $fileInformation;
808
    }
809
810
    /**
811
     * Extracts a specific FileInformation from the FileSystems.
812
     *
813
     * @param string $fileIdentifier
814
     * @param string $containerPath
815
     * @param string $property
816
     *
817
     * @return bool|int|string
818
     * @throws \InvalidArgumentException
819
     */
820 6
    public function getSpecificFileInformation($fileIdentifier, $containerPath, $property)
821
    {
822 6
        $identifier = $this->canonicalizeAndCheckFileIdentifier($containerPath . PathUtility::basename($fileIdentifier));
823 6
        $file = $this->filesystem->getMetadata($fileIdentifier);
824
825
        switch ($property) {
826 6
            case 'size':
827 6
                return $file['size'];
828 4
            case 'atime':
829
                return $file['timestamp'];
830 4
            case 'mtime':
831 6
                return $file['timestamp'];
832 4
            case 'ctime':
833
                return $file['timestamp'];
834 4
            case 'name':
835 6
                return PathUtility::basename($fileIdentifier);
836 4
            case 'mimetype':
837
                return 'application/octet-stream';
838 4
            case 'identifier':
839 6
                return $identifier;
840
            case 'storage':
841
                return $this->storageUid;
842
            case 'identifier_hash':
843
                return $this->hashIdentifier($identifier);
844
            case 'folder_hash':
845
                return $this->hashIdentifier($this->getParentFolderIdentifierOfIdentifier($identifier));
846
            default:
847
                throw new \InvalidArgumentException(sprintf('The information "%s" is not available.', $property));
848
        }
849
    }
850
}
851