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