[BUGFIX] Folder::getFiles directly calls Factory::createFileObject
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Resource / ResourceStorage.php
1 <?php
2 namespace TYPO3\CMS\Core\Resource;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2011-2013 Andreas Wolf <andreas.wolf@typo3.org>
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\Resource\Index\FileIndexRepository;
31 use TYPO3\CMS\Core\Utility\GeneralUtility;
32 use TYPO3\CMS\Core\Utility\PathUtility;
33
34 /**
35 * A "mount point" inside the TYPO3 file handling.
36 *
37 * A "storage" object handles
38 * - abstraction to the driver
39 * - permissions (from the driver, and from the user, + capabilities)
40 * - an entry point for files, folders, and for most other operations
41 *
42 * == Driver entry point
43 * The driver itself, that does the actual work on the file system,
44 * is inside the storage but completely shadowed by
45 * the storage, as the storage also handles the abstraction to the
46 * driver
47 *
48 * The storage can be on the local system, but can also be on a remote
49 * system. The combination of driver + configurable capabilities (storage
50 * is read-only e.g.) allows for flexible uses.
51 *
52 *
53 * == Permission system
54 * As all requests have to run through the storage, the storage knows about the
55 * permissions of a BE/FE user, the file permissions / limitations of the driver
56 * and has some configurable capabilities.
57 * Additionally, a BE user can use "filemounts" (known from previous installations)
58 * to limit his/her work-zone to only a subset (identifier and its subfolders/subfolders)
59 * of the user itself.
60 *
61 * Check 1: "User Permissions" [is the user allowed to write a file) [is the user allowed to write a file]
62 * Check 2: "File Mounts" of the User (act as subsets / filters to the identifiers) [is the user allowed to do something in this folder?]
63 * Check 3: "Capabilities" of Storage (then: of Driver) [is the storage/driver writable?]
64 * Check 4: "File permissions" of the Driver [is the folder writable?]
65 *
66 * @author Andreas Wolf <andreas.wolf@typo3.org>
67 * @author Ingmar Schlecht <ingmar@typo3.org>
68 */
69 class ResourceStorage {
70
71 const SIGNAL_PreProcessConfiguration = 'preProcessConfiguration';
72 const SIGNAL_PostProcessConfiguration = 'postProcessConfiguration';
73 const SIGNAL_PreFileAdd = 'preFileAdd';
74 const SIGNAL_PostFileAdd = 'postFileAdd';
75 const SIGNAL_PreFileCopy = 'preFileCopy';
76 const SIGNAL_PostFileCopy = 'postFileCopy';
77 const SIGNAL_PreFileMove = 'preFileMove';
78 const SIGNAL_PostFileMove = 'postFileMove';
79 const SIGNAL_PreFileDelete = 'preFileDelete';
80 const SIGNAL_PostFileDelete = 'postFileDelete';
81 const SIGNAL_PreFileRename = 'preFileRename';
82 const SIGNAL_PostFileRename = 'postFileRename';
83 const SIGNAL_PreFileReplace = 'preFileReplace';
84 const SIGNAL_PostFileReplace = 'postFileReplace';
85 const SIGNAL_PreFolderCopy = 'preFolderCopy';
86 const SIGNAL_PostFolderCopy = 'postFolderCopy';
87 const SIGNAL_PreFolderMove = 'preFolderMove';
88 const SIGNAL_PostFolderMove = 'postFolderMove';
89 const SIGNAL_PreFolderDelete = 'preFolderDelete';
90 const SIGNAL_PostFolderDelete = 'postFolderDelete';
91 const SIGNAL_PreFolderRename = 'preFolderRename';
92 const SIGNAL_PostFolderRename = 'postFolderRename';
93 const SIGNAL_PreGeneratePublicUrl = 'preGeneratePublicUrl';
94 /**
95 * The storage driver instance belonging to this storage.
96 *
97 * @var Driver\AbstractDriver
98 */
99 protected $driver;
100
101 /**
102 * The database record for this storage
103 *
104 * @var array
105 */
106 protected $storageRecord;
107
108 /**
109 * The configuration belonging to this storage (decoded from the configuration field).
110 *
111 * @var array
112 */
113 protected $configuration;
114
115 /**
116 * The base URI to this storage.
117 *
118 * @var string
119 */
120 protected $baseUri;
121
122 /**
123 * @var Service\FileProcessingService
124 */
125 protected $fileProcessingService;
126
127 /**
128 * Whether to check if file or folder is in user mounts
129 * and the action is allowed for a user
130 * Default is FALSE so that resources are accessible for
131 * front end rendering or admins.
132 *
133 * @var boolean
134 */
135 protected $evaluatePermissions = FALSE;
136
137 /**
138 * User filemounts, added as an array, and used as filters
139 *
140 * @var array
141 */
142 protected $fileMounts = array();
143
144 /**
145 * The file permissions of the user (and their group) merged together and
146 * available as an array
147 *
148 * @var array
149 */
150 protected $userPermissions = array();
151
152 /**
153 * The capabilities of this storage as defined in the storage record.
154 * Also see the CAPABILITY_* constants below
155 *
156 * @var integer
157 */
158 protected $capabilities;
159
160 /**
161 * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
162 */
163 protected $signalSlotDispatcher;
164
165 /**
166 * Capability for being browsable by (backend) users
167 */
168 const CAPABILITY_BROWSABLE = 1;
169 /**
170 * Capability for publicly accessible storages (= accessible from the web)
171 */
172 const CAPABILITY_PUBLIC = 2;
173 /**
174 * Capability for writable storages. This only signifies writability in
175 * general - this might also be further limited by configuration.
176 */
177 const CAPABILITY_WRITABLE = 4;
178 /**
179 * Name of the default processing folder
180 */
181 const DEFAULT_ProcessingFolder = '_processed_';
182 /**
183 * @var Folder
184 */
185 protected $processingFolder;
186
187 /**
188 * whether this storage is online or offline in this request
189 *
190 * @var boolean
191 */
192 protected $isOnline = NULL;
193
194 /**
195 * The filters used for the files and folder names.
196 *
197 * @var array
198 */
199 protected $fileAndFolderNameFilters = array();
200
201 /**
202 * Constructor for a storage object.
203 *
204 * @param Driver\AbstractDriver $driver
205 * @param array $storageRecord The storage record row from the database
206 */
207 public function __construct(Driver\AbstractDriver $driver, array $storageRecord) {
208 $this->storageRecord = $storageRecord;
209 $this->configuration = ResourceFactory::getInstance()->convertFlexFormDataToConfigurationArray($storageRecord['configuration']);
210 $this->driver = $driver;
211 $this->driver->setStorage($this);
212 try {
213 $this->driver->processConfiguration();
214 } catch (Exception\InvalidConfigurationException $e) {
215 // configuration error
216 // mark this storage as permanently unusable
217 $this->markAsPermanentlyOffline();
218 }
219 $this->driver->initialize();
220 $this->capabilities = ($this->storageRecord['is_browsable'] && $this->driver->hasCapability(self::CAPABILITY_BROWSABLE) ? self::CAPABILITY_BROWSABLE : 0) + ($this->storageRecord['is_public'] && $this->driver->hasCapability(self::CAPABILITY_PUBLIC) ? self::CAPABILITY_PUBLIC : 0) + ($this->storageRecord['is_writable'] && $this->driver->hasCapability(self::CAPABILITY_WRITABLE) ? self::CAPABILITY_WRITABLE : 0);
221 // TODO do not set the "public" capability if no public URIs can be generated
222 $this->processConfiguration();
223 $this->resetFileAndFolderNameFiltersToDefault();
224 }
225
226 /**
227 * Gets the configuration
228 *
229 * @return array
230 */
231 public function getConfiguration() {
232 return $this->configuration;
233 }
234
235 /**
236 * Sets the configuration.
237 *
238 * @param array $configuration
239 */
240 public function setConfiguration(array $configuration) {
241 $this->configuration = $configuration;
242 }
243
244 /**
245 * Gets the storage record.
246 *
247 * @return array
248 */
249 public function getStorageRecord() {
250 return $this->storageRecord;
251 }
252
253 /**
254 * Processes the configuration of this storage.
255 *
256 * @throws \InvalidArgumentException If a required configuration option is not set or has an invalid value.
257 * @return void
258 */
259 protected function processConfiguration() {
260 $this->emitPreProcessConfigurationSignal();
261 if (isset($this->configuration['baseUri'])) {
262 $this->baseUri = rtrim($this->configuration['baseUri'], '/') . '/';
263 }
264 $this->emitPostProcessConfigurationSignal();
265 }
266
267 /**
268 * Returns the base URI of this storage; all files are reachable via URLs
269 * beginning with this string.
270 *
271 * @return string
272 */
273 public function getBaseUri() {
274 return $this->baseUri;
275 }
276
277 /**
278 * Sets the storage that belongs to this storage.
279 *
280 * @param Driver\AbstractDriver $driver
281 * @return ResourceStorage
282 */
283 public function setDriver(Driver\AbstractDriver $driver) {
284 $this->driver = $driver;
285 return $this;
286 }
287
288 /**
289 * Returns the driver object belonging to this storage.
290 *
291 * @return Driver\AbstractDriver
292 */
293 protected function getDriver() {
294 return $this->driver;
295 }
296
297 /**
298 * Deprecated function, don't use it. Will be removed in some later revision.
299 *
300 * @param string $identifier
301 *
302 * @throws \BadMethodCallException
303 */
304 public function getFolderByIdentifier($identifier) {
305 throw new \BadMethodCallException('Function TYPO3\\CMS\\Core\\Resource\\ResourceStorage::getFolderByIdentifier() has been renamed to just getFolder(). Please fix the method call.', 1333754514);
306 }
307
308 /**
309 * Deprecated function, don't use it. Will be removed in some later revision.
310 *
311 * @param string $identifier
312 *
313 * @throws \BadMethodCallException
314 */
315 public function getFileByIdentifier($identifier) {
316 throw new \BadMethodCallException('Function TYPO3\\CMS\\Core\\Resource\\ResourceStorage::getFileByIdentifier() has been renamed to just getFileInfoByIdentifier(). ' . 'Please fix the method call.', 1333754533);
317 }
318
319 /**
320 * Returns the name of this storage.
321 *
322 * @return string
323 */
324 public function getName() {
325 return $this->storageRecord['name'];
326 }
327
328 /**
329 * Returns the uid of this storage.
330 *
331 * @return integer
332 */
333 public function getUid() {
334 return (int) $this->storageRecord['uid'];
335 }
336
337 /**
338 * Tells whether there are children in this storage
339 *
340 * @return boolean
341 */
342 public function hasChildren() {
343 return TRUE;
344 }
345
346 /*********************************
347 * Capabilities
348 ********************************/
349 /**
350 * Returns the capabilities of this storage.
351 *
352 * @return integer
353 * @see CAPABILITY_* constants
354 */
355 public function getCapabilities() {
356 return (int) $this->capabilities;
357 }
358
359 /**
360 * Returns TRUE if this storage has the given capability.
361 *
362 * @param integer $capability A capability, as defined in a CAPABILITY_* constant
363 * @return boolean
364 */
365 protected function hasCapability($capability) {
366 return ($this->capabilities & $capability) == $capability;
367 }
368
369 /**
370 * Returns TRUE if this storage is publicly available. This is just a
371 * configuration option and does not mean that it really *is* public. OTOH
372 * a storage that is marked as not publicly available will trigger the file
373 * publishing mechanisms of TYPO3.
374 *
375 * @return boolean
376 */
377 public function isPublic() {
378 return $this->hasCapability(self::CAPABILITY_PUBLIC);
379 }
380
381 /**
382 * Returns TRUE if this storage is writable. This is determined by the
383 * driver and the storage configuration; user permissions are not taken into account.
384 *
385 * @return boolean
386 */
387 public function isWritable() {
388 return $this->hasCapability(self::CAPABILITY_WRITABLE);
389 }
390
391 /**
392 * Returns TRUE if this storage is browsable by a (backend) user of TYPO3.
393 *
394 * @return boolean
395 */
396 public function isBrowsable() {
397 return $this->isOnline() && $this->hasCapability(self::CAPABILITY_BROWSABLE);
398 }
399
400 /**
401 * Returns TRUE if the identifiers used by this storage are case-sensitive
402 *
403 * @return boolean
404 */
405 public function usesCaseSensitiveIdentifiers() {
406 return $this->driver->isCaseSensitiveFileSystem();
407 }
408
409 /**
410 * Returns TRUE if this storage is browsable by a (backend) user of TYPO3.
411 *
412 * @return boolean
413 */
414 public function isOnline() {
415 if ($this->isOnline === NULL) {
416 if ($this->getUid() === 0) {
417 $this->isOnline = TRUE;
418 }
419 // the storage is not marked as online for a longer time
420 if ($this->storageRecord['is_online'] == 0) {
421 $this->isOnline = FALSE;
422 }
423 if ($this->isOnline !== FALSE) {
424 // all files are ALWAYS available in the frontend
425 if (TYPO3_MODE === 'FE') {
426 $this->isOnline = TRUE;
427 } else {
428 // check if the storage is disabled temporary for now
429 $registryObject = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Registry');
430 $offlineUntil = $registryObject->get('core', 'sys_file_storage-' . $this->getUid() . '-offline-until');
431 if ($offlineUntil && $offlineUntil > time()) {
432 $this->isOnline = FALSE;
433 } else {
434 $this->isOnline = TRUE;
435 }
436 }
437 }
438 }
439 return $this->isOnline;
440 }
441
442 /**
443 * blow the "fuse" and mark the storage as offline
444 * can only be modified by an admin
445 * typically this is only done if the configuration is wrong
446 */
447 public function markAsPermanentlyOffline() {
448 if ($this->getUid() > 0) {
449 // @todo: move this to the storage repository
450 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('sys_file_storage', 'uid=' . intval($this->getUid()), array('is_online' => 0));
451 }
452 $this->storageRecord['is_online'] = 0;
453 $this->isOnline = FALSE;
454 }
455
456 /**
457 * mark this storage as offline
458 *
459 * non-permanent: this typically happens for remote storages
460 * that are "flaky" and not available all the time
461 * mark this storage as offline for the next 5 minutes
462 *
463 * @return void
464 */
465 public function markAsTemporaryOffline() {
466 $registryObject = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Registry');
467 $registryObject->set('core', 'sys_file_storage-' . $this->getUid() . '-offline-until', time() + 60 * 5);
468 $this->storageRecord['is_online'] = 0;
469 $this->isOnline = FALSE;
470 }
471
472 /*********************************
473 * User Permissions / File Mounts
474 ********************************/
475 /**
476 * Adds a filemount as a "filter" for users to only work on a subset of a
477 * storage object
478 *
479 * @param string $folderIdentifier
480 * @param array $additionalData
481 *
482 * @throws Exception\FolderDoesNotExistException
483 * @return void
484 */
485 public function addFileMount($folderIdentifier, $additionalData = array()) {
486 // check for the folder before we add it as a filemount
487 if ($this->driver->folderExists($folderIdentifier) === FALSE) {
488 // if there is an error, this is important and should be handled
489 // as otherwise the user would see the whole storage without any restrictions for the filemounts
490 throw new Exception\FolderDoesNotExistException('Folder for file mount ' . $folderIdentifier . ' does not exist.', 1334427099);
491 }
492 $folderObject = $this->driver->getFolder($folderIdentifier);
493 if (empty($additionalData)) {
494 $additionalData = array(
495 'path' => $folderIdentifier,
496 'title' => $folderIdentifier,
497 'folder' => $folderObject
498 );
499 } else {
500 $additionalData['folder'] = $folderObject;
501 if (!isset($additionalData['title'])) {
502 $additionalData['title'] = $folderIdentifier;
503 }
504 }
505 $this->fileMounts[$folderIdentifier] = $additionalData;
506 }
507
508 /**
509 * Returns all file mounts that are registered with this storage.
510 *
511 * @return array
512 */
513 public function getFileMounts() {
514 return $this->fileMounts;
515 }
516
517 /**
518 * Checks if the given subject is within one of the registered user
519 * filemounts. If not, working with the file is not permitted for the user.
520 *
521 * @param $subject
522 * @return boolean
523 */
524 public function isWithinFileMountBoundaries($subject) {
525 if (!$this->evaluatePermissions) {
526 return TRUE;
527 }
528 $isWithinFilemount = FALSE;
529 if (!$subject) {
530 $subject = $this->getRootLevelFolder();
531 }
532 $identifier = $subject->getIdentifier();
533
534 // Allow access to processing folder
535 if ($this->driver->isWithin($this->getProcessingFolder(), $identifier)) {
536 $isWithinFilemount = TRUE;
537 } else {
538 // Check if the identifier of the subject is within at
539 // least one of the file mounts
540 foreach ($this->fileMounts as $fileMount) {
541 if ($this->driver->isWithin($fileMount['folder'], $identifier)) {
542 $isWithinFilemount = TRUE;
543 break;
544 }
545 }
546 }
547 return $isWithinFilemount;
548 }
549
550 /**
551 * @param boolean $evaluatePermissions
552 */
553 public function setEvaluatePermissions($evaluatePermissions) {
554 $this->evaluatePermissions = (boolean) $evaluatePermissions;
555 }
556
557 /**
558 * Sets the user permissions of the storage
559 *
560 * @param array $userPermissions
561 * @return void
562 */
563 public function setUserPermissions(array $userPermissions) {
564 $this->userPermissions = $userPermissions;
565 }
566
567 /**
568 * Check if the ACL settings allow for a certain action
569 * (is a user allowed to read a file or copy a folder)
570 *
571 * @param string $action
572 * @param string $type either File or Folder
573 * @return boolean
574 */
575 public function checkUserActionPermission($action, $type) {
576 if (!$this->evaluatePermissions) {
577 return TRUE;
578 }
579
580 $allow = FALSE;
581 if (!empty($this->userPermissions[strtolower($action) . ucfirst(strtolower($type))])) {
582 $allow = TRUE;
583 }
584
585 return $allow;
586 }
587
588 /**
589 * Check if a file operation (= action) is allowed on a
590 * File/Folder/Storage (= subject).
591 *
592 * This method, by design, does not throw exceptions or do logging.
593 * Besides the usage from other methods in this class, it is also used by
594 * the File List UI to check whether an action is allowed and whether action
595 * related UI elements should thus be shown (move icon, edit icon, etc.)
596 *
597 * @param string $action, can be read, write, delete
598 * @param FileInterface $file
599 * @return boolean
600 */
601 public function checkFileActionPermission($action, FileInterface $file) {
602 $isProcessedFile = $file instanceof ProcessedFile;
603 // Check 1: Does the user have permission to perform the action? e.g. "readFile"
604 if (!$isProcessedFile && $this->checkUserActionPermission($action, 'File') === FALSE) {
605 return FALSE;
606 }
607 // Check 2: No action allowed on files for denied file extensions
608 if (!$this->checkFileExtensionPermission($file->getName())) {
609 return FALSE;
610 }
611 // Check 3: Does the user have the right to perform the action?
612 // (= is he within the file mount borders)
613 if (!$isProcessedFile && !$this->isWithinFileMountBoundaries($file)) {
614 return FALSE;
615 }
616 $isReadCheck = FALSE;
617 if (in_array($action, array('read', 'copy', 'move'), TRUE)) {
618 $isReadCheck = TRUE;
619 }
620 $isWriteCheck = FALSE;
621 if (in_array($action, array('add', 'write', 'move', 'rename', 'unzip', 'delete'), TRUE)) {
622 $isWriteCheck = TRUE;
623 }
624
625 $isMissing = FALSE;
626 if (!$isProcessedFile && $file instanceof File) {
627 $isMissing = $file->isMissing();
628 }
629
630 // Check 4: Check the capabilities of the storage (and the driver)
631 if ($isWriteCheck && ($isMissing || !$this->isWritable())) {
632 return FALSE;
633 }
634 // Check 5: "File permissions" of the driver (only when file isn't marked as missing)
635 if (!$isMissing) {
636 $filePermissions = $this->driver->getFilePermissions($file);
637 if ($isReadCheck && !$filePermissions['r']) {
638 return FALSE;
639 }
640 if ($isWriteCheck && !$filePermissions['w']) {
641 return FALSE;
642 }
643 }
644 return TRUE;
645 }
646
647 /**
648 * Check if a folder operation (= action) is allowed on a Folder
649 *
650 * This method, by design, does not throw exceptions or do logging.
651 * See the checkFileActionPermission() method above for the reasons.
652 *
653 * @param string $action
654 * @param Folder $folder
655 * @return boolean
656 */
657 public function checkFolderActionPermission($action, Folder $folder = NULL) {
658 // Check 1: Does the user have permission to perform the action? e.g. "writeFolder"
659 if ($this->checkUserActionPermission($action, 'Folder') === FALSE) {
660 return FALSE;
661 }
662
663 // If we do not have a folder here, we cannot do further checks
664 if ($folder === NULL) {
665 return TRUE;
666 }
667
668 // Check 2: Does the user has the right to perform the action?
669 // (= is he within the file mount borders)
670 if (!$this->isWithinFileMountBoundaries($folder)) {
671 return FALSE;
672 }
673 $isReadCheck = FALSE;
674 if (in_array($action, array('read', 'copy'), TRUE)) {
675 $isReadCheck = TRUE;
676 }
677 $isWriteCheck = FALSE;
678 if (in_array($action, array('add', 'move', 'write', 'delete', 'rename'), TRUE)) {
679 $isWriteCheck = TRUE;
680 }
681 // Check 3: Check the capabilities of the storage (and the driver)
682 if ($isReadCheck && !$this->isBrowsable()) {
683 return FALSE;
684 }
685 if ($isWriteCheck && !$this->isWritable()) {
686 return FALSE;
687 }
688 // Check 4: "Folder permissions" of the driver
689 $folderPermissions = $this->driver->getFolderPermissions($folder);
690 if ($isReadCheck && !$folderPermissions['r']) {
691 return FALSE;
692 }
693 if ($isWriteCheck && !$folderPermissions['w']) {
694 return FALSE;
695 }
696 return TRUE;
697 }
698
699 /**
700 * If the fileName is given, check it against the
701 * TYPO3_CONF_VARS[BE][fileDenyPattern] + and if the file extension is allowed
702 *
703 * @param string $fileName Full filename
704 * @return boolean TRUE if extension/filename is allowed
705 */
706 protected function checkFileExtensionPermission($fileName) {
707 if (!$this->evaluatePermissions) {
708 return TRUE;
709 }
710 $fileName = $this->driver->sanitizeFileName($fileName);
711 $isAllowed = GeneralUtility::verifyFilenameAgainstDenyPattern($fileName);
712 if ($isAllowed) {
713 $fileInfo = GeneralUtility::split_fileref($fileName);
714 // Set up the permissions for the file extension
715 $fileExtensionPermissions = $GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']['webspace'];
716 $fileExtensionPermissions['allow'] = GeneralUtility::uniqueList(strtolower($fileExtensionPermissions['allow']));
717 $fileExtensionPermissions['deny'] = GeneralUtility::uniqueList(strtolower($fileExtensionPermissions['deny']));
718 $fileExtension = strtolower($fileInfo['fileext']);
719 if ($fileExtension !== '') {
720 // If the extension is found amongst the allowed types, we return TRUE immediately
721 if ($fileExtensionPermissions['allow'] === '*' || GeneralUtility::inList($fileExtensionPermissions['allow'], $fileExtension)) {
722 return TRUE;
723 }
724 // If the extension is found amongst the denied types, we return FALSE immediately
725 if ($fileExtensionPermissions['deny'] === '*' || GeneralUtility::inList($fileExtensionPermissions['deny'], $fileExtension)) {
726 return FALSE;
727 }
728 // If no match we return TRUE
729 return TRUE;
730 } else {
731 if ($fileExtensionPermissions['allow'] === '*') {
732 return TRUE;
733 }
734 if ($fileExtensionPermissions['deny'] === '*') {
735 return FALSE;
736 }
737 return TRUE;
738 }
739 }
740 return FALSE;
741 }
742
743 /**
744 * Assure read permission for given folder
745 *
746 * @param Folder $folder If a folder is given, mountpoits are checked. If not only user folder read permissions are checked.
747 * @return void
748 * @throws Exception\InsufficientFolderAccessPermissionsException
749 */
750 protected function assureFolderReadPermission(Folder $folder = NULL) {
751 if (!$this->checkFolderActionPermission('read', $folder)) {
752 throw new Exception\InsufficientFolderAccessPermissionsException('You are not allowed to access the given folder', 1375955684);
753 }
754 }
755
756 /**
757 * Assure delete permission for given folder
758 *
759 * @param Folder $folder If a folder is given, mountpoits are checked. If not only user folder delete permissions are checked.
760 * @param boolean $checkDeleteRecursively
761 * @return void
762 * @throws Exception\InsufficientFolderAccessPermissionsException
763 * @throws Exception\InsufficientFolderWritePermissionsException
764 * @throws Exception\InsufficientUserPermissionsException
765 */
766 protected function assureFolderDeletePermission(Folder $folder, $checkDeleteRecursively) {
767 // Check user permissions for recursive deletion if it is requested
768 if ($checkDeleteRecursively && !$this->checkUserActionPermission('recursivedelete', 'Folder')) {
769 throw new Exception\InsufficientUserPermissionsException('You are not allowed to delete folders recursively', 1377779423);
770 }
771 // Check user action permission
772 if (!$this->checkFolderActionPermission('delete', $folder)) {
773 throw new Exception\InsufficientFolderAccessPermissionsException('You are not allowed to delete the given folder', 1377779039);
774 }
775 // Check if the user has write permissions to folders
776 // Would be good if we could check for actual write permissions in the containig folder
777 // but we cannot since we have no access to the containing folder of this file.
778 if (!$this->checkUserActionPermission('write', 'Folder')) {
779 throw new Exception\InsufficientFolderWritePermissionsException('Writing to folders is not allowed.', 1377779111);
780 }
781 }
782
783 /**
784 * Assure read permission for given file
785 *
786 * @param FileInterface $file
787 * @return void
788 * @throws Exception\InsufficientFileAccessPermissionsException
789 * @throws Exception\IllegalFileExtensionException
790 */
791 protected function assureFileReadPermission(FileInterface $file) {
792 if (!$this->checkFileActionPermission('read', $file)) {
793 throw new Exception\InsufficientFileAccessPermissionsException('You are not allowed to access that file.', 1375955429);
794 }
795 if (!$this->checkFileExtensionPermission($file->getName())) {
796 throw new Exception\IllegalFileExtensionException('You are not allowed to use that file extension', 1375955430);
797 }
798 }
799
800 /**
801 * Assure write permission for given file
802 *
803 * @param FileInterface $file
804 * @return void
805 * @throws Exception\IllegalFileExtensionException
806 * @throws Exception\InsufficientFileWritePermissionsException
807 * @throws Exception\InsufficientUserPermissionsException
808 */
809 protected function assureFileWritePermissions(FileInterface $file) {
810 // Check if user is allowed to write the file and $file is writable
811 if (!$this->checkFileActionPermission('write', $file)) {
812 throw new Exception\InsufficientFileWritePermissionsException('Writing to file "' . $file->getIdentifier() . '" is not allowed.', 1330121088);
813 }
814 if (!$this->checkFileExtensionPermission($file->getName())) {
815 throw new Exception\IllegalFileExtensionException('You are not allowed to edit a file with extension "' . $file->getExtension() . '"', 1366711933);
816 }
817 }
818
819 /**
820 * Assure delete permission for given file
821 *
822 * @param FileInterface $file
823 * @return void
824 * @throws Exception\IllegalFileExtensionException
825 * @throws Exception\InsufficientFileWritePermissionsException
826 * @throws Exception\InsufficientFolderWritePermissionsException
827 */
828 protected function assureFileDeletePermissions(FileInterface $file) {
829 // Check for disallowed file extensions
830 if (!$this->checkFileExtensionPermission($file->getName())) {
831 throw new Exception\IllegalFileExtensionException('You are not allowed to delete a file with extension "' . $file->getExtension() . '"', 1377778916);
832 }
833 // Check further permissions if file is not a processed file
834 if (!$file instanceof ProcessedFile) {
835 // Check if user is allowed to delete the file and $file is writable
836 if (!$this->checkFileActionPermission('delete', $file)) {
837 throw new Exception\InsufficientFileWritePermissionsException('You are not allowed to delete the file "' . $file->getIdentifier() . '"', 1319550425);
838 }
839 // Check if the user has write permissions to folders
840 // Would be good if we could check for actual write permissions in the containig folder
841 // but we cannot since we have no access to the containing folder of this file.
842 if (!$this->checkUserActionPermission('write', 'Folder')) {
843 throw new Exception\InsufficientFolderWritePermissionsException('Writing to folders is not allowed.', 1377778702);
844 }
845 }
846 }
847
848 /**
849 * Check if a file has the permission to be uploaded to a Folder/Storage,
850 * if not throw an exception
851 *
852 * @param string $localFilePath the temporary file name from $_FILES['file1']['tmp_name']
853 * @param Folder $targetFolder
854 * @param string $targetFileName the destination file name $_FILES['file1']['name']
855 *
856 * @throws Exception\InsufficientFolderWritePermissionsException
857 * @throws Exception\UploadException
858 * @throws Exception\IllegalFileExtensionException
859 * @throws Exception\UploadSizeException
860 * @throws Exception\InsufficientUserPermissionsException
861 * @return void
862 */
863 protected function assureFileAddPermissions($localFilePath, $targetFolder, $targetFileName) {
864 // Check for a valid file extension
865 if (!$this->checkFileExtensionPermission($targetFileName) || ($localFilePath && !$this->checkFileExtensionPermission($localFilePath))) {
866 throw new Exception\IllegalFileExtensionException('Extension of file name is not allowed in "' . $targetFileName . '"!', 1322120271);
867 }
868 // Makes sure the user is allowed to upload
869 if (!$this->checkUserActionPermission('add', 'File')) {
870 throw new Exception\InsufficientUserPermissionsException('You are not allowed to add files to this storage "' . $this->getUid() . '"', 1376992145);
871 }
872 // Check if targetFolder is writable
873 if (!$this->checkFolderActionPermission('write', $targetFolder)) {
874 throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $targetFolder->getIdentifier() . '"', 1322120356);
875 }
876 }
877
878 /**
879 * Check if a file has the permission to be uploaded to a Folder/Storage,
880 * if not throw an exception
881 *
882 * @param string $localFilePath the temporary file name from $_FILES['file1']['tmp_name']
883 * @param Folder $targetFolder
884 * @param string $targetFileName the destination file name $_FILES['file1']['name']
885 * @param integer $uploadedFileSize
886 *
887 * @throws Exception\InsufficientFolderWritePermissionsException
888 * @throws Exception\UploadException
889 * @throws Exception\IllegalFileExtensionException
890 * @throws Exception\UploadSizeException
891 * @throws Exception\InsufficientUserPermissionsException
892 * @return void
893 */
894 protected function assureFileUploadPermissions($localFilePath, $targetFolder, $targetFileName, $uploadedFileSize) {
895 // Makes sure this is an uploaded file
896 if (!is_uploaded_file($localFilePath)) {
897 throw new Exception\UploadException('The upload has failed, no uploaded file found!', 1322110455);
898 }
899 // Max upload size (kb) for files.
900 $maxUploadFileSize = GeneralUtility::getMaxUploadFileSize() * 1024;
901 if ($uploadedFileSize >= $maxUploadFileSize) {
902 unlink($localFilePath);
903 throw new Exception\UploadSizeException('The uploaded file exceeds the size-limit of ' . $maxUploadFileSize . ' bytes', 1322110041);
904 }
905 $this->assureFileAddPermissions($localFilePath, $targetFolder, $targetFileName);
906 }
907
908 /**
909 * Checks for permissions to move a file.
910 *
911 * @throws \RuntimeException
912 * @throws Exception\InsufficientFolderAccessPermissionsException
913 * @throws Exception\InsufficientUserPermissionsException
914 * @throws Exception\IllegalFileExtensionException
915 * @param FileInterface $file
916 * @param Folder $targetFolder
917 * @param string $targetFileName
918 * @return void
919 */
920 protected function assureFileMovePermissions(FileInterface $file, Folder $targetFolder, $targetFileName) {
921 // Check if targetFolder is within this storage
922 if ($this->getUid() !== $targetFolder->getStorage()->getUid()) {
923 throw new \RuntimeException();
924 }
925 // Check for a valid file extension
926 if (!$this->checkFileExtensionPermission($targetFileName)) {
927 throw new Exception\IllegalFileExtensionException('Extension of file name is not allowed in "' . $targetFileName . '"!', 1378243279);
928 }
929 // Check if user is allowed to move and $file is readable and writable
930 if (!$file->getStorage()->checkFileActionPermission('move', $file)) {
931 throw new Exception\InsufficientUserPermissionsException('You are not allowed to move files to storage "' . $this->getUid() . '"', 1319219349);
932 }
933 // Check if target folder is writable
934 if (!$this->checkFolderActionPermission('write', $targetFolder)) {
935 throw new Exception\InsufficientFolderAccessPermissionsException('You are not allowed to write to the target folder "' . $targetFolder->getIdentifier() . '"', 1319219349);
936 }
937 }
938
939 /**
940 * Checks for permissions to rename a file.
941 *
942 * @param FileInterface $file
943 * @param string $targetFileName
944 * @throws Exception\InsufficientFileWritePermissionsException
945 * @throws Exception\IllegalFileExtensionException
946 * @throws Exception\InsufficientFileReadPermissionsException
947 * @throws Exception\InsufficientUserPermissionsException
948 * @return void
949 */
950 protected function assureFileRenamePermissions(FileInterface $file, $targetFileName) {
951 // Check if file extension is allowed
952 if (!$this->checkFileExtensionPermission($targetFileName) || !$this->checkFileExtensionPermission($file->getName())) {
953 throw new Exception\IllegalFileExtensionException('You are not allowed to rename a file with to this extension', 1371466663);
954 }
955 // Check if user is allowed to rename
956 if (!$this->checkFileActionPermission('rename', $file)) {
957 throw new Exception\InsufficientUserPermissionsException('You are not allowed to rename files."', 1319219349);
958 }
959 // Check if the user is allowed to write to folders
960 // Although it would be good to check, we cannot check here if the folder actually is writable
961 // because we do not know in which folder the file resides.
962 // So we rely on the driver to throw an exception in case the renaming failed.
963 if (!$this->checkFolderActionPermission('write')) {
964 throw new Exception\InsufficientFileWritePermissionsException('You are not allowed to write to folders', 1319219349);
965 }
966 }
967
968 /**
969 * Check if a file has the permission to be copied on a File/Folder/Storage,
970 * if not throw an exception
971 *
972 * @param FileInterface $file
973 * @param Folder $targetFolder
974 * @param string $targetFileName
975 *
976 * @throws Exception
977 * @throws Exception\InsufficientFolderWritePermissionsException
978 * @throws Exception\IllegalFileExtensionException
979 * @throws Exception\InsufficientFileReadPermissionsException
980 * @throws Exception\InsufficientUserPermissionsException
981 * @return void
982 */
983 protected function assureFileCopyPermissions(FileInterface $file, Folder $targetFolder, $targetFileName) {
984 // Check if targetFolder is within this storage, this should never happen
985 if ($this->getUid() != $targetFolder->getStorage()->getUid()) {
986 throw new Exception('The operation of the folder cannot be called by this storage "' . $this->getUid() . '"', 1319550405);
987 }
988 // Check if user is allowed to copy
989 if (!$file->getStorage()->checkFileActionPermission('copy', $file)) {
990 throw new Exception\InsufficientFileReadPermissionsException('You are not allowed to copy the file "' . $file->getIdentifier() . '"', 1319550425);
991 }
992 // Check if targetFolder is writable
993 if (!$this->checkFolderActionPermission('write', $targetFolder)) {
994 throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $targetFolder->getIdentifier() . '"', 1319550435);
995 }
996 // Check for a valid file extension
997 if (!$this->checkFileExtensionPermission($targetFileName) || !$this->checkFileExtensionPermission($file->getName())) {
998 throw new Exception\IllegalFileExtensionException('You are not allowed to copy a file of that type.', 1319553317);
999 }
1000 }
1001
1002 /**
1003 * Check if a file has the permission to be copied on a File/Folder/Storage,
1004 * if not throw an exception
1005 *
1006 * @param FolderInterface $folderToCopy
1007 * @param FolderInterface $targetParentFolder
1008 * @return void
1009 *
1010 * @throws Exception
1011 * @throws Exception\InsufficientFolderWritePermissionsException
1012 * @throws Exception\IllegalFileExtensionException
1013 * @throws Exception\InsufficientFileReadPermissionsException
1014 * @throws Exception\InsufficientUserPermissionsException
1015 * @throws \RuntimeException
1016 */
1017 protected function assureFolderCopyPermissions(FolderInterface $folderToCopy, FolderInterface $targetParentFolder) {
1018 // Check if targetFolder is within this storage, this should never happen
1019 if ($this->getUid() !== $targetParentFolder->getStorage()->getUid()) {
1020 throw new Exception('The operation of the folder cannot be called by this storage "' . $this->getUid() . '"', 1377777624);
1021 }
1022 if (!$folderToCopy instanceof Folder) {
1023 throw new \RuntimeException('The folder "' . $folderToCopy->getIdentifier() . '" to copy is not of type Folder.', 1384209020);
1024 }
1025 // Check if user is allowed to copy and the folder is readable
1026 if (!$folderToCopy->getStorage()->checkFolderActionPermission('copy', $folderToCopy)) {
1027 throw new Exception\InsufficientFileReadPermissionsException('You are not allowed to copy the folder "' . $folderToCopy->getIdentifier() . '"', 1377777629);
1028 }
1029 if (!$targetParentFolder instanceof Folder) {
1030 throw new \RuntimeException('The target folder "' . $targetParentFolder->getIdentifier() . '" is not of type Folder.', 1384209021);
1031 }
1032 // Check if targetFolder is writable
1033 if (!$this->checkFolderActionPermission('write', $targetParentFolder)) {
1034 throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $targetParentFolder->getIdentifier() . '"', 1377777635);
1035 }
1036 }
1037
1038 /**
1039 * Check if a file has the permission to be copied on a File/Folder/Storage,
1040 * if not throw an exception
1041 *
1042 * @param FolderInterface $folderToMove
1043 * @param FolderInterface $targetParentFolder
1044 *
1045 * @throws \InvalidArgumentException
1046 * @throws Exception\InsufficientFolderWritePermissionsException
1047 * @throws Exception\IllegalFileExtensionException
1048 * @throws Exception\InsufficientFileReadPermissionsException
1049 * @throws Exception\InsufficientUserPermissionsException
1050 * @throws \RuntimeException
1051 * @return void
1052 */
1053 protected function assureFolderMovePermissions(FolderInterface $folderToMove, FolderInterface $targetParentFolder) {
1054 // Check if targetFolder is within this storage, this should never happen
1055 if ($this->getUid() !== $targetParentFolder->getStorage()->getUid()) {
1056 throw new \InvalidArgumentException('Cannot move a folder into a folder that does not belong to this storage.', 1325777289);
1057 }
1058 if (!$folderToMove instanceof Folder) {
1059 throw new \RuntimeException('The folder "' . $folderToMove->getIdentifier() . '" to move is not of type Folder.', 1384209022);
1060 }
1061 // Check if user is allowed to move and the folder is writable
1062 // In fact we would need to check if the parent folder of the folder to move is writable also
1063 // But as of now we cannot extract the parent folder from this folder
1064 if (!$folderToMove->getStorage()->checkFolderActionPermission('move', $folderToMove)) {
1065 throw new Exception\InsufficientFileReadPermissionsException('You are not allowed to copy the folder "' . $folderToMove->getIdentifier() . '"', 1377778045);
1066 }
1067 if (!$targetParentFolder instanceof Folder) {
1068 throw new \RuntimeException('The target folder "' . $targetParentFolder->getIdentifier() . '" is not of type Folder.', 1384209023);
1069 }
1070 // Check if targetFolder is writable
1071 if (!$this->checkFolderActionPermission('write', $targetParentFolder)) {
1072 throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $targetParentFolder->getIdentifier() . '"', 1377778049);
1073 }
1074 }
1075
1076 /********************
1077 * FILE ACTIONS
1078 ********************/
1079 /**
1080 * Moves a file from the local filesystem to this storage.
1081 *
1082 * @param string $localFilePath The file on the server's hard disk to add.
1083 * @param Folder $targetFolder The target path, without the fileName
1084 * @param string $targetFileName The fileName. If not set, the local file name is used.
1085 * @param string $conflictMode possible value are 'cancel', 'replace', 'changeName'
1086 *
1087 * @throws \InvalidArgumentException
1088 * @throws Exception\ExistingTargetFileNameException
1089 * @return FileInterface
1090 */
1091 public function addFile($localFilePath, Folder $targetFolder, $targetFileName = '', $conflictMode = 'changeName') {
1092 $localFilePath = PathUtility::getCanonicalPath($localFilePath);
1093 if (!file_exists($localFilePath)) {
1094 throw new \InvalidArgumentException('File "' . $localFilePath . '" does not exist.', 1319552745);
1095 }
1096 $this->assureFileAddPermissions($localFilePath, $targetFolder, $targetFileName);
1097 $targetFolder = $targetFolder ?: $this->getDefaultFolder();
1098 $targetFileName = $targetFileName ?: PathUtility::basename($localFilePath);
1099 if ($conflictMode === 'cancel' && $this->driver->fileExistsInFolder($targetFileName, $targetFolder)) {
1100 throw new Exception\ExistingTargetFileNameException('File "' . $targetFileName . '" already exists in folder ' . $targetFolder->getIdentifier(), 1322121068);
1101 } elseif ($conflictMode === 'changeName') {
1102 $targetFileName = $this->getUniqueName($targetFolder, $targetFileName);
1103 }
1104 // We do not care whether the file exists if $conflictMode is "replace",
1105 // so just use the name as is in that case
1106 $this->emitPreFileAddSignal($targetFileName, $targetFolder);
1107
1108 $file = $this->driver->addFile($localFilePath, $targetFolder, $targetFileName);
1109
1110 $this->emitPostFileAddSignal($file, $targetFolder);
1111
1112 return $file;
1113 }
1114
1115 /**
1116 * Updates a processed file with a new file from the local filesystem.
1117 *
1118 * @param $localFilePath
1119 * @param ProcessedFile $processedFile
1120 * @return FileInterface
1121 * @throws \InvalidArgumentException
1122 * @internal use only
1123 */
1124 public function updateProcessedFile($localFilePath, ProcessedFile $processedFile) {
1125 if (!file_exists($localFilePath)) {
1126 throw new \InvalidArgumentException('File "' . $localFilePath . '" does not exist.', 1319552745);
1127 }
1128 $file = $this->driver->addFile($localFilePath, $this->getProcessingFolder(), $processedFile->getName());
1129 if ($file instanceof File) {
1130 $file->setIndexable(FALSE);
1131 }
1132 return $file;
1133 }
1134
1135 /**
1136 * Creates a (cryptographic) hash for a file.
1137 *
1138 * @param FileInterface $fileObject
1139 * @param string $hash
1140 * @return string
1141 */
1142 public function hashFile(FileInterface $fileObject, $hash) {
1143 return $this->hashFileByIdentifier($fileObject->getIdentifier(), $hash);
1144 }
1145
1146 /**
1147 * Creates a (cryptographic) hash for a fileIdentifier.
1148
1149 * @param string $fileIdentifier
1150 * @param string $hash
1151 *
1152 * @return string
1153 */
1154 public function hashFileByIdentifier($fileIdentifier, $hash) {
1155 return $this->driver->hash($fileIdentifier, $hash);
1156 }
1157
1158 /**
1159 * Hashes a file identifier, taking the case sensitivity of the file system
1160 * into account. This helps mitigating problems with case-insensitive
1161 * databases.
1162 *
1163 * @param string|FileInterface $file
1164 * @return string
1165 */
1166 public function hashFileIdentifier($file) {
1167 if (is_object($file) && $file instanceof FileInterface) {
1168 /** @var FileInterface $file */
1169 $file = $file->getIdentifier();
1170 }
1171 return $this->driver->hashIdentifier($file);
1172 }
1173
1174 /**
1175 * Returns a publicly accessible URL for a file.
1176 *
1177 * WARNING: Access to the file may be restricted by further means, e.g.
1178 * some web-based authentication. You have to take care of this yourself.
1179 *
1180 * @param ResourceInterface $resourceObject The file or folder object
1181 * @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)
1182 * @return string
1183 */
1184 public function getPublicUrl(ResourceInterface $resourceObject, $relativeToCurrentScript = FALSE) {
1185 $publicUrl = NULL;
1186 if ($this->isOnline()) {
1187 // Pre-process the public URL by an accordant slot
1188 $this->emitPreGeneratePublicUrl($resourceObject, $relativeToCurrentScript, array('publicUrl' => &$publicUrl));
1189 // If slot did not handle the signal, use the default way to determine public URL
1190 if ($publicUrl === NULL) {
1191 $publicUrl = $this->driver->getPublicUrl($resourceObject, $relativeToCurrentScript);
1192 }
1193 }
1194 return $publicUrl;
1195 }
1196
1197 /**
1198 * Passes a file to the File Processing Services and returns the resulting ProcessedFile object.
1199 *
1200 * @param FileInterface $fileObject The file object
1201 * @param string $context
1202 * @param array $configuration
1203 *
1204 * @return ProcessedFile
1205 * @throws \InvalidArgumentException
1206 */
1207 public function processFile(FileInterface $fileObject, $context, array $configuration) {
1208 if ($fileObject->getStorage() !== $this) {
1209 throw new \InvalidArgumentException('Cannot process files of foreign storage', 1353401835);
1210 }
1211 $processedFile = $this->getFileProcessingService()->processFile($fileObject, $this, $context, $configuration);
1212
1213 return $processedFile;
1214 }
1215
1216 /**
1217 * Copies a file from the storage for local processing.
1218 *
1219 * @param FileInterface $fileObject
1220 * @param boolean $writable
1221 * @return string Path to local file (either original or copied to some temporary local location)
1222 */
1223 public function getFileForLocalProcessing(FileInterface $fileObject, $writable = TRUE) {
1224 $filePath = $this->driver->getFileForLocalProcessing($fileObject, $writable);
1225 // @todo: shouldn't this go in the driver? this function is called from the indexing service
1226 // @todo: and recursively calls itself over and over again, this is left out for now with getModificationTime()
1227 // touch($filePath, $fileObject->getModificationTime());
1228 return $filePath;
1229 }
1230
1231 /**
1232 * Get file by identifier
1233 *
1234 * @param string $identifier
1235 * @return FileInterface
1236 */
1237 public function getFile($identifier) {
1238 $file = $this->getFileFactory()->getFileObjectByStorageAndIdentifier($this->getUid(), $identifier);
1239 if (!$this->driver->fileExists($identifier)) {
1240 $file->setMissing(TRUE);
1241 }
1242 return $file;
1243 }
1244
1245 /**
1246 * Get information about a file
1247 *
1248 * @param FileInterface $fileObject
1249 * @return array
1250 * @internal
1251 */
1252 public function getFileInfo(FileInterface $fileObject) {
1253 return $this->driver->getFileInfo($fileObject);
1254 }
1255
1256 /**
1257 * Get information about a file by its identifier
1258 *
1259 * @param string $identifier
1260 * @param array $propertiesToExtract
1261 * @return array
1262 * @internal
1263 */
1264 public function getFileInfoByIdentifier($identifier, array $propertiesToExtract = array()) {
1265 return $this->driver->getFileInfoByIdentifier($identifier, $propertiesToExtract);
1266 }
1267
1268 /**
1269 * Unsets the file and folder name filters, thus making this storage return unfiltered file lists.
1270 *
1271 * @return void
1272 */
1273 public function unsetFileAndFolderNameFilters() {
1274 $this->fileAndFolderNameFilters = array();
1275 }
1276
1277 /**
1278 * Resets the file and folder name filters to the default values defined in the TYPO3 configuration.
1279 *
1280 * @return void
1281 */
1282 public function resetFileAndFolderNameFiltersToDefault() {
1283 $this->fileAndFolderNameFilters = $GLOBALS['TYPO3_CONF_VARS']['SYS']['fal']['defaultFilterCallbacks'];
1284 }
1285
1286 /**
1287 * Returns the file and folder name filters used by this storage.
1288 *
1289 * @return array
1290 */
1291 public function getFileAndFolderNameFilters() {
1292 return $this->fileAndFolderNameFilters;
1293 }
1294
1295 public function setFileAndFolderNameFilters(array $filters) {
1296 $this->fileAndFolderNameFilters = $filters;
1297 return $this;
1298 }
1299
1300 public function addFileAndFolderNameFilter($filter) {
1301 $this->fileAndFolderNameFilters[] = $filter;
1302 }
1303
1304 /**
1305 * @param string $fileIdentifier
1306 *
1307 * @return string
1308 */
1309 public function getFolderIdentifierFromFileIdentifier($fileIdentifier) {
1310 return $this->driver->getFolderIdentifierForFile($fileIdentifier);
1311 }
1312 /**
1313 * Returns a list of files in a given path, filtered by some custom filter methods.
1314 *
1315 * @see getUnfilteredFileList(), getFileListWithDefaultFilters()
1316 * @param string $path The path to list
1317 * @param integer $start The position to start the listing; if not set or 0, start from the beginning
1318 * @param integer $numberOfItems The number of items to list; if not set, return all items
1319 * @param boolean $useFilters If FALSE, the list is returned without any filtering; otherwise, the filters defined for this storage are used.
1320 * @param boolean $loadIndexRecords If set to TRUE, the index records for all files are loaded from the database. This can greatly improve performance of this method, especially with a lot of files.
1321 * @param boolean $recursive
1322 * @return array Information about the files found.
1323 * @deprecated since 6.2, will be removed two versions later
1324 */
1325 public function getFileList($path, $start = 0, $numberOfItems = 0, $useFilters = TRUE, $loadIndexRecords = TRUE, $recursive = FALSE) {
1326 GeneralUtility::logDeprecatedFunction();
1327 // This also checks for read permissions on folder
1328 $folder = $this->getFolder($path);
1329 $rows = array();
1330 if ($loadIndexRecords) {
1331 $rows = $this->getFileIndexRepository()->findByFolder($folder);
1332 }
1333 $filters = $useFilters == TRUE ? $this->fileAndFolderNameFilters : array();
1334 $items = $this->driver->getFileList($path, $start, $numberOfItems, $filters, $rows, $recursive);
1335
1336 // We should not sort when fetching a recursive list, as these are indexed numerically
1337 if ($recursive === FALSE) {
1338 uksort($items, 'strnatcasecmp');
1339 }
1340
1341 return $items;
1342 }
1343
1344 /**
1345 * @param Folder $folder
1346 * @param int $start
1347 * @param int $maxNumberOfItems
1348 * @param bool $useFilters
1349 * @param bool $recursive
1350 * @return File[]
1351 */
1352 public function getFilesInFolder(Folder $folder, $start = 0, $maxNumberOfItems = 0, $useFilters = TRUE, $recursive = FALSE) {
1353 $this->assureFolderReadPermission($folder);
1354
1355 $rows = $this->getFileIndexRepository()->findByFolder($folder);
1356
1357 $filters = $useFilters == TRUE ? $this->fileAndFolderNameFilters : array();
1358 $fileIdentifiers = $this->driver->getFileIdentifierListInFolder($folder->getIdentifier(), $recursive, $filters);
1359 $fileIdentifiersCount = count($fileIdentifiers);
1360 $items = array();
1361 if ($maxNumberOfItems === 0) {
1362 $maxNumberOfItems = $fileIdentifiersCount;
1363 }
1364 $end = min($fileIdentifiersCount, $start + $maxNumberOfItems);
1365 for ($i = $start; $i < $end; $i++) {
1366 $identifier = $fileIdentifiers[$i];
1367 if (isset($rows[$identifier])) {
1368 $fileObject = $this->getFileFactory()->getFileObject($rows[$identifier]['uid'], $rows[$identifier]);
1369 } else {
1370 $fileObject = $this->getFileFactory()->getFileObjectByStorageAndIdentifier($this->getUid(), $identifier);
1371 }
1372 $key = $fileObject->getName();
1373 while (isset($items[$key])) {
1374 $key .= 'z';
1375 }
1376 $items[$key] = $fileObject;
1377 }
1378 uksort($items, 'strnatcasecmp');
1379
1380 return $items;
1381 }
1382
1383 /**
1384 * @param string $folderIdentifier
1385 * @param boolean $useFilters
1386 * @param boolean $recursive
1387 *
1388 * @return array
1389 */
1390 public function getFileIdentifiersInFolder($folderIdentifier, $useFilters = TRUE, $recursive = FALSE) {
1391 $filters = $useFilters == TRUE ? $this->fileAndFolderNameFilters : array();
1392 return $this->driver->getFileIdentifierListInFolder($folderIdentifier, $recursive, $filters);
1393 }
1394
1395 /**
1396 * Returns TRUE if the specified file exists.
1397 *
1398 * @param string $identifier
1399 * @return boolean
1400 */
1401 public function hasFile($identifier) {
1402 // Allow if identifier is in processing folder
1403 if (!$this->driver->isWithin($this->getProcessingFolder(), $identifier)) {
1404 $this->assureFolderReadPermission();
1405 }
1406 return $this->driver->fileExists($identifier);
1407 }
1408
1409 /**
1410 * Checks if the queried file in the given folder exists.
1411 *
1412 * @param string $fileName
1413 * @param Folder $folder
1414 * @return boolean
1415 */
1416 public function hasFileInFolder($fileName, Folder $folder) {
1417 $this->assureFolderReadPermission($folder);
1418 return $this->driver->fileExistsInFolder($fileName, $folder);
1419 }
1420
1421 /**
1422 * Get contents of a file object
1423 *
1424 * @param FileInterface $file
1425 *
1426 * @throws Exception\InsufficientFileReadPermissionsException
1427 * @return string
1428 */
1429 public function getFileContents($file) {
1430 $this->assureFileReadPermission($file);
1431 return $this->driver->getFileContents($file);
1432 }
1433
1434 /**
1435 * Set contents of a file object.
1436 *
1437 * @param AbstractFile $file
1438 * @param string $contents
1439 *
1440 * @throws \Exception|\RuntimeException
1441 * @throws Exception\InsufficientFileWritePermissionsException
1442 * @throws Exception\InsufficientUserPermissionsException
1443 * @return integer The number of bytes written to the file
1444 */
1445 public function setFileContents(AbstractFile $file, $contents) {
1446 // Check if user is allowed to edit
1447 $this->assureFileWritePermissions($file);
1448 // Call driver method to update the file and update file index entry afterwards
1449 $result = $this->driver->setFileContents($file, $contents);
1450 $this->getIndexer()->updateIndexEntry($file);
1451 return $result;
1452 }
1453
1454 /**
1455 * Creates a new file
1456 *
1457 * previously in \TYPO3\CMS\Core\Utility\File\ExtendedFileUtility::func_newfile()
1458 *
1459 * @param string $fileName
1460 * @param Folder $targetFolderObject
1461 *
1462 * @throws Exception\IllegalFileExtensionException
1463 * @throws Exception\InsufficientFolderWritePermissionsException
1464 * @return FileInterface The file object
1465 */
1466 public function createFile($fileName, Folder $targetFolderObject) {
1467 $this->assureFileAddPermissions('', $targetFolderObject, $fileName);
1468 return $this->driver->createFile($fileName, $targetFolderObject);
1469 }
1470
1471 /**
1472 * Previously in \TYPO3\CMS\Core\Utility\File\ExtendedFileUtility::deleteFile()
1473 *
1474 * @param $fileObject FileInterface
1475 * @throws Exception\InsufficientFileAccessPermissionsException
1476 * @throws Exception\FileOperationErrorException
1477 * @return boolean TRUE if deletion succeeded
1478 */
1479 public function deleteFile($fileObject) {
1480 $this->assureFileDeletePermissions($fileObject);
1481
1482 $this->emitPreFileDeleteSignal($fileObject);
1483
1484 $result = $this->driver->deleteFile($fileObject);
1485 if ($result === FALSE) {
1486 throw new Exception\FileOperationErrorException('Deleting the file "' . $fileObject->getIdentifier() . '\' failed.', 1329831691);
1487 }
1488 // Mark the file object as deleted
1489 if ($fileObject instanceof File) {
1490 $fileObject->setDeleted();
1491 }
1492
1493 $this->emitPostFileDeleteSignal($fileObject);
1494
1495 return TRUE;
1496 }
1497
1498 /**
1499 * Previously in \TYPO3\CMS\Core\Utility\File\ExtendedFileUtility::func_copy()
1500 * copies a source file (from any location) in to the target
1501 * folder, the latter has to be part of this storage
1502 *
1503 * @param FileInterface $file
1504 * @param Folder $targetFolder
1505 * @param string $targetFileName an optional destination fileName
1506 * @param string $conflictMode "overrideExistingFile", "renameNewFile", "cancel
1507 *
1508 * @throws \Exception|Exception\AbstractFileOperationException
1509 * @throws Exception\ExistingTargetFileNameException
1510 * @return FileInterface
1511 */
1512 public function copyFile(FileInterface $file, Folder $targetFolder, $targetFileName = NULL, $conflictMode = 'renameNewFile') {
1513 if ($targetFileName === NULL) {
1514 $targetFileName = $file->getName();
1515 }
1516 $this->assureFileCopyPermissions($file, $targetFolder, $targetFileName);
1517 $this->emitPreFileCopySignal($file, $targetFolder);
1518 // File exists and we should abort, let's abort
1519 if ($conflictMode === 'cancel' && $targetFolder->hasFile($targetFileName)) {
1520 throw new Exception\ExistingTargetFileNameException('The target file already exists.', 1320291063);
1521 }
1522 // File exists and we should find another name, let's find another one
1523 if ($conflictMode === 'renameNewFile' && $targetFolder->hasFile($targetFileName)) {
1524 $targetFileName = $this->getUniqueName($targetFolder, $targetFileName);
1525 }
1526 $sourceStorage = $file->getStorage();
1527 // Call driver method to create a new file from an existing file object,
1528 // and return the new file object
1529 if ($sourceStorage === $this) {
1530 $newFileObject = $this->driver->copyFileWithinStorage($file, $targetFolder, $targetFileName);
1531 } else {
1532 $tempPath = $file->getForLocalProcessing();
1533 $newFileObject = $this->driver->addFile($tempPath, $targetFolder, $targetFileName);
1534 }
1535 $this->emitPostFileCopySignal($file, $targetFolder);
1536 return $newFileObject;
1537 }
1538
1539 /**
1540 * Moves a $file into a $targetFolder
1541 * the target folder has to be part of this storage
1542 *
1543 * previously in \TYPO3\CMS\Core\Utility\File\ExtendedFileUtility::func_move()
1544 *
1545 * @param FileInterface $file
1546 * @param Folder $targetFolder
1547 * @param string $targetFileName an optional destination fileName
1548 * @param string $conflictMode "overrideExistingFile", "renameNewFile", "cancel
1549 *
1550 * @throws Exception\ExistingTargetFileNameException
1551 * @throws \RuntimeException
1552 * @return FileInterface
1553 */
1554 public function moveFile($file, $targetFolder, $targetFileName = NULL, $conflictMode = 'renameNewFile') {
1555 if ($targetFileName === NULL) {
1556 $targetFileName = $file->getName();
1557 }
1558 $this->assureFileMovePermissions($file, $targetFolder, $targetFileName);
1559 if ($targetFolder->hasFile($targetFileName)) {
1560 // File exists and we should abort, let's abort
1561 if ($conflictMode === 'renameNewFile') {
1562 $targetFileName = $this->getUniqueName($targetFolder, $targetFileName);
1563 } elseif ($conflictMode === 'cancel') {
1564 throw new Exception\ExistingTargetFileNameException('The target file already exists', 1329850997);
1565 }
1566 }
1567 $this->emitPreFileMoveSignal($file, $targetFolder);
1568 $sourceStorage = $file->getStorage();
1569 // Call driver method to move the file and update the index entry
1570 try {
1571 if ($sourceStorage === $this) {
1572 $newIdentifier = $this->driver->moveFileWithinStorage($file, $targetFolder, $targetFileName);
1573 if (!$file instanceof AbstractFile) {
1574 throw new \RuntimeException('The given file is not of type AbstractFile.', 1384209025);
1575 }
1576 $file->updateProperties(array('identifier' => $newIdentifier));
1577 } else {
1578 $tempPath = $file->getForLocalProcessing();
1579 $newIdentifier = $this->driver->addFileRaw($tempPath, $targetFolder, $targetFileName);
1580 $sourceStorage->driver->deleteFileRaw($file->getIdentifier());
1581 $file->updateProperties(array('storage' => $this->getUid(), 'identifier' => $newIdentifier));
1582 }
1583 $this->getIndexer()->updateIndexEntry($file);
1584 } catch (\TYPO3\CMS\Core\Exception $e) {
1585 echo $e->getMessage();
1586 }
1587 $this->emitPostFileMoveSignal($file, $targetFolder);
1588 return $file;
1589 }
1590
1591 /**
1592 * Previously in \TYPO3\CMS\Core\Utility\File\ExtendedFileUtility::func_rename()
1593 *
1594 * @param FileInterface $file
1595 * @param string $targetFileName
1596 *
1597 * @throws Exception\InsufficientFileWritePermissionsException
1598 * @throws Exception\InsufficientFileReadPermissionsException
1599 * @throws Exception\InsufficientUserPermissionsException
1600 * @return FileInterface
1601 */
1602 // TODO add $conflictMode setting
1603 public function renameFile($file, $targetFileName) {
1604 // The name should be different from the current.
1605 if ($file->getName() === $targetFileName) {
1606 return $file;
1607 }
1608 $this->assureFileRenamePermissions($file, $targetFileName);
1609 $this->emitPreFileRenameSignal($file, $targetFileName);
1610
1611 // Call driver method to rename the file and update the index entry
1612 try {
1613 $newIdentifier = $this->driver->renameFile($file, $targetFileName);
1614 $file->updateProperties(array('identifier' => $newIdentifier));
1615 $this->getIndexer()->updateIndexEntry($file);
1616 } catch (\RuntimeException $e) {
1617
1618 }
1619
1620 $this->emitPostFileRenameSignal($file, $targetFileName);
1621
1622 return $file;
1623 }
1624
1625 /**
1626 * Replaces a file with a local file (e.g. a freshly uploaded file)
1627 *
1628 * @param FileInterface $file
1629 * @param string $localFilePath
1630 *
1631 * @throws \InvalidArgumentException
1632 * @return FileInterface
1633 */
1634 public function replaceFile(FileInterface $file, $localFilePath) {
1635 $this->assureFileWritePermissions($file);
1636 if (!$this->checkFileExtensionPermission($localFilePath)) {
1637 throw new Exception\IllegalFileExtensionException('Sorce file extension not allowed', 1378132239);
1638 }
1639 if (!file_exists($localFilePath)) {
1640 throw new \InvalidArgumentException('File "' . $localFilePath . '" does not exist.', 1325842622);
1641 }
1642 // TODO check permissions
1643 $this->emitPreFileReplaceSignal($file, $localFilePath);
1644 $result = $this->driver->replaceFile($file, $localFilePath);
1645 $this->getIndexer()->updateIndexEntry($file);
1646 $this->emitPostFileReplaceSignal($file, $localFilePath);
1647 return $result;
1648 }
1649
1650 /**
1651 * Adds an uploaded file into the Storage. Previously in \TYPO3\CMS\Core\Utility\File\ExtendedFileUtility::file_upload()
1652 *
1653 * @param array $uploadedFileData contains information about the uploaded file given by $_FILES['file1']
1654 * @param Folder $targetFolder the target folder
1655 * @param string $targetFileName the file name to be written
1656 * @param string $conflictMode possible value are 'cancel', 'replace'
1657 * @return FileInterface The file object
1658 */
1659 public function addUploadedFile(array $uploadedFileData, Folder $targetFolder = NULL, $targetFileName = NULL, $conflictMode = 'cancel') {
1660 $localFilePath = $uploadedFileData['tmp_name'];
1661 if ($targetFolder === NULL) {
1662 $targetFolder = $this->getDefaultFolder();
1663 }
1664 if ($targetFileName === NULL) {
1665 $targetFileName = $uploadedFileData['name'];
1666 }
1667 // Handling $conflictMode is delegated to addFile()
1668 $this->assureFileUploadPermissions($localFilePath, $targetFolder, $targetFileName, $uploadedFileData['size']);
1669 $resultObject = $this->addFile($localFilePath, $targetFolder, $targetFileName, $conflictMode);
1670 return $resultObject;
1671 }
1672
1673 /********************
1674 * FOLDER ACTIONS
1675 ********************/
1676 /**
1677 * Returns an array with all file objects in a folder and its subfolders, with the file identifiers as keys.
1678 *
1679 * @param Folder $folder
1680 * @return File[]
1681 */
1682 protected function getAllFileObjectsInFolder(Folder $folder) {
1683 $files = array();
1684 $folderQueue = array($folder);
1685 while (!empty($folderQueue)) {
1686 $folder = array_shift($folderQueue);
1687 foreach ($folder->getSubfolders() as $subfolder) {
1688 $folderQueue[] = $subfolder;
1689 }
1690 foreach ($folder->getFiles() as $file) {
1691 $files[$file->getIdentifier()] = $file;
1692 }
1693 }
1694 return $files;
1695 }
1696
1697 /**
1698 * Moves a folder. If you want to move a folder from this storage to another
1699 * one, call this method on the target storage, otherwise you will get an exception.
1700 *
1701 * @param Folder $folderToMove The folder to move.
1702 * @param Folder $targetParentFolder The target parent folder
1703 * @param string $newFolderName
1704 * @param string $conflictMode How to handle conflicts; one of "overrideExistingFile", "renameNewFolder", "cancel
1705 *
1706 * @throws \Exception|\TYPO3\CMS\Core\Exception
1707 * @throws \InvalidArgumentException
1708 * @return Folder
1709 */
1710 public function moveFolder(Folder $folderToMove, Folder $targetParentFolder, $newFolderName = NULL, $conflictMode = 'renameNewFolder') {
1711 // TODO add tests
1712 $this->assureFolderMovePermissions($folderToMove, $targetParentFolder);
1713 $sourceStorage = $folderToMove->getStorage();
1714 $returnObject = NULL;
1715 $newFolderName = $newFolderName ? $newFolderName : $folderToMove->getName();
1716 // TODO check if folder already exists in $targetParentFolder, handle this conflict then
1717 $this->emitPreFolderMoveSignal($folderToMove, $targetParentFolder, $newFolderName);
1718 // Get all file objects now so we are able to update them after moving the folder
1719 $fileObjects = $this->getAllFileObjectsInFolder($folderToMove);
1720 if ($sourceStorage === $this) {
1721 $fileMappings = $this->driver->moveFolderWithinStorage($folderToMove, $targetParentFolder, $newFolderName);
1722 } else {
1723 $fileMappings = $this->moveFolderBetweenStorages($folderToMove, $targetParentFolder, $newFolderName);
1724 }
1725 // Update the identifier and storage of all file objects
1726 foreach ($fileObjects as $oldIdentifier => $fileObject) {
1727 $newIdentifier = $fileMappings[$oldIdentifier];
1728 $fileObject->updateProperties(array('storage' => $this->getUid(), 'identifier' => $newIdentifier));
1729 $this->getIndexer()->updateIndexEntry($fileObject);
1730 }
1731 $returnObject = $this->getFolder($fileMappings[$folderToMove->getIdentifier()]);
1732 $this->emitPostFolderMoveSignal($folderToMove, $targetParentFolder, $newFolderName);
1733 return $returnObject;
1734 }
1735
1736 /**
1737 * Moves the given folder from a different storage to the target folder in this storage.
1738 *
1739 * @param Folder $folderToMove
1740 * @param Folder $targetParentFolder
1741 * @param string $newFolderName
1742 *
1743 * @return boolean
1744 */
1745 protected function moveFolderBetweenStorages(Folder $folderToMove, Folder $targetParentFolder, $newFolderName) {
1746 return $this->getDriver()->moveFolderBetweenStorages($folderToMove, $targetParentFolder, $newFolderName);
1747 }
1748
1749 /**
1750 * Copy folder
1751 *
1752 * @param FolderInterface $folderToCopy The folder to copy
1753 * @param FolderInterface $targetParentFolder The target folder
1754 * @param string $newFolderName
1755 * @param string $conflictMode "overrideExistingFolder", "renameNewFolder", "cancel
1756 * @return Folder The new (copied) folder object
1757 */
1758 public function copyFolder(FolderInterface $folderToCopy, FolderInterface $targetParentFolder, $newFolderName = NULL, $conflictMode = 'renameNewFolder') {
1759 // TODO implement the $conflictMode handling
1760 $this->assureFolderCopyPermissions($folderToCopy, $targetParentFolder);
1761 $returnObject = NULL;
1762 $newFolderName = $newFolderName ? $newFolderName : $folderToCopy->getName();
1763 $this->emitPreFolderCopySignal($folderToCopy, $targetParentFolder, $newFolderName);
1764 $sourceStorage = $folderToCopy->getStorage();
1765 // call driver method to move the file
1766 // that also updates the file object properties
1767 try {
1768 if ($sourceStorage === $this) {
1769 $this->driver->copyFolderWithinStorage($folderToCopy, $targetParentFolder, $newFolderName);
1770 $returnObject = $this->getFolder($targetParentFolder->getSubfolder($newFolderName)->getIdentifier());
1771 } else {
1772 $this->copyFolderBetweenStorages($folderToCopy, $targetParentFolder, $newFolderName);
1773 }
1774 } catch (\TYPO3\CMS\Core\Exception $e) {
1775 echo $e->getMessage();
1776 }
1777 $this->emitPostFolderCopySignal($folderToCopy, $targetParentFolder, $newFolderName);
1778 return $returnObject;
1779 }
1780
1781 /**
1782 * Copy folders between storages
1783 *
1784 * @param Folder $folderToCopy
1785 * @param Folder $targetParentFolder
1786 * @param string $newFolderName
1787 *
1788 * @return boolean
1789 */
1790 protected function copyFolderBetweenStorages(Folder $folderToCopy, Folder $targetParentFolder, $newFolderName) {
1791 return $this->getDriver()->copyFolderBetweenStorages($folderToCopy, $targetParentFolder, $newFolderName);
1792 }
1793
1794 /**
1795 * Previously in \TYPO3\CMS\Core\Utility\File\ExtendedFileUtility::folder_move()
1796 *
1797 * @param Folder $folderObject
1798 * @param string $newName
1799 * @throws \Exception
1800 * @throws \InvalidArgumentException
1801 * @return Folder
1802 */
1803 public function renameFolder($folderObject, $newName) {
1804 // TODO unit tests
1805
1806 // Renaming the folder should check if the parent folder is writable
1807 // We cannot do this however because we cannot extract the parent folder from a folder currently
1808 if (!$this->checkFolderActionPermission('rename', $folderObject)) {
1809 throw new \TYPO3\CMS\Core\Resource\Exception\InsufficientUserPermissionsException('You are not allowed to rename the folder "' . $folderObject->getIdentifier() . '\'', 1357811441);
1810 }
1811
1812 $returnObject = NULL;
1813 if ($this->driver->folderExistsInFolder($newName, $folderObject)) {
1814 throw new \InvalidArgumentException('The folder ' . $newName . ' already exists in folder ' . $folderObject->getIdentifier(), 1325418870);
1815 }
1816
1817 $this->emitPreFolderRenameSignal($folderObject, $newName);
1818
1819 $fileObjects = $this->getAllFileObjectsInFolder($folderObject);
1820 $fileMappings = $this->driver->renameFolder($folderObject, $newName);
1821 // Update the identifier of all file objects
1822 foreach ($fileObjects as $oldIdentifier => $fileObject) {
1823 $newIdentifier = $fileMappings[$oldIdentifier];
1824 $fileObject->updateProperties(array('identifier' => $newIdentifier));
1825 $this->getIndexer()->updateIndexEntry($fileObject);
1826 }
1827 $returnObject = $this->getFolder($fileMappings[$folderObject->getIdentifier()]);
1828
1829 $this->emitPostFolderRenameSignal($folderObject, $newName);
1830
1831 return $returnObject;
1832 }
1833
1834 /**
1835 * Previously in \TYPO3\CMS\Core\Utility\File\ExtendedFileUtility::folder_delete()
1836 *
1837 * @param Folder $folderObject
1838 * @param boolean $deleteRecursively
1839 * @throws \RuntimeException
1840 * @return boolean
1841 */
1842 public function deleteFolder($folderObject, $deleteRecursively = FALSE) {
1843 $isEmpty = $this->driver->isFolderEmpty($folderObject);
1844 $this->assureFolderDeletePermission($folderObject, ($deleteRecursively && !$isEmpty));
1845 if (!$isEmpty && !$deleteRecursively) {
1846 throw new \RuntimeException('Could not delete folder "' . $folderObject->getIdentifier() . '" because it is not empty.', 1325952534);
1847 }
1848
1849 $this->emitPreFolderDeleteSignal($folderObject);
1850
1851 $result = $this->driver->deleteFolder($folderObject, $deleteRecursively);
1852
1853 $this->emitPostFolderDeleteSignal($folderObject);
1854
1855 return $result;
1856 }
1857
1858 /**
1859 * Returns a list of folders in a given path.
1860 *
1861 * @param string $path The path to list
1862 * @param integer $start The position to start the listing; if not set or 0, start from the beginning
1863 * @param integer $numberOfItems The number of items to list; if not set, return all items
1864 * @param boolean $useFilters If FALSE, the list is returned without any filtering; otherwise, the filters defined for this storage are used.
1865 * @return array Information about the folders found.
1866 */
1867 public function getFolderList($path, $start = 0, $numberOfItems = 0, $useFilters = TRUE) {
1868 // Permissions are checked in $this->fetchFolderListFromDriver()
1869 $filters = $useFilters === TRUE ? $this->fileAndFolderNameFilters : array();
1870 return $this->fetchFolderListFromDriver($path, $start, $numberOfItems, $filters);
1871 }
1872
1873 /**
1874 * @param $path
1875 * @param integer $start
1876 * @param integer $numberOfItems
1877 * @param array $folderFilterCallbacks
1878 * @param boolean $recursive
1879 * @return array
1880 */
1881 public function fetchFolderListFromDriver($path, $start = 0, $numberOfItems = 0, array $folderFilterCallbacks = array(), $recursive = FALSE) {
1882 // This also checks for access to that path and throws exceptions accordingly
1883 if ($this->getFolder($path) === NULL) {
1884 return array();
1885 }
1886 $items = $this->driver->getFolderList($path, $start, $numberOfItems, $folderFilterCallbacks, $recursive);
1887 if (!empty($items)) {
1888 // Exclude the _processed_ folder, so it won't get indexed etc
1889 // The processed folder might be any sub folder in storage
1890 $processingFolder = $this->getProcessingFolder();
1891 if ($processingFolder) {
1892 $processedFolderIdentifier = $this->processingFolder->getIdentifier();
1893 $processedFolderIdentifier = trim($processedFolderIdentifier, '/');
1894 $processedFolderIdentifierParts = explode('/', $processedFolderIdentifier);
1895 $processedFolderName = array_pop($processedFolderIdentifierParts);
1896 $processedFolderParent = implode('/', $processedFolderIdentifierParts);
1897 if ($processedFolderParent === trim($path, '/') && isset($items[$processedFolderName])) {
1898 unset($items[$processedFolderName]);
1899 }
1900 }
1901 uksort($items, 'strnatcasecmp');
1902 }
1903 return $items;
1904 }
1905
1906 /**
1907 * Returns TRUE if the specified folder exists.
1908 *
1909 * @param string $identifier
1910 * @return boolean
1911 */
1912 public function hasFolder($identifier) {
1913 $this->assureFolderReadPermission();
1914 return $this->driver->folderExists($identifier);
1915 }
1916
1917 /**
1918 * Checks if the given file exists in the given folder
1919 *
1920 * @param string $folderName
1921 * @param Folder $folder
1922 * @return boolean
1923 */
1924 public function hasFolderInFolder($folderName, Folder $folder) {
1925 $this->assureFolderReadPermission($folder);
1926 return $this->driver->folderExistsInFolder($folderName, $folder);
1927 }
1928
1929 /**
1930 * Creates a new folder.
1931 *
1932 * previously in \TYPO3\CMS\Core\Utility\File\ExtendedFileUtility::func_newfolder()
1933 *
1934 * @param string $folderName The new folder name
1935 * @param Folder $parentFolder (optional) the parent folder to create the new folder inside of. If not given, the root folder is used
1936 *
1937 * @throws Exception\InsufficientFolderWritePermissionsException
1938 * @throws \InvalidArgumentException
1939 * @return Folder The new folder object
1940 */
1941 public function createFolder($folderName, Folder $parentFolder = NULL) {
1942 if ($parentFolder === NULL) {
1943 $parentFolder = $this->getRootLevelFolder();
1944 }
1945 if (!$this->driver->folderExists($parentFolder->getIdentifier())) {
1946 throw new \InvalidArgumentException('Parent folder "' . $parentFolder->getIdentifier() . '" does not exist.', 1325689164);
1947 }
1948 if (!$this->checkFolderActionPermission('add', $parentFolder)) {
1949 throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to create directories in the folder "' . $parentFolder->getIdentifier() . '"', 1323059807);
1950 }
1951 // TODO this only works with hirachical file systems
1952 $folderParts = GeneralUtility::trimExplode('/', $folderName, TRUE);
1953 foreach ($folderParts as $folder) {
1954 // TODO check if folder creation succeeded
1955 if ($this->hasFolderInFolder($folder, $parentFolder)) {
1956 $parentFolder = $this->driver->getFolderInFolder($folder, $parentFolder);
1957 } else {
1958 $parentFolder = $this->driver->createFolder($folder, $parentFolder);
1959 }
1960 }
1961 return $parentFolder;
1962 }
1963
1964 /**
1965 * Returns the default folder where new files are stored if no other folder is given.
1966 *
1967 * @return Folder
1968 */
1969 public function getDefaultFolder() {
1970 return $this->driver->getDefaultFolder();
1971 }
1972
1973 /**
1974 * @param string $identifier
1975 *
1976 * @throws Exception\NotInMountPointException
1977 * @throws Exception\FolderDoesNotExistException
1978 * @return Folder
1979 */
1980 public function getFolder($identifier) {
1981 if (!$this->driver->folderExists($identifier)) {
1982 throw new Exception\FolderDoesNotExistException('Folder ' . $identifier . ' does not exist.', 1320575630);
1983 }
1984 $folder = $this->driver->getFolder($identifier);
1985 $this->assureFolderReadPermission($folder);
1986
1987 return $folder;
1988 }
1989
1990 /**
1991 * Returns the folders on the root level of the storage
1992 * or the first mount point of this storage for this user
1993 *
1994 * @return Folder
1995 */
1996 public function getRootLevelFolder() {
1997 if (count($this->fileMounts)) {
1998 $mount = reset($this->fileMounts);
1999 return $mount['folder'];
2000 } else {
2001 return $this->driver->getRootLevelFolder();
2002 }
2003 }
2004
2005 /**
2006 * Emits the configuration pre-processing signal
2007 *
2008 * @return void
2009 */
2010 protected function emitPreProcessConfigurationSignal() {
2011 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\ResourceStorage', self::SIGNAL_PreProcessConfiguration, array($this));
2012 }
2013
2014 /**
2015 * Emits the configuration post-processing signal
2016 *
2017 * @return void
2018 */
2019 protected function emitPostProcessConfigurationSignal() {
2020 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\ResourceStorage', self::SIGNAL_PostProcessConfiguration, array($this));
2021 }
2022
2023 /**
2024 * Emits file pre-add signal
2025 *
2026 * @param string $fileName
2027 * @param Folder $targetFolder
2028 * @return void
2029 */
2030 protected function emitPreFileAddSignal($fileName, Folder $targetFolder) {
2031 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\ResourceStorage', self::SIGNAL_PreFileAdd, array($fileName, $targetFolder));
2032 }
2033
2034 /**
2035 * Emits the file post-add signal
2036 *
2037 * @param FileInterface $file
2038 * @param Folder $targetFolder
2039 * @return void
2040 */
2041 protected function emitPostFileAddSignal(FileInterface $file, Folder $targetFolder) {
2042 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\ResourceStorage', self::SIGNAL_PostFileAdd, array($file, $targetFolder));
2043 }
2044
2045 /**
2046 * Emits file pre-copy signal
2047 *
2048 * @param FileInterface $file
2049 * @param Folder $targetFolder
2050 * @return void
2051 */
2052 protected function emitPreFileCopySignal(FileInterface $file, Folder $targetFolder) {
2053 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\ResourceStorage', self::SIGNAL_PreFileCopy, array($file, $targetFolder));
2054 }
2055
2056 /**
2057 * Emits the file post-copy signal
2058 *
2059 * @param FileInterface $file
2060 * @param Folder $targetFolder
2061 * @return void
2062 */
2063 protected function emitPostFileCopySignal(FileInterface $file, Folder $targetFolder) {
2064 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\ResourceStorage', self::SIGNAL_PostFileCopy, array($file, $targetFolder));
2065 }
2066
2067 /**
2068 * Emits the file pre-move signal
2069 *
2070 * @param FileInterface $file
2071 * @param Folder $targetFolder
2072 * @return void
2073 */
2074 protected function emitPreFileMoveSignal(FileInterface $file, Folder $targetFolder) {
2075 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\ResourceStorage', self::SIGNAL_PreFileMove, array($file, $targetFolder));
2076 }
2077
2078 /**
2079 * Emits the file post-move signal
2080 *
2081 * @param FileInterface $file
2082 * @param Folder $targetFolder
2083 * @return void
2084 */
2085 protected function emitPostFileMoveSignal(FileInterface $file, Folder $targetFolder) {
2086 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\ResourceStorage', self::SIGNAL_PostFileMove, array($file, $targetFolder));
2087 }
2088
2089 /**
2090 * Emits the file pre-rename signal
2091 *
2092 * @param FileInterface $file
2093 * @param $targetFolder
2094 * @return void
2095 */
2096 protected function emitPreFileRenameSignal(FileInterface $file, $targetFolder) {
2097 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\ResourceStorage', self::SIGNAL_PreFileRename, array($file, $targetFolder));
2098 }
2099
2100 /**
2101 * Emits the file post-rename signal
2102 *
2103 * @param FileInterface $file
2104 * @param $targetFolder
2105 * @return void
2106 */
2107 protected function emitPostFileRenameSignal(FileInterface $file, $targetFolder) {
2108 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\ResourceStorage', self::SIGNAL_PostFileRename, array($file, $targetFolder));
2109 }
2110
2111 /**
2112 * Emits the file pre-replace signal
2113 *
2114 * @param FileInterface $file
2115 * @param $localFilePath
2116 * @return void
2117 */
2118 protected function emitPreFileReplaceSignal(FileInterface $file, $localFilePath) {
2119 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\ResourceStorage', self::SIGNAL_PreFileReplace, array($file, $localFilePath));
2120 }
2121
2122 /**
2123 * Emits the file post-replace signal
2124 *
2125 * @param FileInterface $file
2126 * @param $localFilePath
2127 * @return void
2128 */
2129 protected function emitPostFileReplaceSignal(FileInterface $file, $localFilePath) {
2130 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\ResourceStorage', self::SIGNAL_PostFileReplace, array($file, $localFilePath));
2131 }
2132
2133 /**
2134 * Emits the file pre-deletion signal
2135 *
2136 * @param FileInterface $file
2137 * @return void
2138 */
2139 protected function emitPreFileDeleteSignal(FileInterface $file) {
2140 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\ResourceStorage', self::SIGNAL_PreFileDelete, array($file));
2141 }
2142
2143 /**
2144 * Emits the file post-deletion signal
2145 *
2146 * @param FileInterface $file
2147 * @return void
2148 */
2149 protected function emitPostFileDeleteSignal(FileInterface $file) {
2150 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\ResourceStorage', self::SIGNAL_PostFileDelete, array($file));
2151 }
2152
2153 /**
2154 * Emits the folder pre-copy signal
2155 *
2156 * @param Folder $folder
2157 * @param Folder $targetFolder
2158 * @param $newName
2159 * @return void
2160 */
2161 protected function emitPreFolderCopySignal(Folder $folder, Folder $targetFolder, $newName) {
2162 $this->getSignalSlotDispatcher()->dispatch('ResourceStorage', self::SIGNAL_PreFolderCopy, array($folder, $targetFolder));
2163 }
2164
2165 /**
2166 * Emits the folder post-copy signal
2167 *
2168 * @param Folder $folder
2169 * @param Folder $targetFolder
2170 * @param $newName
2171 * @return void
2172 */
2173 protected function emitPostFolderCopySignal(Folder $folder, Folder $targetFolder, $newName) {
2174 $this->getSignalSlotDispatcher()->dispatch('ResourceStorage', self::SIGNAL_PostFolderCopy, array($folder, $targetFolder));
2175 }
2176
2177 /**
2178 * Emits the folder pre-move signal
2179 *
2180 * @param Folder $folder
2181 * @param Folder $targetFolder
2182 * @param $newName
2183 * @return void
2184 */
2185 protected function emitPreFolderMoveSignal(Folder $folder, Folder $targetFolder, $newName) {
2186 $this->getSignalSlotDispatcher()->dispatch('ResourceStorage', self::SIGNAL_PreFolderMove, array($folder, $targetFolder));
2187 }
2188
2189 /**
2190 * Emits the folder post-move signal
2191 *
2192 * @param Folder $folder
2193 * @param Folder $targetFolder
2194 * @param $newName
2195 * @return void
2196 */
2197 protected function emitPostFolderMoveSignal(Folder $folder, Folder $targetFolder, $newName) {
2198 $this->getSignalSlotDispatcher()->dispatch('ResourceStorage', self::SIGNAL_PostFolderMove, array($folder, $targetFolder));
2199 }
2200
2201 /**
2202 * Emits the folder pre-rename signal
2203 *
2204 * @param Folder $folder
2205 * @param $newName
2206 * @return void
2207 */
2208 protected function emitPreFolderRenameSignal(Folder $folder, $newName) {
2209 $this->getSignalSlotDispatcher()->dispatch('ResourceStorage', self::SIGNAL_PreFolderRename, array($folder, $newName));
2210 }
2211
2212 /**
2213 * Emits the folder post-rename signal
2214 *
2215 * @param Folder $folder
2216 * @param $newName
2217 * @return void
2218 */
2219 protected function emitPostFolderRenameSignal(Folder $folder, $newName) {
2220 $this->getSignalSlotDispatcher()->dispatch('ResourceStorage', self::SIGNAL_PostFolderRename, array($folder, $newName));
2221 }
2222
2223 /**
2224 * Emits the folder pre-deletion signal
2225 *
2226 * @param Folder $folder
2227 * @return void
2228 */
2229 protected function emitPreFolderDeleteSignal(Folder $folder) {
2230 $this->getSignalSlotDispatcher()->dispatch('ResourceStorage', self::SIGNAL_PreFolderDelete, array($folder));
2231 }
2232
2233 /**
2234 * Emits folder postdeletion signal.
2235 *
2236 * @param Folder $folder
2237 * @return void
2238 */
2239 protected function emitPostFolderDeleteSignal(Folder $folder) {
2240 $this->getSignalSlotDispatcher()->dispatch('ResourceStorage', self::SIGNAL_PostFolderDelete, array($folder));
2241 }
2242
2243 /**
2244 * Emits file pre-processing signal when generating a public url for a file or folder.
2245 *
2246 * @param ResourceInterface $resourceObject
2247 * @param boolean $relativeToCurrentScript
2248 * @param array $urlData
2249 */
2250 protected function emitPreGeneratePublicUrl(ResourceInterface $resourceObject, $relativeToCurrentScript, array $urlData) {
2251 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\ResourceStorage', self::SIGNAL_PreGeneratePublicUrl, array($this, $this->driver, $resourceObject, $relativeToCurrentScript, $urlData));
2252 }
2253
2254 /**
2255 * Returns the destination path/fileName of a unique fileName/foldername in that path.
2256 * If $theFile exists in $theDest (directory) the file have numbers appended up to $this->maxNumber. Hereafter a unique string will be appended.
2257 * This function is used by fx. TCEmain when files are attached to records and needs to be uniquely named in the uploads/* folders
2258 *
2259 * @param Folder $folder
2260 * @param string $theFile The input fileName to check
2261 * @param boolean $dontCheckForUnique If set the fileName is returned with the path prepended without checking whether it already existed!
2262 *
2263 * @throws \RuntimeException
2264 * @return string A unique fileName inside $folder, based on $theFile.
2265 * @see \TYPO3\CMS\Core\Utility\File\BasicFileUtility::getUniqueName()
2266 */
2267 // TODO check if this should be moved back to Folder
2268 protected function getUniqueName(Folder $folder, $theFile, $dontCheckForUnique = FALSE) {
2269 static $maxNumber = 99, $uniqueNamePrefix = '';
2270 // Fetches info about path, name, extention of $theFile
2271 $origFileInfo = GeneralUtility::split_fileref($theFile);
2272 // Adds prefix
2273 if ($uniqueNamePrefix) {
2274 $origFileInfo['file'] = $uniqueNamePrefix . $origFileInfo['file'];
2275 $origFileInfo['filebody'] = $uniqueNamePrefix . $origFileInfo['filebody'];
2276 }
2277 // Check if the file exists and if not - return the fileName...
2278 $fileInfo = $origFileInfo;
2279 // The destinations file
2280 $theDestFile = $fileInfo['file'];
2281 // If the file does NOT exist we return this fileName
2282 if (!$this->driver->fileExistsInFolder($theDestFile, $folder) || $dontCheckForUnique) {
2283 return $theDestFile;
2284 }
2285 // Well the fileName in its pure form existed. Now we try to append
2286 // numbers / unique-strings and see if we can find an available fileName
2287 // This removes _xx if appended to the file
2288 $theTempFileBody = preg_replace('/_[0-9][0-9]$/', '', $origFileInfo['filebody']);
2289 $theOrigExt = $origFileInfo['realFileext'] ? '.' . $origFileInfo['realFileext'] : '';
2290 for ($a = 1; $a <= $maxNumber + 1; $a++) {
2291 // First we try to append numbers
2292 if ($a <= $maxNumber) {
2293 $insert = '_' . sprintf('%02d', $a);
2294 } else {
2295 // TODO remove constant 6
2296 $insert = '_' . substr(md5(uniqId('')), 0, 6);
2297 }
2298 $theTestFile = $theTempFileBody . $insert . $theOrigExt;
2299 // The destinations file
2300 $theDestFile = $theTestFile;
2301 // If the file does NOT exist we return this fileName
2302 if (!$this->driver->fileExistsInFolder($theDestFile, $folder)) {
2303 return $theDestFile;
2304 }
2305 }
2306 throw new \RuntimeException('Last possible name "' . $theDestFile . '" is already taken.', 1325194291);
2307 }
2308
2309 /**
2310 * Get the SignalSlot dispatcher
2311 *
2312 * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
2313 */
2314 protected function getSignalSlotDispatcher() {
2315 if (!isset($this->signalSlotDispatcher)) {
2316 $this->signalSlotDispatcher = $this->getObjectManager()->get('TYPO3\\CMS\\Extbase\\SignalSlot\\Dispatcher');
2317 }
2318 return $this->signalSlotDispatcher;
2319 }
2320
2321 /**
2322 * Get the ObjectManager
2323 *
2324 * @return \TYPO3\CMS\Extbase\Object\ObjectManager
2325 */
2326 protected function getObjectManager() {
2327 return GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');
2328 }
2329
2330 /**
2331 * @return ResourceFactory
2332 */
2333 protected function getFileFactory() {
2334 return GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\ResourceFactory');
2335 }
2336
2337 /**
2338 * @return \TYPO3\CMS\Core\Resource\FileRepository
2339 */
2340 protected function getFileRepository() {
2341 return GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\FileRepository');
2342 }
2343
2344 /**
2345 * @return \TYPO3\CMS\Core\Resource\Index\FileIndexRepository
2346 */
2347 protected function getFileIndexRepository() {
2348 return FileIndexRepository::getInstance();
2349 }
2350
2351 /**
2352 * @return Service\FileProcessingService
2353 */
2354 protected function getFileProcessingService() {
2355 if (!$this->fileProcessingService) {
2356 $this->fileProcessingService = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\Service\\FileProcessingService', $this, $this->driver);
2357 }
2358 return $this->fileProcessingService;
2359 }
2360
2361 /**
2362 * Gets the role of a folder
2363 *
2364 * @param FolderInterface $folder Folder object to get the role from
2365 * @return string The role the folder has
2366 */
2367 public function getRole(FolderInterface $folder) {
2368 $folderRole = FolderInterface::ROLE_DEFAULT;
2369
2370 if (method_exists($this->driver, 'getRole')) {
2371 $folderRole = $this->driver->getRole($folder);
2372 }
2373
2374 if ($folder->getIdentifier() === $this->getProcessingFolder()->getIdentifier()) {
2375 $folderRole = FolderInterface::ROLE_PROCESSING;
2376 }
2377
2378 return $folderRole;
2379 }
2380
2381 /**
2382 * Getter function to return the folder where the files can
2383 * be processed. does not check for access rights here
2384 *
2385 * @todo check if we need to implement "is writable" capability
2386 * @return Folder the processing folder, can be empty as well, if the storage doesn't have a processing folder
2387 */
2388 public function getProcessingFolder() {
2389 if (!isset($this->processingFolder)) {
2390 $processingFolder = self::DEFAULT_ProcessingFolder;
2391 if (!empty($this->storageRecord['processingfolder'])) {
2392 $processingFolder = $this->storageRecord['processingfolder'];
2393 }
2394 $processingFolder = '/' . trim($processingFolder, '/') . '/';
2395 // this way, we also worry about deeplinked folders like typo3temp/_processed_
2396 if ($this->driver->folderExists($processingFolder) === FALSE) {
2397 // TODO: This assumes that we have a hirarchical storage.
2398 // TODO: Recursive creation of folders should go to the driver, so that we can just call $this->driver->createFolder() here.
2399 $processingFolderParts = explode('/', $processingFolder);
2400 $parentFolder = $this->driver->getRootLevelFolder();
2401 foreach ($processingFolderParts as $folderPart) {
2402 if ($folderPart === '') {
2403 continue;
2404 }
2405 if (!$this->driver->folderExistsInFolder($folderPart, $parentFolder)) {
2406 $parentFolder = $this->driver->createFolder($folderPart, $parentFolder);
2407 } else {
2408 // We do not use the Folder API here to get the subfolder
2409 // Because permission checks are triggered then, which is not wanted
2410 // Since the whole method assumes that folders are hirarchical,
2411 // we can also asume it here to build the subfolder identifier
2412 // and fetch it directly from the driver.
2413 $subFolderIdentifier = $parentFolder->getIdentifier() . $folderPart;
2414 $parentFolder = $this->driver->getFolder($subFolderIdentifier);
2415 }
2416 }
2417 }
2418 $this->processingFolder = $this->driver->getFolder($processingFolder);
2419 }
2420 return $this->processingFolder;
2421 }
2422
2423 /**
2424 * Gets the driver Type configured for this storage
2425 *
2426 * @return string
2427 */
2428 public function getDriverType() {
2429 return $this->storageRecord['driver'];
2430 }
2431
2432 /**
2433 * Gets Indexer
2434 *
2435 * @return \TYPO3\CMS\Core\Resource\Index\Indexer
2436 */
2437 protected function getIndexer() {
2438 return GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\Index\\Indexer', $this);
2439 }
2440 }