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