Completed
Push — master ( 9cf1bc...df6565 )
by Cedric
01:48
created

FlysystemDriver::addFile()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 22
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 5.2

Importance

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