Passed
Push — master ( 36fc37...a85bac )
by
unknown
58:34 queued 43:55
created

Folder::getName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Core\Resource;
17
18
use TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException;
19
use TYPO3\CMS\Core\Resource\Exception\ResourcePermissionsUnavailableException;
20
use TYPO3\CMS\Core\Resource\Search\FileSearchDemand;
21
use TYPO3\CMS\Core\Resource\Search\Result\FileSearchResultInterface;
22
use TYPO3\CMS\Core\Utility\PathUtility;
23
24
/**
25
 * A folder that groups files in a storage. This may be a folder on the local
26
 * disk, a bucket in Amazon S3 or a user or a tag in Flickr.
27
 *
28
 * This object is not persisted in TYPO3 locally, but created on the fly by
29
 * storage drivers for the folders they "offer".
30
 *
31
 * Some folders serve as a physical container for files (e.g. folders on the
32
 * local disk, S3 buckets or Flickr users). Other folders just group files by a
33
 * certain criterion, e.g. a tag.
34
 * The way this is implemented depends on the storage driver.
35
 */
36
class Folder implements FolderInterface
37
{
38
    /**
39
     * The storage this folder belongs to.
40
     *
41
     * @var ResourceStorage
42
     */
43
    protected $storage;
44
45
    /**
46
     * The identifier of this folder to identify it on the storage.
47
     * On some drivers, this is the path to the folder, but drivers could also just
48
     * provide any other unique identifier for this folder on the specific storage.
49
     *
50
     * @var string
51
     */
52
    protected $identifier;
53
54
    /**
55
     * The name of this folder
56
     *
57
     * @var string
58
     */
59
    protected $name;
60
61
    /**
62
     * The filters this folder should use for a filelist.
63
     *
64
     * @var callable[]
65
     */
66
    protected $fileAndFolderNameFilters = [];
67
68
    /**
69
     * Modes for filter usage in getFiles()/getFolders()
70
     */
71
    const FILTER_MODE_NO_FILTERS = 0;
72
    // Merge local filters into storage's filters
73
    const FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS = 1;
74
    // Only use the filters provided by the storage
75
    const FILTER_MODE_USE_STORAGE_FILTERS = 2;
76
    // Only use the filters provided by the current class
77
    const FILTER_MODE_USE_OWN_FILTERS = 3;
78
79
    /**
80
     * Initialization of the folder
81
     *
82
     * @param ResourceStorage $storage
83
     * @param string $identifier
84
     * @param string $name
85
     */
86
    public function __construct(ResourceStorage $storage, $identifier, $name)
87
    {
88
        $this->storage = $storage;
89
        $this->identifier = $identifier;
90
        $this->name = $name;
91
    }
92
93
    /**
94
     * Returns the name of this folder.
95
     *
96
     * @return string
97
     */
98
    public function getName()
99
    {
100
        return $this->name;
101
    }
102
103
    /**
104
     * Returns the full path of this folder, from the root.
105
     *
106
     * @param string $rootId ID of the root folder, NULL to auto-detect
107
     *
108
     * @return string
109
     */
110
    public function getReadablePath($rootId = null)
111
    {
112
        if ($rootId === null) {
113
            // Find first matching filemount and use that as root
114
            foreach ($this->storage->getFileMounts() as $fileMount) {
115
                if ($this->storage->isWithinFolder($fileMount['folder'], $this)) {
116
                    $rootId = $fileMount['folder']->getIdentifier();
117
                    break;
118
                }
119
            }
120
            if ($rootId === null) {
121
                $rootId = $this->storage->getRootLevelFolder()->getIdentifier();
122
            }
123
        }
124
        $readablePath = '/';
125
        if ($this->identifier !== $rootId) {
126
            try {
127
                $readablePath = $this->getParentFolder()->getReadablePath($rootId);
128
            } catch (InsufficientFolderAccessPermissionsException $e) {
129
                // May no access to parent folder (e.g. because of mount point)
130
                $readablePath = '/';
131
            }
132
        }
133
        return $readablePath . ($this->name ? $this->name . '/' : '');
134
    }
135
136
    /**
137
     * Sets a new name of the folder
138
     * currently this does not trigger the "renaming process"
139
     * as the name is more seen as a label
140
     *
141
     * @param string $name The new name
142
     */
143
    public function setName($name)
144
    {
145
        $this->name = $name;
146
    }
147
148
    /**
149
     * Returns the storage this folder belongs to.
150
     *
151
     * @return ResourceStorage
152
     */
153
    public function getStorage()
154
    {
155
        return $this->storage;
156
    }
157
158
    /**
159
     * Returns the path of this folder inside the storage. It depends on the
160
     * type of storage whether this is a real path or just some unique identifier.
161
     *
162
     * @return string
163
     */
164
    public function getIdentifier()
165
    {
166
        return $this->identifier;
167
    }
168
169
    /**
170
     * Get hashed identifier
171
     *
172
     * @return string
173
     */
174
    public function getHashedIdentifier()
175
    {
176
        return $this->storage->hashFileIdentifier($this->identifier);
177
    }
178
179
    /**
180
     * Returns a combined identifier of this folder, i.e. the storage UID and
181
     * the folder identifier separated by a colon ":".
182
     *
183
     * @return string Combined storage and folder identifier, e.g. StorageUID:folder/path/
184
     */
185
    public function getCombinedIdentifier()
186
    {
187
        return $this->getStorage()->getUid() . ':' . $this->getIdentifier();
188
    }
189
190
    /**
191
     * Returns a publicly accessible URL for this folder
192
     *
193
     * WARNING: Access to the folder may be restricted by further means, e.g. some
194
     * web-based authentication. You have to take care of this yourself.
195
     *
196
     * @param bool $relativeToCurrentScript Determines whether the URL returned should be relative to the current script, in case it is relative at all (only for the LocalDriver)
197
     * @return string|null NULL if file is missing or deleted, the generated url otherwise
198
     */
199
    public function getPublicUrl($relativeToCurrentScript = false)
200
    {
201
        return $this->getStorage()->getPublicUrl($this, $relativeToCurrentScript);
202
    }
203
204
    /**
205
     * Returns a list of files in this folder, optionally filtered. There are several filter modes available, see the
206
     * FILTER_MODE_* constants for more information.
207
     *
208
     * For performance reasons the returned items can also be limited to a given range
209
     *
210
     * @param int $start The item to start at
211
     * @param int $numberOfItems The number of items to return
212
     * @param int $filterMode The filter mode to use for the filelist.
213
     * @param bool $recursive
214
     * @param string $sort Property name used to sort the items.
215
     *                     Among them may be: '' (empty, no sorting), name,
216
     *                     fileext, size, tstamp and rw.
217
     *                     If a driver does not support the given property, it
218
     *                     should fall back to "name".
219
     * @param bool $sortRev TRUE to indicate reverse sorting (last to first)
220
     * @return \TYPO3\CMS\Core\Resource\File[]
221
     */
222
    public function getFiles($start = 0, $numberOfItems = 0, $filterMode = self::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, $recursive = false, $sort = '', $sortRev = false)
223
    {
224
        // Fallback for compatibility with the old method signature variable $useFilters that was used instead of $filterMode
225
        if ($filterMode === false) {
0 ignored issues
show
introduced by
The condition $filterMode === false is always false.
Loading history...
226
            $useFilters = false;
227
            $backedUpFilters = [];
228
        } else {
229
            [$backedUpFilters, $useFilters] = $this->prepareFiltersInStorage($filterMode);
230
        }
231
232
        $fileObjects = $this->storage->getFilesInFolder($this, $start, $numberOfItems, $useFilters, $recursive, $sort, $sortRev);
233
234
        $this->restoreBackedUpFiltersInStorage($backedUpFilters);
235
236
        return $fileObjects;
237
    }
238
239
    /**
240
     * Returns a file search result based on the given demand.
241
     * The result also includes matches in meta data fields that are defined in TCA.
242
     *
243
     * @param FileSearchDemand $searchDemand
244
     * @param int $filterMode The filter mode to use for the found files
245
     * @return FileSearchResultInterface
246
     */
247
    public function searchFiles(FileSearchDemand $searchDemand, int $filterMode = self::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS): FileSearchResultInterface
248
    {
249
        [$backedUpFilters, $useFilters] = $this->prepareFiltersInStorage($filterMode);
250
        $searchResult = $this->storage->searchFiles($searchDemand, $this, $useFilters);
251
        $this->restoreBackedUpFiltersInStorage($backedUpFilters);
252
253
        return $searchResult;
254
    }
255
256
    /**
257
     * Returns amount of all files within this folder, optionally filtered by
258
     * the given pattern
259
     *
260
     * @param array $filterMethods
261
     * @param bool $recursive
262
     * @return int
263
     * @throws Exception\InsufficientFolderAccessPermissionsException
264
     */
265
    public function getFileCount(array $filterMethods = [], $recursive = false)
266
    {
267
        return $this->storage->countFilesInFolder($this, true, $recursive);
268
    }
269
270
    /**
271
     * Returns the object for a subfolder of the current folder, if it exists.
272
     *
273
     * @param string $name Name of the subfolder
274
     * @return Folder
275
     * @throws \InvalidArgumentException
276
     */
277
    public function getSubfolder($name)
278
    {
279
        if (!$this->storage->hasFolderInFolder($name, $this)) {
280
            throw new \InvalidArgumentException('Folder "' . $name . '" does not exist in "' . $this->identifier . '"', 1329836110);
281
        }
282
        return $this->storage->getFolderInFolder($name, $this);
283
    }
284
285
    /**
286
     * Returns a list of subfolders
287
     *
288
     * @param int $start The item to start at
289
     * @param int $numberOfItems The number of items to return
290
     * @param int $filterMode The filter mode to use for the filelist.
291
     * @param bool $recursive
292
     * @return Folder[]
293
     */
294
    public function getSubfolders($start = 0, $numberOfItems = 0, $filterMode = self::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, $recursive = false)
295
    {
296
        [$backedUpFilters, $useFilters] = $this->prepareFiltersInStorage($filterMode);
297
        $folderObjects = $this->storage->getFoldersInFolder($this, $start, $numberOfItems, $useFilters, $recursive);
298
        $this->restoreBackedUpFiltersInStorage($backedUpFilters);
299
        return $folderObjects;
300
    }
301
302
    /**
303
     * Adds a file from the local server disk. If the file already exists and
304
     * overwriting is disabled,
305
     *
306
     * @param string $localFilePath
307
     * @param string $fileName
308
     * @param string $conflictMode a value of the \TYPO3\CMS\Core\Resource\DuplicationBehavior enumeration
309
     * @return File The file object
310
     */
311
    public function addFile($localFilePath, $fileName = null, $conflictMode = DuplicationBehavior::CANCEL)
312
    {
313
        $fileName = $fileName ?: PathUtility::basename($localFilePath);
314
        return $this->storage->addFile($localFilePath, $this, $fileName, $conflictMode);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->storage->a...ileName, $conflictMode) also could return the type TYPO3\CMS\Core\Resource\ProcessedFile which is incompatible with the documented return type TYPO3\CMS\Core\Resource\File.
Loading history...
315
    }
