Completed
Push — master ( dbba03...5f62cc )
by Cedric
01:48
created

FlysystemDriver::getFileInFolder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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