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