316
317
    /**
318
     * Adds an uploaded file into the Storage.
319
     *
320
     * @param array $uploadedFileData contains information about the uploaded file given by $_FILES['file1']
321
     * @param string $conflictMode a value of the \TYPO3\CMS\Core\Resource\DuplicationBehavior enumeration
322
     * @return File The file object
323
     */
324
    public function addUploadedFile(array $uploadedFileData, $conflictMode = DuplicationBehavior::CANCEL)
325
    {
326
        return $this->storage->addUploadedFile($uploadedFileData, $this, $uploadedFileData['name'], $conflictMode);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->storage->a...'name'], $conflictMode) also could return the type TYPO3\CMS\Core\Resource\ProcessedFile which is incompatible with the documented return type TYPO3\CMS\Core\Resource\File.
Loading history...
327
    }
328
329
    /**
330
     * Renames this folder.
331
     *
332
     * @param string $newName
333
     * @return Folder
334
     */
335
    public function rename($newName)
336
    {
337
        return $this->storage->renameFolder($this, $newName);
338
    }
339
340
    /**
341
     * Deletes this folder from its storage. This also means that this object becomes useless.
342
     *
343
     * @param bool $deleteRecursively
344
     * @return bool TRUE if deletion succeeded
345
     */
346
    public function delete($deleteRecursively = true)
