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