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