347
    {
348
        return $this->storage->deleteFolder($this, $deleteRecursively);
349
    }
350
351
    /**
352
     * Creates a new blank file
353
     *
354
     * @param string $fileName
355
     * @return File The new file object
356
     */
357
    public function createFile($fileName)
358
    {
359
        return $this->storage->createFile($fileName, $this);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->storage->c...eFile($fileName, $this) also could return the type TYPO3\CMS\Core\Resource\ProcessedFile which is incompatible with the documented return type TYPO3\CMS\Core\Resource\File.
Loading history...
360
    }
361
362
    /**
363
     * Creates a new folder
364
     *
365
     * @param string $folderName
366
     * @return Folder The new folder object
367
     */
368
    public function createFolder($folderName)
369
    {
370
        return $this->storage->createFolder($folderName, $this);
371
    }
372
373
    /**
374
     * Copies folder to a target folder
375
     *
376
     * @param Folder $targetFolder Target folder to copy to.
377
     * @param string $targetFolderName an optional destination fileName
378
     * @param string $conflictMode a value of the \TYPO3\CMS\Core\Resource\DuplicationBehavior enumeration
379
     * @return Folder New (copied) folder object.
380
     */
381
    public function copyTo(Folder $targetFolder, $targetFolderName = null, $conflictMode = DuplicationBehavior::RENAME)
