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