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