382
    {
383
        return $targetFolder->getStorage()->copyFolder($this, $targetFolder, $targetFolderName, $conflictMode);
384
    }
385
386
    /**
387
     * Moves folder to a target folder
388
     *
389
     * @param Folder $targetFolder Target folder to move to.
390
     * @param string $targetFolderName an optional destination fileName
391
     * @param string $conflictMode a value of the \TYPO3\CMS\Core\Resource\DuplicationBehavior enumeration
392
     * @return Folder New (copied) folder object.
393
     */
394
    public function moveTo(Folder $targetFolder, $targetFolderName = null, $conflictMode = DuplicationBehavior::RENAME)
395
    {
396
        return $targetFolder->getStorage()->moveFolder($this, $targetFolder, $targetFolderName, $conflictMode);
397
    }
398
399
    /**
400
     * Checks if a file exists in this folder
401
     *
402
     * @param string $name
403
     * @return bool
404
     */
405
    public function hasFile($name)
406
    {
407
        return $this->storage->hasFileInFolder($name, $this);
408
    }
409
410
    /**
411
     * Fetches a file from a folder, must be a direct descendant of a folder.
412
     *
413
     * @param string $fileName
414
     * @return File|ProcessedFile|null
415
     */
416
    public function getFile(string $fileName)
417
    {
418
        if ($this->storage->hasFileInFolder($fileName, $this)) {
419
            return $this->storage->getFileInFolder($fileName, $this);
420
        }
421
        return null;
422
    }
423
424
    /**
425
     * Checks if a folder exists in this folder.
426
     *
427
     * @param string $name
428
     * @return bool
429
     */
430
    public function hasFolder($name)
431
    {
432
        return $this->storage->hasFolderInFolder($name, $this);
433
    }
434
435
    /**
436
     * Check if a file operation (= action) is allowed on this folder
437
     *
438
     * @param string $action Action that can be read, write or delete
439
     * @return bool
440
     */
441
    public function checkActionPermission($action)
442
    {
443
        try {
444
            return $this->getStorage()->checkFolderActionPermission($action, $this);
445
        } catch (ResourcePermissionsUnavailableException $e) {
446
            return false;
447
        }
448
    }
449
450
    /**
451
     * Updates the properties of this folder, e.g. after re-indexing or moving it.
452
     *
453
     * NOTE: This method should not be called from outside the File Abstraction Layer (FAL)!
454
     *
455
     * @param array $properties
456
     * @internal
457
     */
