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