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