458
    public function updateProperties(array $properties)
459
    {
460
        // Setting identifier and name to update values
461
        if (isset($properties['identifier'])) {
462
            $this->identifier = $properties['identifier'];
463
        }
464
        if (isset($properties['name'])) {
465
            $this->name = $properties['name'];
466
        }
467
    }
468
469
    /**
470
     * Prepares the filters in this folder's storage according to a set filter mode.
471
     *
472
     * @param int $filterMode The filter mode to use; one of the FILTER_MODE_* constants
473
     * @return array The backed up filters as an array (NULL if filters were not backed up) and whether to use filters or not (bool)
474
     */
475
    protected function prepareFiltersInStorage($filterMode)
476
    {
477
        $backedUpFilters = null;
478
        $useFilters = true;
479
480
        switch ($filterMode) {
481
            case self::FILTER_MODE_USE_OWN_FILTERS:
482
                $backedUpFilters = $this->storage->getFileAndFolderNameFilters();
483
                $this->storage->setFileAndFolderNameFilters($this->fileAndFolderNameFilters);
484
485
                break;
486
487
            case self::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS:
488
                if (!empty($this->fileAndFolderNameFilters)) {
489
                    $backedUpFilters = $this->storage->getFileAndFolderNameFilters();
490
                    foreach ($this->fileAndFolderNameFilters as $filter) {
491
                        $this->storage->addFileAndFolderNameFilter($filter);
492
                    }
493
                }
494
495
                break;
496
497
            case self::FILTER_MODE_USE_STORAGE_FILTERS:
498
                // nothing to do here
499
500
                break;
501
502
            case self::FILTER_MODE_NO_FILTERS:
503
                $useFilters = false;
504
505
                break;
506
        }
507
        return [$backedUpFilters, $useFilters];
508
    }
509
510
    /**
511
     * Restores the filters of a storage.
512
     *
513
     * @param array $backedUpFilters The filters to restore; might be NULL if no filters have been backed up, in
514
     *                               which case this method does nothing.
515
     * @see prepareFiltersInStorage()
516
     */
517
    protected function restoreBackedUpFiltersInStorage($backedUpFilters)
518
    {
519
        if ($backedUpFilters !== null) {
0 ignored issues
show
introduced by
The condition $backedUpFilters !== null is always true.
Loading history...
520
            $this->storage->setFileAndFolderNameFilters($backedUpFilters);
521
        }
522
    }
523
524
    /**
525
     * Sets the filters to use when listing files. These are only used if the filter mode is one of
526
     * FILTER_MODE_USE_OWN_FILTERS and FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS
527
     *
528
     * @param array $filters
529
     */
530
    public function setFileAndFolderNameFilters(array $filters)
531
    {
532
        $this->fileAndFolderNameFilters = $filters;
533
    }
534
535
    /**
536
     * Returns the role of this folder (if any). See FolderInterface::ROLE_* constants for possible values.
537
     *
538
     * @return string
539
     */
540
    public function getRole()
541
    {
542
        return $this->storage->getRole($this);
543
    }
544
545
    /**
546
     * Returns the parent folder.
547
     *
548
     * In non-hierarchical storages, that always is the root folder.
549
     *
550
     * The parent folder of the root folder is the root folder.
551
     *
552
     * @return FolderInterface
553
     */
554
    public function getParentFolder()
555
    {
556
        return $this->getStorage()->getFolder($this->getStorage()->getFolderIdentifierFromFileIdentifier($this->getIdentifier()));
557
    }
558
559
    /**
560
     * Returns the modification time of the file as Unix timestamp
561
     *
562
     * @return int
563
     */
564
    public function getModificationTime()
565
    {
566
        return $this->storage->getFolderInfo($this)['mtime'];
567
    }
568
569
    /**
570
     * Returns the creation time of the file as Unix timestamp
571
     *
572
     * @return int
573
     */
574
    public function getCreationTime()
575
    {
576
        return $this->storage->getFolderInfo($this)['ctime'];
577
    }
578
}
579