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