[CLEANUP] Add missing empty lines before classes' closing curly braces
[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 * @throws Exception\InsufficientFolderAccessPermissionsException
1846 * @throws Exception\InsufficientUserPermissionsException
1847 * @throws Exception\FileOperationErrorException
1848 * @throws Exception\InvalidPathException
1849 * @return bool
1850 */
1851 public function deleteFolder($folderObject, $deleteRecursively = FALSE) {
1852 $isEmpty = $this->driver->isFolderEmpty($folderObject->getIdentifier());
1853 $this->assureFolderDeletePermission($folderObject, ($deleteRecursively && !$isEmpty));
1854 if (!$isEmpty && !$deleteRecursively) {
1855 throw new \RuntimeException('Could not delete folder "' . $folderObject->getIdentifier() . '" because it is not empty.', 1325952534);
1856 }
1857
1858 $this->emitPreFolderDeleteSignal($folderObject);
1859
1860 $result = $this->driver->deleteFolder($folderObject->getIdentifier(), $deleteRecursively);
1861
1862 $this->emitPostFolderDeleteSignal($folderObject);
1863
1864 return $result;
1865 }
1866
1867 /**
1868 * @param Folder $folder
1869 * @param int $start
1870 * @param int $maxNumberOfItems
1871 * @param bool $useFilters
1872 * @param bool $recursive
1873 *
1874 * @return Folder[]
1875 */
1876 public function getFoldersInFolder(Folder $folder, $start = 0, $maxNumberOfItems = 0, $useFilters = TRUE, $recursive = FALSE) {
1877 $filters = $useFilters == TRUE ? $this->fileAndFolderNameFilters : array();
1878 $folderIdentifiers = $this->driver->getFoldersInFolder($folder->getIdentifier(), $start, $maxNumberOfItems, $recursive, $filters);
1879
1880 $processingIdentifier = $this->getProcessingFolder()->getIdentifier();
1881 if (isset($folderIdentifiers[$processingIdentifier])) {
1882 unset($folderIdentifiers[$processingIdentifier]);
1883 }
1884 $folders = array();
1885 foreach ($folderIdentifiers as $folderIdentifier) {
1886 $folders[$folderIdentifier] = $this->getFolder($folderIdentifier, TRUE);
1887 }
1888 return $folders;
1889 }
1890
1891 /**
1892 * Returns TRUE if the specified folder exists.
1893 *
1894 * @param string $identifier
1895 * @return bool
1896 */
1897 public function hasFolder($identifier) {
1898 $this->assureFolderReadPermission();
1899 return $this->driver->folderExists($identifier);
1900 }
1901
1902 /**
1903 * Checks if the given file exists in the given folder
1904 *
1905 * @param string $folderName
1906 * @param Folder $folder
1907 * @return bool
1908 */
1909 public function hasFolderInFolder($folderName, Folder $folder) {
1910 $this->assureFolderReadPermission($folder);
1911 return $this->driver->folderExistsInFolder($folderName, $folder->getIdentifier());
1912 }
1913
1914 /**
1915 * Creates a new folder.
1916 *
1917 * previously in \TYPO3\CMS\Core\Utility\File\ExtendedFileUtility::func_newfolder()
1918 *
1919 * @param string $folderName The new folder name
1920 * @param Folder $parentFolder (optional) the parent folder to create the new folder inside of. If not given, the root folder is used
1921 *
1922 * @throws Exception\InsufficientFolderWritePermissionsException
1923 * @throws \InvalidArgumentException
1924 * @return Folder The new folder object
1925 */
1926 public function createFolder($folderName, Folder $parentFolder = NULL) {
1927 if ($parentFolder === NULL) {
1928 $parentFolder = $this->getRootLevelFolder();
1929 } elseif (!$this->driver->folderExists($parentFolder->getIdentifier())) {
1930 throw new \InvalidArgumentException('Parent folder "' . $parentFolder->getIdentifier() . '" does not exist.', 1325689164);
1931 }
1932 if (!$this->checkFolderActionPermission('add', $parentFolder)) {
1933 throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to create directories in the folder "' . $parentFolder->getIdentifier() . '"', 1323059807);
1934 }
1935
1936 $this->emitPreFolderAddSignal($parentFolder, $folderName);
1937
1938 $newFolder = $this->getDriver()->createFolder($folderName, $parentFolder->getIdentifier(), TRUE);
1939 $newFolder = $this->getFolder($newFolder);
1940
1941 $this->emitPostFolderAddSignal($newFolder);
1942
1943 return $newFolder;
1944 }
1945
1946 /**
1947 * Returns the default folder where new files are stored if no other folder is given.
1948 *
1949 * @return Folder
1950 */
1951 public function getDefaultFolder() {
1952 return $this->getFolder($this->driver->getDefaultFolder());
1953 }
1954
1955 /**
1956 * @param string $identifier
1957 * @param bool $returnInaccessibleFolderObject
1958 *
1959 * @return Folder
1960 * @throws \Exception
1961 * @throws Exception\InsufficientFolderAccessPermissionsException
1962 */
1963 public function getFolder($identifier, $returnInaccessibleFolderObject = FALSE) {
1964 $data = $this->driver->getFolderInfoByIdentifier($identifier);
1965 $folder = ResourceFactory::getInstance()->createFolderObject($this, $data['identifier'], $data['name']);
1966
1967 try {
1968 $this->assureFolderReadPermission($folder);
1969 } catch (Exception\InsufficientFolderAccessPermissionsException $e) {
1970 $folder = NULL;
1971 if ($returnInaccessibleFolderObject) {
1972 // if parent folder is readable return inaccessible folder object
1973 $parentPermissions = $this->driver->getPermissions($this->driver->getParentFolderIdentifierOfIdentifier($identifier));
1974 if ($parentPermissions['r']) {
1975 $folder = GeneralUtility::makeInstance(
1976 \TYPO3\CMS\Core\Resource\InaccessibleFolder::class, $this, $data['identifier'], $data['name']
1977 );
1978 }
1979 }
1980
1981 if ($folder === NULL) {
1982 throw $e;
1983 }
1984 }
1985 return $folder;
1986 }
1987
1988 /**
1989 * @param string $identifier
1990 * @return bool
1991 */
1992 public function isWithinProcessingFolder($identifier) {
1993 return $this->driver->isWithin($this->getProcessingFolder()->getIdentifier(), $identifier);
1994 }
1995
1996 /**
1997 * Returns the folders on the root level of the storage
1998 * or the first mount point of this storage for this user.
1999 *
2000 * @return Folder
2001 */
2002 public function getRootLevelFolder() {
2003 if (count($this->fileMounts)) {
2004 $mount = reset($this->fileMounts);
2005 return $mount['folder'];
2006 } else {
2007 return ResourceFactory::getInstance()->createFolderObject($this, $this->driver->getRootLevelFolder(), '');
2008 }
2009 }
2010
2011 /**
2012 * Emits file pre-add signal.
2013 *
2014 * @param string $targetFileName
2015 * @param Folder $targetFolder
2016 * @param string $sourceFilePath
2017 * @return string Modified target file name
2018 */
2019 protected function emitPreFileAddSignal($targetFileName, Folder $targetFolder, $sourceFilePath) {
2020 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFileAdd, array(&$targetFileName, $targetFolder, $sourceFilePath, $this, $this->driver));
2021 return $targetFileName;
2022 }
2023
2024 /**
2025 * Emits the file post-add signal.
2026 *
2027 * @param FileInterface $file
2028 * @param Folder $targetFolder
2029 * @return void
2030 */
2031 protected function emitPostFileAddSignal(FileInterface $file, Folder $targetFolder) {
2032 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileAdd, array($file, $targetFolder));
2033 }
2034
2035 /**
2036 * Emits file pre-copy signal.
2037 *
2038 * @param FileInterface $file
2039 * @param Folder $targetFolder
2040 * @return void
2041 */
2042 protected function emitPreFileCopySignal(FileInterface $file, Folder $targetFolder) {
2043 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFileCopy, array($file, $targetFolder));
2044 }
2045
2046 /**
2047 * Emits the file post-copy signal.
2048 *
2049 * @param FileInterface $file
2050 * @param Folder $targetFolder
2051 * @return void
2052 */
2053 protected function emitPostFileCopySignal(FileInterface $file, Folder $targetFolder) {
2054 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileCopy, array($file, $targetFolder));
2055 }
2056
2057 /**
2058 * Emits the file pre-move signal.
2059 *
2060 * @param FileInterface $file
2061 * @param Folder $targetFolder
2062 * @return void
2063 */
2064 protected function emitPreFileMoveSignal(FileInterface $file, Folder $targetFolder) {
2065 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFileMove, array($file, $targetFolder));
2066 }
2067
2068 /**
2069 * Emits the file post-move signal.
2070 *
2071 * @param FileInterface $file
2072 * @param Folder $targetFolder
2073 * @param Folder $originalFolder
2074 * @return void
2075 */
2076 protected function emitPostFileMoveSignal(FileInterface $file, Folder $targetFolder, Folder $originalFolder) {
2077 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileMove, array($file, $targetFolder, $originalFolder));
2078 }
2079
2080 /**
2081 * Emits the file pre-rename signal
2082 *
2083 * @param FileInterface $file
2084 * @param $targetFolder
2085 * @return void
2086 */
2087 protected function emitPreFileRenameSignal(FileInterface $file, $targetFolder) {
2088 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFileRename, array($file, $targetFolder));
2089 }
2090
2091 /**
2092 * Emits the file post-rename signal.
2093 *
2094 * @param FileInterface $file
2095 * @param $targetFolder
2096 * @return void
2097 */
2098 protected function emitPostFileRenameSignal(FileInterface $file, $targetFolder) {
2099 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileRename, array($file, $targetFolder));
2100 }
2101
2102 /**
2103 * Emits the file pre-replace signal.
2104 *
2105 * @param FileInterface $file
2106 * @param $localFilePath
2107 * @return void
2108 */
2109 protected function emitPreFileReplaceSignal(FileInterface $file, $localFilePath) {
2110 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFileReplace, array($file, $localFilePath));
2111 }
2112
2113 /**
2114 * Emits the file post-replace signal
2115 *
2116 * @param FileInterface $file
2117 * @param $localFilePath
2118 * @return void
2119 */
2120 protected function emitPostFileReplaceSignal(FileInterface $file, $localFilePath) {
2121 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileReplace, array($file, $localFilePath));
2122 }
2123
2124 /**
2125 * Emits the file post-create signal
2126 *
2127 * @param string $newFileIdentifier
2128 * @param Folder $targetFolder
2129 */
2130 protected function emitPostFileCreateSignal($newFileIdentifier, Folder $targetFolder) {
2131 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileCreate, array($newFileIdentifier, $targetFolder));
2132 }
2133
2134 /**
2135 * Emits the file pre-deletion signal.
2136 *
2137 * @param FileInterface $file
2138 * @return void
2139 */
2140 protected function emitPreFileDeleteSignal(FileInterface $file) {
2141 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFileDelete, array($file));
2142 }
2143
2144 /**
2145 * Emits the file post-deletion signal
2146 *
2147 * @param FileInterface $file
2148 * @return void
2149 */
2150 protected function emitPostFileDeleteSignal(FileInterface $file) {
2151 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileDelete, array($file));
2152 }
2153
2154 /**
2155 * Emits the file post-set-contents signal
2156 *
2157 * @param FileInterface $file
2158 * @param mixed $content
2159 * @return void
2160 */
2161 protected function emitPostFileSetContentsSignal(FileInterface $file, $content) {
2162 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileSetContents, array($file, $content));
2163 }
2164
2165 /**
2166 * Emits the folder pre-add signal.
2167 *
2168 * @param Folder $targetFolder
2169 * @param string $name
2170 * @return void
2171 */
2172 protected function emitPreFolderAddSignal(Folder $targetFolder, $name) {
2173 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFolderAdd, array($targetFolder, $name));
2174 }
2175
2176 /**
2177 * Emits the folder post-add signal.
2178 *
2179 * @param Folder $folder
2180 * @return void
2181 */
2182 protected function emitPostFolderAddSignal(Folder $folder) {
2183 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFolderAdd, array($folder));
2184 }
2185
2186 /**
2187 * Emits the folder pre-copy signal.
2188 *
2189 * @param Folder $folder
2190 * @param Folder $targetFolder
2191 * @param $newName
2192 * @return void
2193 */
2194 protected function emitPreFolderCopySignal(Folder $folder, Folder $targetFolder, $newName) {
2195 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFolderCopy, array($folder, $targetFolder, $newName));
2196 }
2197
2198 /**
2199 * Emits the folder post-copy signal.
2200 *
2201 * @param Folder $folder
2202 * @param Folder $targetFolder
2203 * @param $newName
2204 * @return void
2205 */
2206 protected function emitPostFolderCopySignal(Folder $folder, Folder $targetFolder, $newName) {
2207 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFolderCopy, array($folder, $targetFolder, $newName));
2208 }
2209
2210 /**
2211 * Emits the folder pre-move signal.
2212 *
2213 * @param Folder $folder
2214 * @param Folder $targetFolder
2215 * @param $newName
2216 * @return void
2217 */
2218 protected function emitPreFolderMoveSignal(Folder $folder, Folder $targetFolder, $newName) {
2219 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFolderMove, array($folder, $targetFolder, $newName));
2220 }
2221
2222 /**
2223 * Emits the folder post-move signal.
2224 *
2225 * @param Folder $folder
2226 * @param Folder $targetFolder
2227 * @param $newName
2228 * @param Folder $originalFolder
2229 * @return void
2230 */
2231 protected function emitPostFolderMoveSignal(Folder $folder, Folder $targetFolder, $newName, Folder $originalFolder) {
2232 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFolderMove, array($folder, $targetFolder, $newName, $originalFolder));
2233 }
2234
2235 /**
2236 * Emits the folder pre-rename signal.
2237 *
2238 * @param Folder $folder
2239 * @param $newName
2240 * @return void
2241 */
2242 protected function emitPreFolderRenameSignal(Folder $folder, $newName) {
2243 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFolderRename, array($folder, $newName));
2244 }
2245
2246 /**
2247 * Emits the folder post-rename signal.
2248 *
2249 * @param Folder $folder
2250 * @param $newName
2251 * @return void
2252 */
2253 protected function emitPostFolderRenameSignal(Folder $folder, $newName) {
2254 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFolderRename, array($folder, $newName));
2255 }
2256
2257 /**
2258 * Emits the folder pre-deletion signal.
2259 *
2260 * @param Folder $folder
2261 * @return void
2262 */
2263 protected function emitPreFolderDeleteSignal(Folder $folder) {
2264 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFolderDelete, array($folder));
2265 }
2266
2267 /**
2268 * Emits folder post-deletion signal..
2269 *
2270 * @param Folder $folder
2271 * @return void
2272 */
2273 protected function emitPostFolderDeleteSignal(Folder $folder) {
2274 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFolderDelete, array($folder));
2275 }
2276
2277 /**
2278 * Emits file pre-processing signal when generating a public url for a file or folder.
2279 *
2280 * @param ResourceInterface $resourceObject
2281 * @param bool $relativeToCurrentScript
2282 * @param array $urlData
2283 */
2284 protected function emitPreGeneratePublicUrlSignal(ResourceInterface $resourceObject, $relativeToCurrentScript, array $urlData) {
2285 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreGeneratePublicUrl, array($this, $this->driver, $resourceObject, $relativeToCurrentScript, $urlData));
2286 }
2287
2288 /**
2289 * Returns the destination path/fileName of a unique fileName/foldername in that path.
2290 * If $theFile exists in $theDest (directory) the file have numbers appended up to $this->maxNumber. Hereafter a unique string will be appended.
2291 * This function is used by fx. TCEmain when files are attached to records and needs to be uniquely named in the uploads/* folders
2292 *
2293 * @param Folder $folder
2294 * @param string $theFile The input fileName to check
2295 * @param bool $dontCheckForUnique If set the fileName is returned with the path prepended without checking whether it already existed!
2296 *
2297 * @throws \RuntimeException
2298 * @return string A unique fileName inside $folder, based on $theFile.
2299 * @see \TYPO3\CMS\Core\Utility\File\BasicFileUtility::getUniqueName()
2300 */
2301 protected function getUniqueName(Folder $folder, $theFile, $dontCheckForUnique = FALSE) {
2302 static $maxNumber = 99, $uniqueNamePrefix = '';
2303 // Fetches info about path, name, extention of $theFile
2304 $origFileInfo = GeneralUtility::split_fileref($theFile);
2305 // Adds prefix
2306 if ($uniqueNamePrefix) {
2307 $origFileInfo['file'] = $uniqueNamePrefix . $origFileInfo['file'];
2308 $origFileInfo['filebody'] = $uniqueNamePrefix . $origFileInfo['filebody'];
2309 }
2310 // Check if the file exists and if not - return the fileName...
2311 $fileInfo = $origFileInfo;
2312 // The destinations file
2313 $theDestFile = $fileInfo['file'];
2314 // If the file does NOT exist we return this fileName
2315 if (!$this->driver->fileExistsInFolder($theDestFile, $folder->getIdentifier()) || $dontCheckForUnique) {
2316 return $theDestFile;
2317 }
2318 // Well the fileName in its pure form existed. Now we try to append
2319 // numbers / unique-strings and see if we can find an available fileName
2320 // This removes _xx if appended to the file
2321 $theTempFileBody = preg_replace('/_[0-9][0-9]$/', '', $origFileInfo['filebody']);
2322 $theOrigExt = $origFileInfo['realFileext'] ? '.' . $origFileInfo['realFileext'] : '';
2323 for ($a = 1; $a <= $maxNumber + 1; $a++) {
2324 // First we try to append numbers
2325 if ($a <= $maxNumber) {
2326 $insert = '_' . sprintf('%02d', $a);
2327 } else {
2328 $insert = '_' . substr(md5(uniqid('', TRUE)), 0, 6);
2329 }
2330 $theTestFile = $theTempFileBody . $insert . $theOrigExt;
2331 // The destinations file
2332 $theDestFile = $theTestFile;
2333 // If the file does NOT exist we return this fileName
2334 if (!$this->driver->fileExistsInFolder($theDestFile, $folder->getIdentifier())) {
2335 return $theDestFile;
2336 }
2337 }
2338 throw new \RuntimeException('Last possible name "' . $theDestFile . '" is already taken.', 1325194291);
2339 }
2340
2341 /**
2342 * Get the SignalSlot dispatcher.
2343 *
2344 * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
2345 */
2346 protected function getSignalSlotDispatcher() {
2347 if (!isset($this->signalSlotDispatcher)) {
2348 $this->signalSlotDispatcher = $this->getObjectManager()->get(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class);
2349 }
2350 return $this->signalSlotDispatcher;
2351 }
2352
2353 /**
2354 * Gets the ObjectManager.
2355 *
2356 * @return \TYPO3\CMS\Extbase\Object\ObjectManager
2357 */
2358 protected function getObjectManager() {
2359 return GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
2360 }
2361
2362 /**
2363 * @return ResourceFactory
2364 */
2365 protected function getFileFactory() {
2366 return GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\ResourceFactory::class);
2367 }
2368
2369 /**
2370 * @return \TYPO3\CMS\Core\Resource\Index\FileIndexRepository
2371 */
2372 protected function getFileIndexRepository() {
2373 return FileIndexRepository::getInstance();
2374 }
2375
2376 /**
2377 * @return Service\FileProcessingService
2378 */
2379 protected function getFileProcessingService() {
2380 if (!$this->fileProcessingService) {
2381 $this->fileProcessingService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\Service\FileProcessingService::class, $this, $this->driver);
2382 }
2383 return $this->fileProcessingService;
2384 }
2385
2386 /**
2387 * Gets the role of a folder.
2388 *
2389 * @param FolderInterface $folder Folder object to get the role from
2390 * @return string The role the folder has
2391 */
2392 public function getRole(FolderInterface $folder) {
2393 $folderRole = FolderInterface::ROLE_DEFAULT;
2394 $identifier = $folder->getIdentifier();
2395 if (method_exists($this->driver, 'getRole')) {
2396 $folderRole = $this->driver->getRole($folder->getIdentifier());
2397 }
2398 if (isset($this->fileMounts[$identifier])) {
2399 $folderRole = FolderInterface::ROLE_MOUNT;
2400
2401 if (!empty($this->fileMounts[$identifier]['read_only'])) {
2402 $folderRole = FolderInterface::ROLE_READONLY_MOUNT;
2403 }
2404 if ($this->fileMounts[$identifier]['user_mount']) {
2405 $folderRole = FolderInterface::ROLE_USER_MOUNT;
2406 }
2407 }
2408
2409 if ($identifier === $this->getProcessingFolder()->getIdentifier()) {
2410 $folderRole = FolderInterface::ROLE_PROCESSING;
2411 }
2412
2413 return $folderRole;
2414 }
2415
2416 /**
2417 * Getter function to return the folder where the files can
2418 * be processed. Does not check for access rights here.
2419 *
2420 * @return Folder
2421 */
2422 public function getProcessingFolder() {
2423 if (!isset($this->processingFolder)) {
2424 $processingFolder = self::DEFAULT_ProcessingFolder;
2425 if (!empty($this->storageRecord['processingfolder'])) {
2426 $processingFolder = $this->storageRecord['processingfolder'];
2427 }
2428 if ($this->driver->folderExists($processingFolder) === FALSE) {
2429 $this->processingFolder = $this->createFolder($processingFolder);
2430 } else {
2431 $data = $this->driver->getFolderInfoByIdentifier($processingFolder);
2432 $this->processingFolder = ResourceFactory::getInstance()->createFolderObject($this, $data['identifier'], $data['name']);
2433 }
2434 }
2435 return $this->processingFolder;
2436 }
2437
2438 /**
2439 * Gets the driver Type configured for this storage.
2440 *
2441 * @return string
2442 */
2443 public function getDriverType() {
2444 return $this->storageRecord['driver'];
2445 }
2446
2447 /**
2448 * Gets the Indexer.
2449 *
2450 * @return \TYPO3\CMS\Core\Resource\Index\Indexer
2451 */
2452 protected function getIndexer() {
2453 return GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\Index\Indexer::class, $this);
2454 }
2455
2456 /**
2457 * @param bool $isDefault
2458 * @return void
2459 */
2460 public function setDefault($isDefault) {
2461 $this->isDefault = (bool)$isDefault;
2462 }
2463
2464 /**
2465 * @return bool
2466 */
2467 public function isDefault() {
2468 return $this->isDefault;
2469 }
2470
2471 }