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