[FEATURE] Integrate File Abstraction Layer API
[Packages/TYPO3.CMS.git] / t3lib / file / Storage.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2011 Andreas Wolf <andreas.wolf@ikt-werk.de>
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27
28
29 /**
30 * A "mount point" inside the TYPO3 file handling.
31 *
32 * A "storage" object handles
33 * - abstraction to the driver
34 * - permissions (from the driver, and from the user, + capabilities)
35 * - an entry point for files, folders, and for most other operations
36 *
37 * == Driver entry point
38 * The driver itself, that does the actual work on the file system,
39 * is inside the storage but completely shadowed by
40 * the storage, as the storage also handles the abstraction to the
41 * driver
42 *
43 * The storage can be on the local system, but can also be on a remote
44 * system. The combination of driver + configurable capabilities (storage
45 * is read-only e.g.) allows for flexible uses.
46 *
47 *
48 * == Permission system
49 * As all requests have to run through the storage, the storage knows about the
50 * permissions of a BE/FE user, the file permissions / limitations of the driver
51 * and has some configurable capabilities.
52 * Additionally, a BE user can use "filemounts" (known from previous installations)
53 * to limit his/her work-zone to only a subset (identifier and its subfolders/subfolders)
54 * of the user itself.
55 *
56 * Check 1: "User Permissions" [is the user allowed to write a file) [is the user allowed to write a file]
57 * Check 2: "File Mounts" of the User (act as subsets / filters to the identifiers) [is the user allowed to do something in this folder?]
58 * Check 3: "Capabilities" of Storage (then: of Driver) [is the storage/driver writable?]
59 * Check 4: "File permissions" of the Driver [is the folder writable?]
60 *
61 *
62 * @author Andreas Wolf <andreas.wolf@ikt-werk.de>
63 * @author Ingmar Schlecht <ingmar@typo3.org>
64 * @package TYPO3
65 * @subpackage t3lib
66 */
67 class t3lib_file_Storage {
68
69 const SIGNAL_PreProcessConfiguration = 'preProcessConfiguration';
70 const SIGNAL_PostProcessConfiguration = 'postProcessConfiguration';
71 const SIGNAL_PreFileCopy = 'preFileCopy';
72 const SIGNAL_PostFileCopy = 'postFileCopy';
73 const SIGNAL_PreFileMove = 'preFileMove';
74 const SIGNAL_PostFileMove = 'postFileMove';
75 const SIGNAL_PreFileDelete = 'preFileDelete';
76 const SIGNAL_PostFileDelete = 'postFileDelete';
77 const SIGNAL_PreFileRename = 'preFileRename';
78 const SIGNAL_PostFileRename = 'postFileRename';
79 const SIGNAL_PreFileReplace = 'preFileReplace';
80 const SIGNAL_PostFileReplace = 'postFileReplace';
81 const SIGNAL_PreFolderCopy = 'preFolderCopy';
82 const SIGNAL_PostFolderCopy = 'postFolderCopy';
83 const SIGNAL_PreFolderMove = 'preFolderMove';
84 const SIGNAL_PostFolderMove = 'postFolderMove';
85 const SIGNAL_PreFolderDelete = 'preFolderDelete';
86 const SIGNAL_PostFolderDelete = 'postFolderDelete';
87 const SIGNAL_PreFolderRename = 'preFolderRename';
88 const SIGNAL_PostFolderRename = 'postFolderRename';
89 const SIGNAL_PreFileProcess = 'preFileProcess';
90 const SIGNAL_PostFileProcess = 'postFileProcess';
91
92 /**
93 * The storage driver instance belonging to this storage.
94 *
95 * @var t3lib_file_Driver_AbstractDriver
96 */
97 protected $driver;
98
99 /**
100 * The database record for this storage
101 *
102 * @var array
103 */
104 protected $storageRecord;
105
106 /**
107 * The configuration belonging to this storage (decoded from the configuration field).
108 *
109 * @var array
110 */
111 protected $configuration;
112
113 /**
114 * The base URI to this storage.
115 *
116 * @var string
117 */
118 protected $baseUri;
119
120 /**
121 * @var t3lib_file_Service_FileProcessingService
122 */
123 protected $fileProcessingService;
124
125 /**
126 * User filemounts, added as an array, and used as filters
127 *
128 * @var array
129 */
130 protected $fileMounts = array();
131
132 /**
133 * The file permissions of the user (and their group) merged together and available as an array
134 *
135 * @var array
136 */
137 protected $userPermissions = array();
138
139 /**
140 * The capabilities of this storage as defined in the storage record. Also see the CAPABILITY_* constants below
141 *
142 * @var integer
143 */
144 protected $capabilities;
145
146 /**
147 * @var t3lib_SignalSlot_Dispatcher
148 */
149 protected $signalSlotDispatcher;
150
151 /**
152 * Capability for being browsable by (backend) users
153 */
154 const CAPABILITY_BROWSABLE = 1;
155
156 /**
157 * Capability for publicly accessible storages (= accessible from the web)
158 */
159 const CAPABILITY_PUBLIC = 2;
160
161 /**
162 * Capability for writable storages. This only signifies writability in general - this might also be further limited by configuration.
163 */
164 const CAPABILITY_WRITABLE = 4;
165
166 /**
167 * Name of the default processing folder
168 */
169 const DEFAULT_ProcessingFolder = '_processed_';
170
171 /**
172 * @var t3lib_file_Folder
173 */
174 protected $processingFolder;
175
176 /**
177 * Constructor for a storage object.
178 *
179 * @param t3lib_file_Driver_AbstractDriver $driver
180 * @param array $storageRecord The storage record row from the database
181 */
182 public function __construct(t3lib_file_Driver_AbstractDriver $driver, array $storageRecord) {
183 $this->storageRecord = $storageRecord;
184 $this->configuration = $this->getFileFactory()->convertFlexFormDataToConfigurationArray(
185 $storageRecord['configuration']
186 );
187
188 $this->driver = $driver;
189 $this->driver->setStorage($this);
190 $this->driver->initialize();
191 $this->capabilities = ($this->storageRecord['is_browsable'] && $this->driver->hasCapability(self::CAPABILITY_BROWSABLE) ? self::CAPABILITY_BROWSABLE : 0)
192 + ($this->storageRecord['is_public'] && $this->driver->hasCapability(self::CAPABILITY_PUBLIC) ? self::CAPABILITY_PUBLIC : 0)
193 + ($this->storageRecord['is_writable'] && $this->driver->hasCapability(self::CAPABILITY_WRITABLE) ? self::CAPABILITY_WRITABLE : 0);
194
195 $this->processConfiguration();
196 }
197
198 /**
199 * Gets the configuration
200 *
201 * @return array
202 */
203 public function getConfiguration() {
204 return $this->configuration;
205 }
206
207 /**
208 * Sets the configuration.
209 *
210 * @param array $configuration
211 */
212 public function setConfiguration(array $configuration) {
213 $this->configuration = $configuration;
214 }
215
216 /**
217 * Gets the storage record.
218 *
219 * @return array
220 */
221 public function getStorageRecord() {
222 return $this->storageRecord;
223 }
224
225
226 /**
227 * Processes the configuration of this storage.
228 *
229 * @throws InvalidArgumentException If a required configuration option is not set or has an invalid value.
230 * @return void
231 */
232 protected function processConfiguration() {
233 $this->emitPreProcessConfigurationSignal();
234
235 if (isset($this->configuration['baseUri'])) {
236 $this->baseUri = rtrim($this->configuration['baseUri'], '/') . '/';
237 }
238
239 $this->emitPostProcessConfigurationSignal();
240 }
241
242 /**
243 * Returns the base URI of this storage; all files are reachable via URLs beginning with this string.
244 *
245 * @return string
246 */
247 public function getBaseUri() {
248 return $this->baseUri;
249 }
250
251 /**
252 * Sets the storage that belongs to this storage.
253 *
254 * @param t3lib_file_Driver_AbstractDriver $driver
255 * @return t3lib_file_Storage
256 */
257 public function setDriver(t3lib_file_Driver_AbstractDriver $driver) {
258 $this->driver = $driver;
259 return $this;
260 }
261
262 /**
263 * Returns the driver object belonging to this storage.
264 *
265 * @return t3lib_file_Driver_AbstractDriver
266 */
267 protected function getDriver() {
268 return $this->driver;
269 }
270
271 /**
272 * Deprecated function, don't use it. Will be removed in some later revision.
273 *
274 * @param string $identifier
275 */
276 public function getFolderByIdentifier($identifier) {
277 throw new Exception('Function t3lib_file_Storage::getFolderByIdentifier() has been renamed to just getFolder(). Please fix the metho call.');
278 }
279
280 /**
281 * Deprecated function, don't use it. Will be removed in some later revision.
282 *
283 * @param string $identifier
284 */
285 public function getFileByIdentifier($identifier) {
286 throw new Exception('Function t3lib_file_Storage::getFileByIdentifier() has been renamed to just getFileInfoByIdentifier(). Please fix the metho call.');
287 }
288
289 /**
290 * Returns the name of this storage.
291 *
292 * @return string
293 */
294 public function getName() {
295 return $this->storageRecord['name'];
296 }
297
298 /**
299 * Returns the uid of this storage.
300 *
301 * @return integer
302 */
303 public function getUid() {
304 return $this->storageRecord['uid'];
305 }
306
307 /**
308 * Tells whether there are children in this storage
309 *
310 * @return boolean
311 */
312 public function hasChildren() {
313 return TRUE;
314 }
315
316
317 /*********************************
318 * Capabilities
319 ********************************/
320
321 /**
322 * Returns the capabilities of this storage.
323 *
324 * @return int
325 * @see CAPABILITY_* constants
326 */
327 public function getCapabilities() {
328 return $this->capabilities;
329 }
330
331 /**
332 * Returns TRUE if this storage has the given capability.
333 *
334 * @param int $capability A capability, as defined in a CAPABILITY_* constant
335 * @return boolean
336 */
337 protected function hasCapability($capability) {
338 return $this->capabilities && $capability;
339 }
340
341 /**
342 * Returns TRUE if this storage is publicly available. This is just a configuration option and does not mean that it
343 * really *is* public. OTOH a storage that is marked as not publicly available will trigger the file publishing mechanisms
344 * of TYPO3.
345 *
346 * @return boolean
347 */
348 public function isPublic() {
349 return $this->hasCapability(self::CAPABILITY_PUBLIC);
350 }
351
352 /**
353 * Returns TRUE if this storage is writable. This is determined by the driver and the storage configuration; user
354 * permissions are not taken into account.
355 *
356 * @return boolean
357 */
358 public function isWritable() {
359 return $this->hasCapability(self::CAPABILITY_WRITABLE);
360 }
361
362 /**
363 * Returns TRUE if this storage is browsable by a (backend) user of TYPO3.
364 *
365 * @return boolean
366 */
367 public function isBrowsable() {
368 return $this->hasCapability(self::CAPABILITY_BROWSABLE);
369 }
370
371
372 /*********************************
373 * User Permissions / File Mounts
374 ********************************/
375
376 /**
377 * Adds a filemount as a "filter" for users to only work on a subset of a storage object
378 *
379 * @param string $folderIdentifier
380 * @param array $additionalData
381 * @return void
382 */
383 public function injectFileMount($folderIdentifier, $additionalData = array()) {
384 if (empty($additionalData)) {
385 $additionalData = array(
386 'path' => $folderIdentifier,
387 'title' => $folderIdentifier,
388 'folder' => $this->getFolder($folderIdentifier)
389 );
390 } else {
391 $additionalData['folder'] = $this->getFolder($folderIdentifier);
392 if (!isset($additionalData['title'])) {
393 $additionalData['title'] = $folderIdentifier;
394 }
395 }
396 $this->fileMounts[$folderIdentifier] = $additionalData;
397 }
398
399 /**
400 * Returns all file mounts that are registered with this storage.
401 *
402 * @return array
403 */
404 public function getFileMounts() {
405 return $this->fileMounts;
406 }
407
408 /**
409 * Checks if the given subject is within one of the registered user filemounts. If not, working with the file
410 * is not permitted for the user.
411 *
412 * @param $subject
413 * @return boolean
414 */
415 public function isWithinFileMountBoundaries($subject) {
416 $isWithinFilemount = TRUE;
417 if (is_array($this->fileMounts)) {
418 $isWithinFilemount = FALSE;
419
420 if (!$subject) {
421 $subject = $this->getRootLevelFolder();
422 }
423 $identifier = $subject->getIdentifier();
424
425 // check if the identifier of the subject is within at
426 // least one of the file mounts
427 foreach ($this->fileMounts as $fileMount) {
428 if ($this->driver->isWithin($fileMount['folder'], $identifier)) {
429 $isWithinFilemount = TRUE;
430 break;
431 }
432 }
433 }
434 return $isWithinFilemount;
435 }
436
437 /**
438 * Adds user permissions to the storage
439 *
440 * @param array $userPermissions
441 * @return void
442 */
443 public function injectUserPermissions(array $userPermissions) {
444 $this->userPermissions = $userPermissions;
445 }
446
447 /**
448 * Check if the ACL settings allow for a certain action
449 * (is a user allowed to read a file or copy a folder)
450 *
451 * @param string $action
452 * @param string $type either File or Folder
453 * @return bool
454 */
455 public function checkUserActionPermission($action, $type) {
456 // TODO decide if we should return TRUE if no permissions are set
457 if (!empty($this->userPermissions)) {
458 $action = strtolower($action);
459 $type = ucfirst(strtolower($type));
460 if ($this->userPermissions[$action . $type] == 0) {
461 return FALSE;
462 } else {
463 return TRUE;
464 }
465 } else {
466 return TRUE;
467 }
468 }
469
470 /**
471 * Check if a file operation (= action) is allowed on a File/Folder/Storage (= subject).
472 *
473 * This method, by design, does not throw exceptions or do logging.
474 * Besides the usage from other methods in this class, it is also used by the File List UI to check whether
475 * an action is allowed and whether action related UI elements should thus be shown (move icon, edit icon, etc.)
476 *
477 * @param string $action, can be read, write, delete
478 * @param t3lib_file_FileInterface $file
479 * @return boolean
480 */
481 public function checkFileActionPermission($action, t3lib_file_FileInterface $file) {
482 // check 1: Does the user have permission to perform the action? e.g. "readFile"
483 if ($this->checkUserActionPermission($action, 'File') === FALSE) {
484 return FALSE;
485 }
486
487 // check 2: Does the user have the right to perform the action?
488 // (= is he/she within the file mount borders)
489 if (is_array($this->fileMounts) && count($this->fileMounts) && !$this->isWithinFileMountBoundaries($file)) {
490 return FALSE;
491 }
492
493
494 $isReadCheck = FALSE;
495 if (in_array($action, array('read'))) {
496 $isReadCheck = TRUE;
497 }
498
499 $isWriteCheck = FALSE;
500 if (in_array($action, array('write', 'delete'))) {
501 $isWriteCheck = TRUE;
502 }
503
504 // check 3: Check the capabilities of the storage (and the driver)
505 if ($isReadCheck && !$this->isBrowsable()) {
506 return FALSE;
507 }
508 if ($isWriteCheck && !$this->isWritable()) {
509 return FALSE;
510 }
511
512 // check 4: "File permissions" of the driver
513 $filePermissions = $this->driver->getFilePermissions($file);
514 if ($isReadCheck && !$filePermissions['r']) {
515 return FALSE;
516 # we can't thrown an exception here, as this function is just for asking whether the user has the permission. It's also used by the UI. Therefore, the following exception is commented out.
517 #throw new t3lib_file_exception_InsufficientFileReadPermissionsException("TYPO3 has no permission to read file " . $file->getIdentifier());
518 }
519 if ($isWriteCheck && !$filePermissions['w']) {
520 return FALSE;
521 # see comment above.
522 #throw new t3lib_file_exception_InsufficientFileWritePermissionsException("TYPO3 has no permission to write to file " . $file->getIdentifier());
523 }
524
525 return TRUE;
526 }
527
528 /**
529 * Check if a folder operation (= action) is allowed on a Folder
530 *
531 * This method, by design, does not throw exceptions or do logging.
532 * See the checkFileActionPermission() method above for the reasons.
533 *
534 * @param string $action
535 * @param t3lib_file_Folder $folder
536 * @return boolean
537 */
538 public function checkFolderActionPermission($action, t3lib_file_Folder $folder = NULL) {
539 // check 1: Does the user have permission to perform the action? e.g. "writeFolder"
540 if ($this->checkUserActionPermission($action, 'Folder') === FALSE) {
541 return FALSE;
542 }
543
544 // check 2: Does the user have the right to perform the action?
545 // (= is he/she within the file mount borders)
546 if (is_array($this->fileMounts) && count($this->fileMounts) && !$this->isWithinFileMountBoundaries($folder)) {
547 return FALSE;
548 }
549
550
551 $isReadCheck = FALSE;
552 if (in_array($action, array('read'))) {
553 $isReadCheck = TRUE;
554 }
555
556 $isWriteCheck = FALSE;
557 if (in_array($action, array('write', 'delete', 'deleteRecursive'))) {
558 $isWriteCheck = TRUE;
559 }
560
561 // check 3: Check the capabilities of the storage (and the driver)
562 if ($isReadCheck && !$this->isBrowsable()) {
563 return FALSE;
564 }
565 if ($isWriteCheck && !$this->isWritable()) {
566 return FALSE;
567 }
568
569 // check 4: "Folder permissions" of the driver
570 $folderPermissions = $this->driver->getFolderPermissions($folder);
571 if ($isReadCheck && !$folderPermissions['r']) {
572 return FALSE;
573 # we can't thrown an exception here, as this function is just for asking whether the user has the permission. It's also used by the UI. Therefore, the following exception is commented out.
574 # throw new t3lib_file_exception_InsufficientFolderReadPermissionsException("TYPO3 has no permission to read from folder " . $folder->getIdentifier());
575 }
576 if ($isWriteCheck && !$folderPermissions['w']) {
577 return FALSE;
578 # see comment above
579 # throw new t3lib_file_exception_InsufficientFolderWritePermissionsException("TYPO3 has no permission to write to folder " . $folder->getIdentifier());
580 }
581
582 return TRUE;
583 }
584
585 /**
586 * If the fileName is given, check it against the TYPO3_CONF_VARS[BE][fileDenyPattern] +
587 * and if the file extension is allowed
588 *
589 * @param string $fileName Full filename
590 * @return boolean TRUE if extension/filename is allowed
591 *
592 * if (!$this->checkIfAllowed($fI['fileext'], $theDest, $fI['file'])) {
593 * @formallyknownas t3lib_basicfilefunc::checkIfAllowed(), and is_allowed()
594 */
595 protected function checkFileExtensionPermission($fileName) {
596 $isAllowed = t3lib_div::verifyFilenameAgainstDenyPattern($fileName);
597
598 if ($isAllowed) {
599 $fileInfo = t3lib_div::split_fileref($fileName);
600
601 // set up the permissions for the file extension
602 $fileExtensionPermissions = $GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']['webspace'];
603 $fileExtensionPermissions['allow'] = t3lib_div::uniqueList(strtolower($fileExtensionPermissions['allow']));
604 $fileExtensionPermissions['deny'] = t3lib_div::uniqueList(strtolower($fileExtensionPermissions['deny']));
605
606 $fileExtension = strtolower($fileInfo['fileext']);
607 if ($fileExtension !== '') {
608 // If the extension is found amongst the allowed types, we return TRUE immediately
609 if ($fileExtensionPermissions['allow'] === '*' || t3lib_div::inList($fileExtensionPermissions['allow'], $fileExtension)) {
610 return TRUE;
611 }
612 // If the extension is found amongst the denied types, we return FALSE immediately
613 if ($fileExtensionPermissions['deny'] === '*' || t3lib_div::inList($fileExtensionPermissions['deny'], $fileExtension)) {
614 return FALSE;
615 }
616 // If no match we return TRUE
617 return TRUE;
618
619 // no file extension
620 } else {
621 if ($fileExtensionPermissions['allow'] === '*') {
622 return TRUE;
623 }
624 if ($fileExtensionPermissions['deny'] === '*') {
625 return FALSE;
626 }
627 return TRUE;
628 }
629 }
630 return FALSE;
631 }
632
633
634
635
636 /********************
637 * FILE ACTIONS
638 ********************/
639
640
641 /**
642 * Moves a file from the local filesystem to this storage.
643 *
644 * @param string $localFilePath The file on the server's hard disk to add.
645 * @param t3lib_file_Folder $targetFolder The target path, without the fileName
646 * @param string $fileName The fileName. If not set, the local file name is used.
647 * @param string $conflictMode possible value are 'cancel', 'replace', 'changeName'
648 * @return t3lib_file_FileInterface
649 */
650 public function addFile($localFilePath, t3lib_file_Folder $targetFolder, $fileName = '', $conflictMode = 'changeName') {
651 // TODO check permissions (write on target, upload, ...)
652
653 if (!file_exists($localFilePath)) {
654 throw new InvalidArgumentException("File $localFilePath does not exist.", 1319552745);
655 }
656
657 $targetFolder = $targetFolder ? $targetFolder : $this->getDefaultFolder();
658 $fileName = $fileName ? $fileName : basename($localFilePath);
659
660 if ($conflictMode == 'cancel' && $this->driver->fileExistsInFolder($fileName, $targetFolder)) {
661 throw new t3lib_file_exception_ExistingTargetFileNameException("File $fileName already exists in folder "
662 . $targetFolder->getIdentifier(), 1322121068);
663 } elseif ($conflictMode == 'changeName') {
664 $fileName = $this->getUniqueName($targetFolder, $fileName);
665 } // we do not care whether the file exists if $conflictMode is "replace", so just use the name as is in that case
666
667 return $this->driver->addFile($localFilePath, $targetFolder, $fileName);
668 }
669
670 /**
671 * Creates a (cryptographic) hash for a file.
672 *
673 * @param t3lib_file_FileInterface $fileObject
674 * @param $hash
675 * @return string
676 */
677 public function hashFile(t3lib_file_FileInterface $fileObject, $hash) {
678 return $this->driver->hash($fileObject, $hash);
679 }
680
681 /**
682 * Returns a publicly accessible URL for a file.
683 *
684 * WARNING: Access to the file may be restricted by further means, e.g. some web-based authentication. You have to take care of this
685 * yourself.
686 *
687 * @param t3lib_file_FileInterface $fileObject The file object
688 * @param bool $relativeToCurrentScript Determines whether the URL returned should be relative to the current script, in case it is relative at all (only for the LocalDriver)
689 * @return string
690 */
691 public function getPublicUrlForFile(t3lib_file_FileInterface $fileObject, $relativeToCurrentScript = FALSE) {
692 return $this->driver->getPublicUrl($fileObject, $relativeToCurrentScript);
693 }
694
695 /**
696 * Returns a publicly accessible URL for a file.
697 *
698 * @param t3lib_file_FileInterface $fileObject The file object
699 * @param string $context
700 * @param array $configuration
701 * @return t3lib_file_ProcessedFile
702 */
703 public function processFile(t3lib_file_FileInterface $fileObject, $context, array $configuration) {
704 $processedFile = $this->getFileFactory()->getProcessedFileObject(
705 $fileObject,
706 $context,
707 $configuration
708 );
709
710 // Pre-process the file by an accordant slot
711 $this->emitPreFileProcess($processedFile, $fileObject, $context, $configuration);
712
713 // only do something if the file is not processed yet
714 // (maybe modified or already processed by a signal)
715 // or (in case of preview images) already in the DB/in the processing folder
716 if (!$processedFile->isProcessed()) {
717 $processedFile = $this->getFileProcessingService()->process(
718 $processedFile,
719 $fileObject,
720 $context,
721 $configuration
722 );
723 }
724
725 // Post-process (enrich) the file by an accordant slot
726 $this->emitPostFileProcess($processedFile, $fileObject, $context, $configuration);
727
728 return $processedFile;
729 }
730
731 /**
732 * Copies a file from the storage for local processing.
733 *
734 * @param t3lib_file_FileInterface $fileObject
735 * @param bool $writable
736 * @return string Path to local file (either original or copied to some temporary local location)
737 */
738 public function getFileForLocalProcessing(t3lib_file_FileInterface $fileObject, $writable = TRUE) {
739 $filePath = $this->driver->getFileForLocalProcessing($fileObject, $writable);
740 touch($filePath, $fileObject->getModificationTime());
741
742 return $filePath;
743 }
744
745 /**
746 * Get file by identifier
747 *
748 * @param string $identifier
749 * @return t3lib_file_FileInterface
750 */
751 public function getFile($identifier) {
752 return $this->driver->getFile($identifier);
753 }
754
755
756 /**
757 * Get file by identifier
758 *
759 * @param string $identifier
760 * @return t3lib_file_FileInterface
761 */
762 public function getFileInfo($file) {
763 return $this->driver->getFileInfo($file);
764 }
765
766 /**
767 * Get file by identifier
768 *
769 * @deprecated To be removed before final release of FAL. Use combination of getFileInfoByIdentifier() with a file object as argument instead.
770 *
771 * @param string $identifier
772 * @return t3lib_file_FileInterface
773 */
774 public function getFileInfoByIdentifier($identifier) {
775 return $this->driver->getFileInfoByIdentifier($identifier);
776 }
777
778
779 /**
780 * Returns a list of files in a given path.
781 *
782 * @param string $path The path to list
783 * @param string $pattern The pattern the files have to match
784 * @param integer $start The position to start the listing; if not set or 0, start from the beginning
785 * @param integer $numberOfItems The number of items to list; if not set, return all items
786 * @param bool $excludeHiddenFiles Set this to TRUE to exclude hidden files (starting with a dot)
787 * @param bool $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.
788 * @return array Information about the files found.
789 */
790 // TODO check if we should use a folder object instead of $path
791 // TODO add unit test for $loadIndexRecords
792 public function getFileList($path, $pattern = '', $start = 0, $numberOfItems = 0, $excludeHiddenFiles = TRUE, $loadIndexRecords = TRUE) {
793 $rows = array();
794 if ($loadIndexRecords) {
795 /** @var $repository t3lib_file_Repository_FileRepository */
796 $repository = t3lib_div::makeInstance('t3lib_file_Repository_FileRepository');
797 $rows = $repository->getFileIndexRecordsForFolder($this->getFolder($path));
798 }
799
800 $items = $this->driver->getFileList($path, $pattern, $start, $numberOfItems, $excludeHiddenFiles, $rows);
801 uksort($items, 'strnatcasecmp');
802
803 return $items;
804 }
805
806 /**
807 * Returns TRUE if the specified file exists.
808 *
809 * @param string $identifier
810 * @return boolean
811 */
812 public function hasFile($identifier) {
813 // @todo: access check?
814 return $this->driver->fileExists($identifier);
815 }
816
817 /**
818 * Checks if the queried file in the given folder exists.
819 *
820 * @param string $fileName
821 * @param t3lib_file_Folder $folder
822 * @return boolean
823 */
824 public function hasFileInFolder($fileName, t3lib_file_Folder $folder) {
825 return $this->driver->fileExistsInFolder($fileName, $folder);
826 }
827
828
829 /**
830 * Get contents of a file object
831 *
832 * @param t3lib_file_FileInterface $file
833 * @return string
834 */
835 public function getFileContents($file) {
836 // check if $file is readable
837 if (!$this->checkFileActionPermission('read', $file)) {
838 throw new t3lib_file_exception_InsufficientFileReadPermissionsException('Reading file '
839 . $file->getIdentifier() . ' is not allowed.', 1330121089);
840 }
841
842 return $this->driver->getFileContents($file);
843 }
844
845 /**
846 * Set contents of a file object.
847 *
848 * @param t3lib_file_AbstractFile $file
849 * @param string $contents
850 * @return integer The number of bytes written to the file
851 */
852 public function setFileContents(t3lib_file_AbstractFile $file, $contents) {
853 // TODO does setting file contents require update permission?
854 // check if user is allowed to update
855 if (!$this->checkUserActionPermission('update', 'File')) {
856 throw new t3lib_file_exception_InsufficientUserPermissionsException('Updating file '
857 . $file->getIdentifier() . ' not allowed for user.', 1330121117);
858 }
859
860 // check if $file is writable
861 if (!$this->checkFileActionPermission('write', $file)) {
862 throw new t3lib_file_exception_InsufficientFileWritePermissionsException('Writing to file '
863 . $file->getIdentifier() . ' is not allowed.', 1330121088);
864 }
865
866 // call driver method to update the file and update file properties afterwards
867 try {
868 $result = $this->driver->setFileContents($file, $contents);
869
870 $fileInfo = $this->driver->getFileInfo($file);
871 $fileInfo['sha1'] = $this->driver->hash($file, 'sha1');
872 $file->updateProperties($fileInfo);
873 } catch (RuntimeException $e) {
874 throw $e;
875 }
876
877 return $result;
878 }
879
880 /**
881 * Creates a new file
882 *
883 * previously in t3lib_extFileFunc::func_newfile()
884 *
885 * @param string $fileName
886 * @param t3lib_file_Folder $targetFolderObject
887 * @return t3lib_file_FileInterface The file object
888 */
889 public function createFile($fileName, t3lib_file_Folder $targetFolderObject) {
890 if (!$this->checkFolderActionPermission('createFile', $targetFolderObject)) {
891 throw new t3lib_file_exception_InsufficientFolderWritePermissionsException('You are not allowed to create directories on this storage "' . $targetFolderObject->getIdentifier() . '"', 1323059807);
892 }
893 return $this->driver->createFile($fileName, $targetFolderObject);
894 }
895
896 /**
897 * Previously in t3lib_extFileFunc::deleteFile()
898 * @param $fileObject t3lib_file_FileInterface
899 * @return bool TRUE if deletion succeeded
900 *
901 * TODO throw FileInUseException when the file is still used anywhere
902 */
903 public function deleteFile($fileObject) {
904 if (!$this->checkFileActionPermission('delete', $fileObject)) {
905 throw new t3lib_file_exception_InsufficientFileAccessPermissionsException('You are not allowed to delete the file "' . $fileObject->getIdentifier() . "'", 1319550425);
906 }
907
908 $result = $this->driver->deleteFile($fileObject);
909
910 if ($result === FALSE) {
911 throw new t3lib_file_exception_FileOperationErrorException('Deleting the file "' . $fileObject->getIdentifier() . "' failed.", 1329831691);
912 }
913
914 // mark the file object as deleted
915 $fileObject->setDeleted();
916
917 return TRUE;
918 }
919
920 /**
921 * Previously in t3lib_extFileFunc::func_copy()
922 * copies a source file (from any location) in to the target
923 * folder, the latter has to be part of this storage
924 *
925 * @param t3lib_file_FileInterface $file
926 * @param t3lib_file_Folder $targetFolder
927 * @param string $conflictMode "overrideExistingFile", "renameNewFile", "cancel"
928 * @param string $targetFileName an optional destination fileName
929 * @return t3lib_file_FileInterface
930 */
931 public function copyFile(t3lib_file_FileInterface $file, t3lib_file_Folder $targetFolder, $targetFileName = NULL, $conflictMode = 'renameNewFile') {
932 $this->emitPreFileCopySignal($file, $targetFolder);
933
934 $this->checkFileCopyPermissions($file, $targetFolder, $targetFileName);
935
936 if ($targetFileName === NULL) {
937 $targetFileName = $file->getName();
938 }
939
940 // file exists and we should abort, let's abort
941 if ($conflictMode === 'cancel' && $targetFolder->hasFile($targetFileName)) {
942 throw new t3lib_file_exception_ExistingTargetFileNameException('The target file already exists.', 1320291063);
943 }
944
945 // file exists and we should find another name, let's find another one
946 if ($conflictMode === 'renameNewFile' && $targetFolder->hasFile($targetFileName)) {
947 $targetFileName = $this->getUniqueName($targetFolder, $targetFileName);
948 }
949
950 $sourceStorage = $file->getStorage();
951 // call driver method to create a new file from an existing file object,
952 // and return the new file object
953 try {
954 if ($sourceStorage == $this) {
955 $newFileObject = $this->driver->copyFileWithinStorage($file, $targetFolder, $targetFileName);
956 } else {
957 $tempPath = $file->getForLocalProcessing();
958 $newFileObject = $this->driver->addFile($tempPath, $targetFolder, $targetFileName);
959 // TODO update metadata
960 }
961 } catch (t3lib_file_exception_AbstractFileOperationException $e) {
962 throw $e;
963 }
964
965 $this->emitPostFileCopySignal($file, $targetFolder);
966
967 return $newFileObject;
968 }
969
970 /**
971 * Check if a file has the permission to be uploaded to a Folder/Storage,
972 * if not throw an exception
973 *
974 * @param string $localFilePath the temporary file name from $_FILES['file1']['tmp_name']
975 * @param t3lib_file_Folder $targetFolder
976 * @param string $targetFileName the destination file name $_FILES['file1']['name']
977 * @param int $uploadedFileSize
978 * @return void
979 */
980 protected function checkFileUploadPermissions($localFilePath, $targetFolder, $targetFileName, $uploadedFileSize) {
981
982 // Makes sure the user is allowed to upload
983 if (!$this->checkUserActionPermission('upload', 'File')) {
984 throw new t3lib_file_exception_InsufficientUserPermissionsException('You are not allowed to upload files to this storage "' . $this->getUid() . '"', 1322112430);
985 }
986
987 // Makes sure this is an uploaded file
988 if (!is_uploaded_file($localFilePath)) {
989 throw new t3lib_file_exception_UploadException("The upload has failed, no uploaded file found!", 1322110455);
990 }
991
992 // max upload size (kb) for files. Remember that PHP has an inner limit often set to 2 MB
993 $maxUploadFileSize = t3lib_div::getMaxUploadFileSize() * 1024;
994 if ($uploadedFileSize >= $maxUploadFileSize) {
995 throw new t3lib_file_exception_UploadSizeException("The uploaded file exceeds the size-limit of $maxUploadFileSize bytes", 1322110041);
996 }
997
998 // check if targetFolder is writable
999 if (!$this->checkFolderActionPermission('write', $targetFolder)) {
1000 throw new t3lib_file_exception_InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $targetFolder->getIdentifier() . '"', 1322120356);
1001 }
1002
1003 // check for a valid file extension
1004 if (!$this->checkFileExtensionPermission($targetFileName)) {
1005 throw new t3lib_file_exception_IllegalFileExtensionException("Extension of file name is not allowed in \"$targetFileName\"!", 1322120271);
1006 }
1007 }
1008
1009 /**
1010 * Check if a file has the permission to be copied on a File/Folder/Storage,
1011 * if not throw an exception
1012 *
1013 * @param t3lib_file_FileInterface $file
1014 * @param t3lib_file_Folder $targetFolder
1015 * @param string $targetFileName
1016 * @return void
1017 */
1018 protected function checkFileCopyPermissions(t3lib_file_FileInterface $file, t3lib_file_Folder $targetFolder, $targetFileName) {
1019 // check if targetFolder is within this storage, this should never happen
1020 if ($this->getUid() != $targetFolder->getStorage()->getUid()) {
1021 throw new t3lib_file_exception_AbstractFileException('The operation of the folder cannot be called by this storage "' . $this->getUid() . '"', 1319550405);
1022 }
1023
1024 // check if user is allowed to copy
1025 if (!$this->checkUserActionPermission('copy', 'File')) {
1026 throw new t3lib_file_exception_InsufficientUserPermissionsException('You are not allowed to copy files to this storage "' . $this->getUid() . '"', 1319550415);
1027 }
1028
1029 // check if $file is readable
1030 if (!$this->checkFileActionPermission('read', $file)) {
1031 throw new t3lib_file_exception_InsufficientFileReadPermissionsException('You are not allowed to read the file "' . $file->getIdentifier() . "'", 1319550425);
1032 }
1033
1034 // check if targetFolder is writable
1035 if (!$this->checkFolderActionPermission('write', $targetFolder)) {
1036 throw new t3lib_file_exception_InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $targetFolder->getIdentifier() . '"', 1319550435);
1037 }
1038
1039 // check for a valid file extension
1040 if (!$this->checkFileExtensionPermission($targetFileName)) {
1041 throw new t3lib_file_exception_IllegalFileExtensionException('You are not allowed to copy a file of that type.', 1319553317);
1042 }
1043 }
1044
1045 /**
1046 * Moves a $file into a $targetFolder
1047 * the target folder has to be part of this storage
1048 *
1049 * previously in t3lib_extFileFunc::func_move()
1050 * @param t3lib_file_FileInterface $file
1051 * @param t3lib_file_Folder $targetFolder
1052 * @param string $conflictMode "overrideExistingFile", "renameNewFile", "cancel"
1053 * @param string $targetFileName an optional destination fileName
1054 * @return t3lib_file_FileInterface
1055 */
1056 public function moveFile($file, $targetFolder, $targetFileName = NULL, $conflictMode = 'renameNewFile') {
1057 $this->checkFileMovePermissions($file, $targetFolder);
1058
1059 if ($targetFileName === NULL) {
1060 $targetFileName = $file->getName();
1061 }
1062
1063 if ($targetFolder->hasFile($targetFileName)) {
1064 // file exists and we should abort, let's abort
1065 if ($conflictMode === 'renameNewFile') {
1066 $targetFileName = $this->getUniqueName($targetFolder, $targetFileName);
1067
1068 // cancel if requested so
1069 } elseif ($conflictMode == 'cancel') {
1070 throw new t3lib_file_exception_ExistingTargetFileNameException('The target file already exists', 1329850997);
1071 } // when $conflictMode is "overrideExistingFile", just ignore the naming conflict
1072 // TODO check what to do with an existing index record for the target file then
1073 }
1074
1075 $this->emitPreFileMoveSignal($file, $targetFolder);
1076
1077 $sourceStorage = $file->getStorage();
1078 // call driver method to move the file
1079 // that also updates the file object properties
1080 try {
1081 if ($sourceStorage == $this) {
1082 $newIdentifier = $this->driver->moveFileWithinStorage($file, $targetFolder, $targetFileName);
1083
1084 $this->updateFile($file, $newIdentifier);
1085 } else {
1086 $tempPath = $file->getForLocalProcessing();
1087 $newIdentifier = $this->driver->addFileRaw($tempPath, $targetFolder, $targetFileName);
1088 $sourceStorage->driver->deleteFileRaw($file->getIdentifier());
1089
1090 $this->updateFile($file, $newIdentifier, $this);
1091 }
1092 } catch (t3lib_exception $e) {
1093 echo $e->getMessage();
1094 // TODO rollback things that have happened
1095 // TODO emit FileMoveFailedSignal?
1096 }
1097
1098 $this->emitPostFileMoveSignal($file, $targetFolder);
1099
1100 return $file;
1101 }
1102
1103 /**
1104 * Updates the properties of a file object with some that are freshly fetched from the driver.
1105 *
1106 * @param t3lib_file_AbstractFile $file
1107 * @param string $identifier The identifier of the file. If set, this will overwrite the file object's identifier (use e.g. after moving a file)
1108 * @param t3lib_file_Storage $storage
1109 * @return void
1110 */
1111 protected function updateFile(t3lib_file_AbstractFile $file, $identifier = '', $storage = NULL) {
1112 if ($identifier == '') {
1113 $identifier = $file->getIdentifier();
1114 }
1115 $fileInfo = $this->driver->getFileInfoByIdentifier($identifier);
1116 // TODO extend mapping
1117 $newProperties = array(
1118 'storage' => $fileInfo['storage'],
1119 'identifier' => $fileInfo['identifier'],
1120 'tstamp' => $fileInfo['mtime'],
1121 'crdate' => $fileInfo['ctime'],
1122 'mime_type' => $fileInfo['mimetype'],
1123 'size' => $fileInfo['size'],
1124 'tstamp' => $fileInfo['mtime']
1125 );
1126 if ($storage !== NULL) {
1127 $newProperties['storage'] = $storage->getUid();
1128 }
1129
1130 $file->updateProperties($newProperties);
1131
1132 /** @var $fileRepository t3lib_file_Repository_FileRepository */
1133 $fileRepository = t3lib_div::makeInstance('t3lib_file_Repository_FileRepository');
1134 $fileRepository->update($file);
1135 }
1136
1137 /**
1138 * Checks for permissions to move a file.
1139 *
1140 * @throws RuntimeException|t3lib_file_exception_InsufficientFileReadPermissionsException|t3lib_file_exception_InsufficientFileWritePermissionsException|t3lib_file_exception_InsufficientFolderAccessPermissionsException|t3lib_file_exception_InsufficientUserPermissionsException
1141 * @param t3lib_file_FileInterface $file
1142 * @param t3lib_file_Folder $targetFolder
1143 * @return void
1144 */
1145 protected function checkFileMovePermissions(t3lib_file_FileInterface $file, t3lib_file_Folder $targetFolder) {
1146 // check if targetFolder is within this storage
1147 if ($this->getUid() != $targetFolder->getStorage()->getUid()) {
1148 throw new RuntimeException();
1149 }
1150
1151 // check if user is allowed to move
1152 if (!$this->checkUserActionPermission('move', 'File')) {
1153 throw new t3lib_file_exception_InsufficientUserPermissionsException('You are not allowed to move files to storage "' . $this->getUid() . '"', 1319219349);
1154 }
1155
1156 // check if $file is readable
1157 if (!$this->checkFileActionPermission('read', $file)) {
1158 throw new t3lib_file_exception_InsufficientFileReadPermissionsException('You are not allowed to read the file "' . $file->getIdentifier() . "'", 1319219349);
1159 }
1160
1161 // check if $file is writable
1162 if (!$this->checkFileActionPermission('write', $file)) {
1163 throw new t3lib_file_exception_InsufficientFileWritePermissionsException('You are not allowed to move the file "' . $file->getIdentifier() . "'", 1319219349);
1164 }
1165
1166 // check if targetFolder is writable
1167 if (!$this->checkFolderActionPermission('write', $targetFolder)) {
1168 throw new t3lib_file_exception_InsufficientFolderAccessPermissionsException('You are not allowed to write to the target folder "' . $targetFolder->getIdentifier() . '"', 1319219349);
1169 }
1170 }
1171
1172 /**
1173 * Previously in t3lib_extFileFunc::func_rename()
1174 *
1175 * @param t3lib_file_FileInterface $file
1176 * @param string $targetFileName
1177 * @return t3lib_file_FileInterface
1178 */
1179 // TODO add $conflictMode setting
1180 public function renameFile($file, $targetFileName) {
1181
1182 // The name should be different from the current.
1183 if ($file->getIdentifier() == $targetFileName) {
1184 return $file;
1185 }
1186
1187 // check if user is allowed to rename
1188 if (!$this->checkUserActionPermission('rename', 'File')) {
1189 throw new t3lib_file_exception_InsufficientUserPermissionsException('You are not allowed to rename files."', 1319219349);
1190 }
1191
1192 // check if $file is readable
1193 if (!$this->checkFileActionPermission('read', $file)) {
1194 throw new t3lib_file_exception_InsufficientFileReadPermissionsException('You are not allowed to read the file "' . $file->getIdentifier() . "'", 1319219349);
1195 }
1196
1197 // check if $file is writable
1198 if (!$this->checkFileActionPermission('write', $file)) {
1199 throw new t3lib_file_exception_InsufficientFileWritePermissionsException('You are not allowed to rename the file "' . $file->getIdentifier() . "'", 1319219349);
1200 }
1201
1202 // call driver method to rename the file
1203 // that also updates the file object properties
1204 try {
1205 $newIdentifier = $this->driver->renameFile($file, $targetFileName);
1206
1207 $this->updateFile($file, $newIdentifier);
1208 /** @var $fileRepository t3lib_file_Repository_FileRepository */
1209 $fileRepository = t3lib_div::makeInstance('t3lib_file_Repository_FileRepository');
1210 $fileRepository->update($file);
1211 } catch(RuntimeException $e) {
1212 // rename failed (maybe because file existed?)
1213 // todo: bubble exception?
1214 }
1215
1216 return $file;
1217 }
1218
1219 /**
1220 * Replaces a file with a local file (e.g. a freshly uploaded file)
1221 *
1222 * @param t3lib_file_FileInterface $file
1223 * @param string $localFilePath
1224 * @return t3lib_file_FileInterface
1225 */
1226 public function replaceFile(t3lib_file_FileInterface $file, $localFilePath) {
1227 if (!file_exists($localFilePath)) {
1228 throw new InvalidArgumentException("File '$localFilePath' does not exist.", 1325842622);
1229 }
1230
1231 // TODO check permissions
1232
1233 $this->emitPreFileReplaceSignal($file, $localFilePath);
1234
1235 $result = $this->driver->replaceFile($file, $localFilePath);
1236
1237 $this->emitPostFileReplaceSignal($file, $localFilePath);
1238
1239 return $result;
1240 }
1241
1242 /**
1243 * Adds an uploaded file into the Storage. Previously in t3lib_extFileFunc::file_upload()
1244 *
1245 * @param array $uploadedFileData contains information about the uploaded file given by $_FILES['file1']
1246 * @param t3lib_file_Folder $targetFolder the target folder
1247 * @param string $targetFileName the file name to be written
1248 * @param string $conflictMode possible value are 'cancel', 'replace'
1249 * @return t3lib_file_FileInterface The file object
1250 */
1251 public function addUploadedFile(array $uploadedFileData, t3lib_file_Folder $targetFolder = NULL, $targetFileName = NULL, $conflictMode = 'cancel') {
1252
1253 $localFilePath = $uploadedFileData['tmp_name'];
1254 if ($targetFolder === NULL) {
1255 $targetFolder = $this->getDefaultFolder();
1256 }
1257
1258 if ($targetFileName === NULL) {
1259 $targetFileName = $uploadedFileData['name'];
1260 }
1261
1262 // handling $conflictMode is delegated to addFile()
1263
1264 $this->checkFileUploadPermissions($localFilePath, $targetFolder, $targetFileName, $uploadedFileData['size']);
1265
1266 $resultObject = $this->addFile($localFilePath, $targetFolder, $targetFileName, $conflictMode);
1267 return $resultObject;
1268 }
1269
1270 /********************
1271 * FOLDER ACTIONS
1272 ********************/
1273
1274 /**
1275 * Returns an array with all file objects in a folder and its subfolders, with the file identifiers as keys.
1276 *
1277 * @param t3lib_file_Folder $folder
1278 * @return t3lib_file_File[]
1279 */
1280 protected function getAllFileObjectsInFolder(t3lib_file_Folder $folder) {
1281 $files = array();
1282 $folderQueue = array($folder);
1283
1284 while (!empty($folderQueue)) {
1285 $folder = array_shift($folderQueue);
1286 foreach ($folder->getSubfolders() as $subfolder) {
1287 $folderQueue[] = $subfolder;
1288 }
1289
1290 foreach ($folder->getFiles() as $file) {
1291 $files[$file->getIdentifier()] = $file;
1292 }
1293 }
1294
1295 return $files;
1296 }
1297
1298 /**
1299 * Moves a folder. If you want to move a folder from this storage to another one, call this method on the target storage,
1300 * otherwise you will get an exception.
1301 *
1302 * @param t3lib_file_Folder $folderToMove The folder to move.
1303 * @param t3lib_file_Folder $targetParentFolder The target parent folder
1304 * @param string $newFolderName
1305 * @param string $conflictMode How to handle conflicts; one of "overrideExistingFile", "renameNewFolder", "cancel"
1306 * @return t3lib_file_Folder
1307 */
1308 // TODO add tests
1309 public function moveFolder(t3lib_file_Folder $folderToMove, t3lib_file_Folder $targetParentFolder, $newFolderName = NULL, $conflictMode = 'renameNewFolder') {
1310 $sourceStorage = $folderToMove->getStorage();
1311
1312 if (!$targetParentFolder->getStorage() == $this) {
1313 throw new InvalidArgumentException('Cannot move a folder into a folder that does not belong to this storage.', 1325777289);
1314 }
1315
1316 $newFolderName = $newFolderName ? $newFolderName : $folderToMove->getName();
1317
1318 // TODO check if folder already exists in $targetParentFolder, handle this conflict then
1319
1320 $this->emitPreFolderMoveSignal($folderToMove, $targetParentFolder, $newFolderName);
1321
1322 // get all file objects now so we are able to update them after moving the folder
1323 $fileObjects = $this->getAllFileObjectsInFolder($folderToMove);
1324 try {
1325 if ($sourceStorage == $this) {
1326 $fileMappings = $this->driver->moveFolderWithinStorage($folderToMove, $targetParentFolder, $newFolderName);
1327
1328 } else {
1329 $fileMappings = $this->moveFolderBetweenStorages($folderToMove, $targetParentFolder, $newFolderName);
1330 }
1331
1332 // update the identifier and storage of all file objects
1333 foreach ($fileObjects as $oldIdentifier => $fileObject) {
1334 $newIdentifier = $fileMappings[$oldIdentifier];
1335 $fileObject->updateProperties(array('storage' => $this, 'identifier' => $newIdentifier));
1336 }
1337 } catch (t3lib_exception $e) {
1338 throw $e;
1339 // TODO rollback things that have happened
1340 }
1341
1342 $this->emitPostFolderMoveSignal($folderToMove, $targetParentFolder, $newFolderName);
1343 }
1344
1345 /**
1346 * Moves the given folder from a different storage to the target folder in this storage.
1347 *
1348 * @param t3lib_file_Folder $folderToMove
1349 * @param t3lib_file_Folder $targetParentFolder
1350 * @param string $newFolderName
1351 * @return array Mapping of old file identifiers to new ones
1352 */
1353 protected function moveFolderBetweenStorages(t3lib_file_Folder $folderToMove, t3lib_file_Folder $targetParentFolder, $newFolderName = NULL) {
1354 // This is not implemented for now as moving files between storages might cause quite some headaches when
1355 // something goes wrong. It is also not that common of a use case, so it does not hurt that much to leave it out
1356 // for now.
1357 throw new BadMethodCallException('Moving folders between storages is not implemented.');
1358 }
1359
1360 /**
1361 * Copy folder
1362 *
1363 * @param t3lib_file_Folder $folderToCopy The folder to copy
1364 * @param t3lib_file_Folder $targetParentFolder The target folder
1365 * @param string $newFolderName
1366 * @param string $conflictMode "overrideExistingFolder", "renameNewFolder", "cancel"
1367 * @return t3lib_file_Folder The new (copied) folder object
1368 */
1369 public function copyFolder(t3lib_file_Folder $folderToCopy, t3lib_file_Folder $targetParentFolder, $newFolderName = NULL, $conflictMode = 'renameNewFolder') {
1370 // TODO implement the $conflictMode handling
1371 // TODO permission checks
1372
1373 $newFolderName = $newFolderName ? $newFolderName : $folderToCopy->getName();
1374
1375 $this->emitPreFolderCopySignal($folderToCopy, $targetParentFolder, $newFolderName);
1376
1377 $sourceStorage = $folderToCopy->getStorage();
1378 // call driver method to move the file
1379 // that also updates the file object properties
1380 try {
1381 if ($sourceStorage == $this) {
1382 $this->driver->copyFolderWithinStorage($folderToCopy, $targetParentFolder, $newFolderName);
1383 } else {
1384 $this->copyFolderBetweenStorages($folderToCopy, $targetParentFolder, $newFolderName);
1385 }
1386 } catch (t3lib_exception $e) {
1387 echo $e->getMessage();
1388 // TODO rollback things that have happened
1389 }
1390
1391 $this->emitPostFolderCopySignal($folderToCopy, $targetParentFolder, $newFolderName);
1392 }
1393
1394 /**
1395 * Moves files between storages
1396 *
1397 * @param t3lib_file_Folder $folderToMove
1398 * @param t3lib_file_Folder $targetParentFolder
1399 * @param null $newFolderName
1400 * @return void
1401 */
1402 protected function copyFolderBetweenStorages(t3lib_file_Folder $folderToMove, t3lib_file_Folder $targetParentFolder, $newFolderName = NULL) {
1403 throw new RuntimeException('Not yet implemented!', 1330262731);
1404 /**
1405 * TODO:
1406 * - get all folders, call this method for each of them
1407 * - get all files
1408 * - get a local copy
1409 * - put it into the other storage
1410 */
1411
1412 }
1413
1414 /**
1415 * Previously in t3lib_extFileFunc::folder_move()
1416 *
1417 * @param t3lib_file_Folder $folderObject
1418 * @param string $newName
1419 * @return bool TRUE if the operation succeeded
1420 * @throws RuntimeException if an error occurs during renaming
1421 */
1422 public function renameFolder($folderObject, $newName) {
1423 // TODO unit tests
1424 // TODO access checks
1425
1426 if ($this->driver->folderExistsInFolder($newName, $folderObject)) {
1427 throw new InvalidArgumentException("The folder $newName already exists in folder " . $folderObject->getIdentifier(), 1325418870);
1428 }
1429
1430 $this->emitPreFolderRenameSignal($folderObject, $newName);
1431
1432 $fileObjects = $this->getAllFileObjectsInFolder($folderObject);
1433 try {
1434 $fileMappings = $this->driver->renameFolder($folderObject, $newName);
1435
1436 // update the identifier of all file objects
1437 foreach ($fileObjects as $oldIdentifier => $fileObject) {
1438 $newIdentifier = $fileMappings[$oldIdentifier];
1439 $fileObject->updateProperties(array('identifier' => $newIdentifier));
1440 }
1441 } catch (Exception $e) {
1442 throw $e;
1443 }
1444
1445 $this->emitPostFolderRenameSignal($folderObject, $newName);
1446 }
1447
1448 /**
1449 * Previously in t3lib_extFileFunc::folder_delete()
1450 *
1451 * @param t3lib_file_Folder $folderObject
1452 * @param bool $deleteRecursively
1453 * @return bool
1454 */
1455 public function deleteFolder($folderObject, $deleteRecursively = FALSE) {
1456
1457 if (!$this->checkFolderActionPermission('delete', $folderObject)) {
1458 throw new t3lib_file_exception_InsufficientFileAccessPermissionsException('You are not allowed to access the folder "' . $folderObject->getIdentifier() . "'", 1323423953);
1459 }
1460
1461 if ($this->driver->isFolderEmpty($folderObject) && !$deleteRecursively) {
1462 throw new RuntimeException("Could not delete folder " . $folderObject->getIdentifier() . " because it is not empty.", 1325952534);
1463 }
1464
1465
1466 $this->emitPreFolderDeleteSignal($folderObject);
1467
1468 $this->driver->deleteFolder($folderObject, $deleteRecursively);
1469
1470 $this->emitPostFolderDeleteSignal($folderObject);
1471 }
1472
1473 /**
1474 * Returns a list of files in a given path.
1475 *
1476 * @param string $path The path to list
1477 * @param string $pattern The pattern the files have to match
1478 * @param integer $start The position to start the listing; if not set or 0, start from the beginning
1479 * @param integer $numberOfItems The number of items to list; if not set, return all items
1480 * @param bool $excludeHiddenFolders Set to TRUE to exclude hidden folders (starting with a dot)
1481 * @return array Information about the folders found.
1482 */
1483 public function getFolderList($path, $pattern = '', $start = 0, $numberOfItems = 0, $excludeHiddenFolders = TRUE) {
1484 $items = $this->driver->getFolderList($path, $pattern, $start, $numberOfItems, $excludeHiddenFolders);
1485 // exclude the _processed_ folder, so it won't get indexed etc
1486 $processingFolder = $this->getProcessingFolder();
1487 if ($processingFolder && $path == '/') {
1488 $processedFolderIdentifier = $this->processingFolder->getIdentifier();
1489 $processedFolderIdentifier = trim($processedFolderIdentifier, '/');
1490 if (isset($items[$processedFolderIdentifier])) {
1491 unset($items[$processedFolderIdentifier]);
1492 }
1493 }
1494 uksort($items, 'strnatcasecmp');
1495
1496 return $items;
1497 }
1498
1499 /**
1500 * Returns TRUE if the specified folder exists.
1501 *
1502 * @param $identifier
1503 * @return bool
1504 */
1505 public function hasFolder($identifier) {
1506 return $this->driver->folderExists($identifier);
1507 }
1508
1509 /**
1510 * Checks if the given file exists in the given folder
1511 *
1512 * @param string $folderName
1513 * @param t3lib_file_Folder $folder
1514 * @return boolean
1515 */
1516 public function hasFolderInFolder($folderName, t3lib_file_Folder $folder) {
1517 return $this->driver->folderExistsInFolder($folderName, $folder);
1518 }
1519
1520 /**
1521 * Creates a new folder.
1522 *
1523 * previously in t3lib_extFileFunc::func_newfolder()
1524 *
1525 * @param string $folderName the new folder name
1526 * @param t3lib_file_Folder $parentFolder The parent folder to create the new folder inside of
1527 * @return t3lib_file_Folder The new folder object
1528 */
1529 public function createFolder($folderName, t3lib_file_Folder $parentFolder) {
1530 if (!$this->checkFolderActionPermission('createFolder', $parentFolder)) {
1531 throw new t3lib_file_exception_InsufficientFolderWritePermissionsException('You are not allowed to create directories on this storage "' . $parentFolder->getIdentifier() . '"', 1323059807);
1532 }
1533
1534 if (!$this->driver->folderExists($parentFolder->getIdentifier())) {
1535 throw new InvalidArgumentException('Parent folder "' . $parentFolder->getIdentifier() . '" does not exist.', 1325689164);
1536 }
1537
1538 return $this->driver->createFolder($folderName, $parentFolder);
1539 }
1540
1541 /**
1542 * Returns the default folder where new files are stored if no other folder is given.
1543 *
1544 * @return t3lib_file_Folder
1545 */
1546 public function getDefaultFolder() {
1547 return $this->driver->getDefaultFolder();
1548 }
1549
1550 /**
1551 * @param string $identifier
1552 * @return t3lib_file_Folder
1553 */
1554 public function getFolder($identifier) {
1555 $folderObject = $this->driver->getFolder($identifier);
1556 if ($this->fileMounts && !$this->isWithinFileMountBoundaries($folderObject)) {
1557 throw new t3lib_file_exception_NotInMountPointException('Folder "' . $identifier . '" is not within your mount points.', 1330120649);
1558 } else {
1559 return $folderObject;
1560 }
1561 }
1562
1563 /**
1564 * Returns the folders on the root level of the storage
1565 * or the first mount point of this storage for this user
1566 *
1567 * @return t3lib_file_Folder
1568 */
1569 public function getRootLevelFolder() {
1570 if (count($this->fileMounts)) {
1571 $mount = reset($this->fileMounts);
1572 return $mount['folder'];
1573 } else {
1574 return $this->driver->getRootLevelFolder();
1575 }
1576 }
1577
1578 /**
1579 * Emits the configuration pre-processing signal
1580 *
1581 * @return void
1582 */
1583 protected function emitPreProcessConfigurationSignal() {
1584 $this->getSignalSlotDispatcher()->dispatch(
1585 't3lib_file_Storage',
1586 self::SIGNAL_PreProcessConfiguration,
1587 array($this)
1588 );
1589 }
1590
1591 /**
1592 * Emits the configuration post-processing signal
1593 *
1594 * @return void
1595 */
1596 protected function emitPostProcessConfigurationSignal() {
1597 $this->getSignalSlotDispatcher()->dispatch(
1598 't3lib_file_Storage',
1599 self::SIGNAL_PostProcessConfiguration,
1600 array($this)
1601 );
1602 }
1603
1604 /**
1605 * Emits file pre-copy signal
1606 *
1607 * @param t3lib_file_FileInterface $file
1608 * @param t3lib_file_Folder $targetFolder
1609 * @return void
1610 */
1611 protected function emitPreFileCopySignal(t3lib_file_FileInterface $file, t3lib_file_Folder $targetFolder) {
1612 $this->getSignalSlotDispatcher()->dispatch(
1613 't3lib_file_Storage',
1614 self::SIGNAL_PreFileCopy,
1615 array($file, $targetFolder)
1616 );
1617 }
1618
1619 /**
1620 * Emits the file post-copy signal
1621 *
1622 * @param t3lib_file_FileInterface $file
1623 * @param t3lib_file_Folder $targetFolder
1624 * @return void
1625 */
1626 protected function emitPostFileCopySignal(t3lib_file_FileInterface $file, t3lib_file_Folder $targetFolder) {
1627 $this->getSignalSlotDispatcher()->dispatch(
1628 't3lib_file_Storage',
1629 self::SIGNAL_PostFileCopy,
1630 array($file, $targetFolder)
1631 );
1632 }
1633
1634 /**
1635 * Emits the file pre-move signal
1636 *
1637 * @param t3lib_file_FileInterface $file
1638 * @param t3lib_file_Folder $targetFolder
1639 * @return void
1640 */
1641 protected function emitPreFileMoveSignal(t3lib_file_FileInterface $file, t3lib_file_Folder $targetFolder) {
1642 $this->getSignalSlotDispatcher()->dispatch(
1643 't3lib_file_Storage',
1644 self::SIGNAL_PreFileMove,
1645 array($file, $targetFolder)
1646 );
1647 }
1648
1649 /**
1650 * Emits the file post-move signal
1651 *
1652 * @param t3lib_file_FileInterface $file
1653 * @param t3lib_file_Folder $targetFolder
1654 * @return void
1655 */
1656 protected function emitPostFileMoveSignal(t3lib_file_FileInterface $file, t3lib_file_Folder $targetFolder) {
1657 $this->getSignalSlotDispatcher()->dispatch(
1658 't3lib_file_Storage',
1659 self::SIGNAL_PostFileMove,
1660 array($file, $targetFolder)
1661 );
1662 }
1663
1664 /**
1665 * Emits the file pre-rename signal
1666 *
1667 * @param t3lib_file_FileInterface $file
1668 * @param $targetFolder
1669 * @return void
1670 */
1671 protected function emitPreFileRenameSignal(t3lib_file_FileInterface $file, $targetFolder) {
1672 $this->getSignalSlotDispatcher()->dispatch(
1673 't3lib_file_Storage',
1674 self::SIGNAL_PreFileRename,
1675 array($file, $targetFolder)
1676 );
1677 }
1678
1679 /**
1680 * Emits the file post-rename signal
1681 *
1682 * @param t3lib_file_FileInterface $file
1683 * @param $targetFolder
1684 * @return void
1685 */
1686 protected function emitPostFileRenameSignal(t3lib_file_FileInterface $file, $targetFolder) {
1687 $this->getSignalSlotDispatcher()->dispatch(
1688 't3lib_file_Storage',
1689 self::SIGNAL_PostFileRename,
1690 array($file, $targetFolder)
1691 );
1692 }
1693
1694 /**
1695 * Emits the file pre-replace signal
1696 *
1697 * @param t3lib_file_FileInterface $file
1698 * @param $localFilePath
1699 * @return void
1700 */
1701 protected function emitPreFileReplaceSignal(t3lib_file_FileInterface $file, $localFilePath) {
1702 $this->getSignalSlotDispatcher()->dispatch(
1703 't3lib_file_Storage',
1704 self::SIGNAL_PreFileReplace,
1705 array($file, $localFilePath)
1706 );
1707 }
1708
1709 /**
1710 * Emits the file post-replace signal
1711 *
1712 * @param t3lib_file_FileInterface $file
1713 * @param $localFilePath
1714 * @return void
1715 */
1716 protected function emitPostFileReplaceSignal(t3lib_file_FileInterface $file, $localFilePath) {
1717 $this->getSignalSlotDispatcher()->dispatch(
1718 't3lib_file_Storage',
1719 self::SIGNAL_PostFileReplace,
1720 array($file, $localFilePath)
1721 );
1722 }
1723
1724 /**
1725 * Emits the file pre-deletion signal
1726 *
1727 * @param t3lib_file_FileInterface $file
1728 * @return void
1729 */
1730 protected function emitPreFileDeleteSignal(t3lib_file_FileInterface $file) {
1731 $this->getSignalSlotDispatcher()->dispatch(
1732 't3lib_file_Storage',
1733 self::SIGNAL_PreFileDelete,
1734 array($file)
1735 );
1736 }
1737
1738 /**
1739 * Emits the file post-deletion signal
1740 *
1741 * @param t3lib_file_FileInterface $file
1742 * @return void
1743 */
1744 protected function emitPostFileDeleteSignal(t3lib_file_FileInterface $file) {
1745 $this->getSignalSlotDispatcher()->dispatch(
1746 't3lib_file_Storage',
1747 self::SIGNAL_PostFileDelete,
1748 array($file)
1749 );
1750 }
1751
1752 /**
1753 * Emits the folder pre-copy signal
1754 *
1755 * @param t3lib_File_Folder $folder
1756 * @param t3lib_file_Folder $targetFolder
1757 * @param $newName
1758 * @return void
1759 */
1760 protected function emitPreFolderCopySignal(t3lib_File_Folder $folder, t3lib_file_Folder $targetFolder, $newName) {
1761 $this->getSignalSlotDispatcher()->dispatch(
1762 't3lib_File_Storage',
1763 self::SIGNAL_PreFolderCopy,
1764 array($folder, $targetFolder)
1765 );
1766 }
1767
1768 /**
1769 * Emits the folder post-copy signal
1770 *
1771 * @param t3lib_File_Folder $folder
1772 * @param t3lib_file_Folder $targetFolder
1773 * @param $newName
1774 * @return void
1775 */
1776 protected function emitPostFolderCopySignal(t3lib_File_Folder $folder, t3lib_file_Folder $targetFolder, $newName) {
1777 $this->getSignalSlotDispatcher()->dispatch(
1778 't3lib_File_Storage',
1779 self::SIGNAL_PostFolderCopy,
1780 array($folder, $targetFolder)
1781 );
1782 }
1783
1784 /**
1785 * Emits the folder pre-move signal
1786 *
1787 * @param t3lib_File_Folder $folder
1788 * @param t3lib_file_Folder $targetFolder
1789 * @param $newName
1790 * @return void
1791 */
1792 protected function emitPreFolderMoveSignal(t3lib_File_Folder $folder, t3lib_file_Folder $targetFolder, $newName) {
1793 $this->getSignalSlotDispatcher()->dispatch(
1794 't3lib_File_Storage',
1795 self::SIGNAL_PreFolderMove,
1796 array($folder, $targetFolder)
1797 );
1798 }
1799
1800 /**
1801 * Emits the folder post-move signal
1802 *
1803 * @param t3lib_File_Folder $folder
1804 * @param t3lib_file_Folder $targetFolder
1805 * @param $newName
1806 * @return void
1807 */
1808 protected function emitPostFolderMoveSignal(t3lib_File_Folder $folder, t3lib_file_Folder $targetFolder, $newName) {
1809 $this->getSignalSlotDispatcher()->dispatch(
1810 't3lib_File_Storage',
1811 self::SIGNAL_PostFolderMove,
1812 array($folder, $targetFolder)
1813 );
1814 }
1815
1816 /**
1817 * Emits the folder pre-rename signal
1818 *
1819 * @param t3lib_File_Folder $folder
1820 * @param $newName
1821 * @return void
1822 */
1823 protected function emitPreFolderRenameSignal(t3lib_File_Folder $folder, $newName) {
1824 $this->getSignalSlotDispatcher()->dispatch(
1825 't3lib_File_Storage',
1826 self::SIGNAL_PreFolderRename,
1827 array($folder, $newName)
1828 );
1829 }
1830
1831 /**
1832 * Emits the folder post-rename signal
1833 *
1834 * @param t3lib_File_Folder $folder
1835 * @param $newName
1836 * @return void
1837 */
1838 protected function emitPostFolderRenameSignal(t3lib_File_Folder $folder, $newName) {
1839 $this->getSignalSlotDispatcher()->dispatch(
1840 't3lib_File_Storage',
1841 self::SIGNAL_PostFolderRename,
1842 array($folder, $newName)
1843 );
1844 }
1845
1846 /**
1847 * Emits the folder pre-deletion signal
1848 *
1849 * @param t3lib_File_Folder $folder
1850 * @return void
1851 */
1852 protected function emitPreFolderDeleteSignal(t3lib_File_Folder $folder) {
1853 $this->getSignalSlotDispatcher()->dispatch(
1854 't3lib_File_Storage',
1855 self::SIGNAL_PreFolderDelete,
1856 array($folder)
1857 );
1858 }
1859
1860 /**
1861 * Emits folder postdeletion signal.
1862 *
1863 * @param t3lib_File_Folder $folder
1864 * @return void
1865 */
1866 protected function emitPostFolderDeleteSignal(t3lib_File_Folder $folder) {
1867 $this->getSignalSlotDispatcher()->dispatch(
1868 't3lib_File_Storage',
1869 self::SIGNAL_PostFolderDelete,
1870 array($folder)
1871 );
1872 }
1873
1874
1875 /**
1876 * Emits file pre-processing signal.
1877 *
1878 * @param t3lib_file_ProcessedFile $processedFile
1879 * @param t3lib_file_FileInterface $file
1880 * @param string $context
1881 * @param array $configuration
1882 */
1883 protected function emitPreFileProcess(t3lib_file_ProcessedFile $processedFile, t3lib_file_FileInterface $file, $context, array $configuration = array()) {
1884 t3lib_SignalSlot_Dispatcher::getInstance()->dispatch(
1885 't3lib_file_Storage',
1886 self::SIGNAL_PreFileProcess,
1887 array($this, $this->driver, $processedFile, $file, $context, $configuration)
1888 );
1889 }
1890
1891 /**
1892 * Emits file post-processing signal.
1893 *
1894 * @param t3lib_file_ProcessedFile $processedFile
1895 * @param t3lib_file_FileInterface $file
1896 * @param $context
1897 * @param array $configuration
1898 */
1899 protected function emitPostFileProcess(t3lib_file_ProcessedFile $processedFile, t3lib_file_FileInterface $file, $context, array $configuration = array()) {
1900 t3lib_SignalSlot_Dispatcher::getInstance()->dispatch(
1901 't3lib_file_Storage',
1902 self::SIGNAL_PostFileProcess,
1903 array($this, $this->driver, $processedFile, $file, $context, $configuration)
1904 );
1905 }
1906
1907
1908
1909 /**
1910 * Returns the destination path/fileName of a unique fileName/foldername in that path.
1911 * If $theFile exists in $theDest (directory) the file have numbers appended up to $this->maxNumber. Hereafter a unique string will be appended.
1912 * This function is used by fx. TCEmain when files are attached to records and needs to be uniquely named in the uploads/* folders
1913 *
1914 * @param t3lib_file_Folder $folder
1915 * @param string $theFile The input fileName to check
1916 * @param boolean $dontCheckForUnique If set the fileName is returned with the path prepended without checking whether it already existed!
1917 * @return string A unique fileName inside $folder, based on $theFile.
1918 * @see t3lib_basicFileFunc::getUniqueName()
1919 */
1920 // TODO check if this should be moved back to t3lib_file_Folder
1921 protected function getUniqueName(t3lib_file_Folder $folder, $theFile, $dontCheckForUnique = FALSE) {
1922 static $maxNumber = 99, $uniqueNamePrefix = '';
1923
1924 $origFileInfo = t3lib_div::split_fileref($theFile); // Fetches info about path, name, extention of $theFile
1925 if ($uniqueNamePrefix) { // Adds prefix
1926 $origFileInfo['file'] = $uniqueNamePrefix . $origFileInfo['file'];
1927 $origFileInfo['filebody'] = $uniqueNamePrefix . $origFileInfo['filebody'];
1928 }
1929
1930 // Check if the file exists and if not - return the fileName...
1931 $fileInfo = $origFileInfo;
1932 $theDestFile = $fileInfo['file']; // The destinations file
1933 if (!$this->driver->fileExistsInFolder($theDestFile, $folder) || $dontCheckForUnique) { // If the file does NOT exist we return this fileName
1934 return $theDestFile;
1935 }
1936
1937 // Well the fileName in its pure form existed. Now we try to append numbers / unique-strings and see if we can find an available fileName...
1938 $theTempFileBody = preg_replace('/_[0-9][0-9]$/', '', $origFileInfo['filebody']); // This removes _xx if appended to the file
1939 $theOrigExt = $origFileInfo['realFileext'] ? '.' . $origFileInfo['realFileext'] : '';
1940
1941 for ($a = 1; $a <= ($maxNumber + 1); $a++) {
1942 if ($a <= $maxNumber) { // First we try to append numbers
1943 $insert = '_' . sprintf('%02d', $a);
1944 } else { // .. then we try unique-strings...
1945 $insert = '_' . substr(md5(uniqId('')), 0, 6); // TODO remove constant 6
1946 }
1947 $theTestFile = $theTempFileBody . $insert . $theOrigExt;
1948 $theDestFile = $theTestFile; // The destinations file
1949 if (!$this->driver->fileExistsInFolder($theDestFile, $folder)) { // If the file does NOT exist we return this fileName
1950 return $theDestFile;
1951 }
1952 }
1953
1954 throw new RuntimeException('Last possible name "' . $theDestFile . '" is already taken.', 1325194291);
1955 }
1956
1957 /**
1958 * @return t3lib_SignalSlot_Dispatcher
1959 */
1960 protected function getSignalSlotDispatcher() {
1961 if (!isset($this->signalSlotDispatcher)) {
1962 $this->signalSlotDispatcher = t3lib_div::makeInstance('t3lib_SignalSlot_Dispatcher');
1963 }
1964 return $this->signalSlotDispatcher;
1965 }
1966
1967 /**
1968 * @return t3lib_file_Factory
1969 */
1970 protected function getFileFactory() {
1971 return t3lib_div::makeInstance('t3lib_file_Factory');
1972 }
1973
1974 /**
1975 * @return t3lib_file_Service_FileProcessingService
1976 */
1977 protected function getFileProcessingService() {
1978 if (!$this->fileProcessingService) {
1979 $this->fileProcessingService = t3lib_div::makeInstance(
1980 't3lib_file_Service_FileProcessingService',
1981 $this,
1982 $this->driver
1983 );
1984 }
1985 return $this->fileProcessingService;
1986 }
1987
1988 /**
1989 * Getter function to return the folder where the files can
1990 * be processed. does not check for access rights here
1991 * @todo check if we need to implement "is writable" capability
1992 *
1993 * @return t3lib_file_Folder the processing folder, can be empty as well, if the storage doesn't have a processing folder
1994 */
1995 public function getProcessingFolder() {
1996 if (!isset($this->processingFolder)) {
1997 $processingFolder = self::DEFAULT_ProcessingFolder;
1998
1999 if (!empty($this->storageRecord['processingfolder'])) {
2000 $processingFolder = $this->storageRecord['processingfolder'];
2001 }
2002
2003 $processingFolder = trim($processingFolder, '/');
2004
2005 // @todo: does not resolve deeplinked folders like typo3temp/_processed_
2006 if ($this->driver->folderExists($processingFolder) === FALSE) {
2007 $this->processingFolder = $this->driver->createFolder(
2008 $processingFolder,
2009 $this->driver->getRootLevelFolder()
2010 );
2011 } else {
2012 $this->processingFolder = $this->driver->getFolder($processingFolder);
2013 }
2014 }
2015
2016 return $this->processingFolder;
2017 }
2018 }
2019
2020 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/file/Storage.php'])) {
2021 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/file/Storage.php']);
2022 }
2023
2024 ?>