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