TYPO3 CMS  TYPO3_7-6
ResourceStorage.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
22 
56 {
62  protected $driver;
63 
69  protected $storageRecord;
70 
76  protected $configuration;
77 
82 
91  protected $evaluatePermissions = false;
92 
98  protected $fileMounts = [];
99 
106  protected $userPermissions = [];
107 
114  protected $capabilities;
115 
120 
124  protected $processingFolder;
125 
132 
138  protected $isOnline = null;
139 
143  protected $isDefault = false;
144 
151 
156 
163  public function __construct(Driver\DriverInterface $driver, array $storageRecord)
164  {
165  $this->storageRecord = $storageRecord;
166  $this->configuration = ResourceFactory::getInstance()->convertFlexFormDataToConfigurationArray($storageRecord['configuration']);
167  $this->capabilities =
168  ($this->storageRecord['is_browsable'] ? self::CAPABILITY_BROWSABLE : 0) |
169  ($this->storageRecord['is_public'] ? self::CAPABILITY_PUBLIC : 0) |
170  ($this->storageRecord['is_writable'] ? self::CAPABILITY_WRITABLE : 0);
171 
172  $this->driver = $driver;
173  $this->driver->setStorageUid($storageRecord['uid']);
174  $this->driver->mergeConfigurationCapabilities($this->capabilities);
175  try {
176  $this->driver->processConfiguration();
177  } catch (Exception\InvalidConfigurationException $e) {
178  // Configuration error
179  $this->isOnline = false;
180 
181  $message = sprintf(
182  'Failed initializing storage [%d] "%s", error: %s',
183  $this->getUid(),
184  $this->getName(),
185  $e->getMessage()
186  );
187 
188  $this->getLogger()->error($message);
189  }
190  $this->driver->initialize();
191  $this->capabilities = $this->driver->getCapabilities();
192 
193  $this->isDefault = (isset($storageRecord['is_default']) && $storageRecord['is_default'] == 1);
195  }
196 
202  public function getConfiguration()
203  {
204  return $this->configuration;
205  }
206 
212  public function setConfiguration(array $configuration)
213  {
214  $this->configuration = $configuration;
215  }
216 
222  public function getStorageRecord()
223  {
224  return $this->storageRecord;
225  }
226 
233  public function setDriver(Driver\DriverInterface $driver)
234  {
235  $this->driver = $driver;
236  return $this;
237  }
238 
244  protected function getDriver()
245  {
246  return $this->driver;
247  }
248 
254  public function getName()
255  {
256  return $this->storageRecord['name'];
257  }
258 
264  public function getUid()
265  {
266  return (int)$this->storageRecord['uid'];
267  }
268 
274  public function hasChildren()
275  {
276  return true;
277  }
278 
279  /*********************************
280  * Capabilities
281  ********************************/
288  public function getCapabilities()
289  {
290  return (int)$this->capabilities;
291  }
292 
299  protected function hasCapability($capability)
300  {
301  return ($this->capabilities & $capability) == $capability;
302  }
303 
312  public function isPublic()
313  {
314  return $this->hasCapability(self::CAPABILITY_PUBLIC);
315  }
316 
323  public function isWritable()
324  {
325  return $this->hasCapability(self::CAPABILITY_WRITABLE);
326  }
327 
333  public function isBrowsable()
334  {
335  return $this->isOnline() && $this->hasCapability(self::CAPABILITY_BROWSABLE);
336  }
337 
344  {
345  return $this->driver->isCaseSensitiveFileSystem();
346  }
347 
353  public function isOnline()
354  {
355  if ($this->isOnline === null) {
356  if ($this->getUid() === 0) {
357  $this->isOnline = true;
358  }
359  // the storage is not marked as online for a longer time
360  if ($this->storageRecord['is_online'] == 0) {
361  $this->isOnline = false;
362  }
363  if ($this->isOnline !== false) {
364  // all files are ALWAYS available in the frontend
365  if (TYPO3_MODE === 'FE') {
366  $this->isOnline = true;
367  } else {
368  // check if the storage is disabled temporary for now
369  $registryObject = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Registry::class);
370  $offlineUntil = $registryObject->get('core', 'sys_file_storage-' . $this->getUid() . '-offline-until');
371  if ($offlineUntil && $offlineUntil > time()) {
372  $this->isOnline = false;
373  } else {
374  $this->isOnline = true;
375  }
376  }
377  }
378  }
379  return $this->isOnline;
380  }
381 
387  public function autoExtractMetadataEnabled()
388  {
389  return !empty($this->storageRecord['auto_extract_metadata']);
390  }
391 
401  public function markAsPermanentlyOffline()
402  {
403  if ($this->getUid() > 0) {
404  // @todo: move this to the storage repository
405  $GLOBALS['TYPO3_DB']->exec_UPDATEquery('sys_file_storage', 'uid=' . (int)$this->getUid(), ['is_online' => 0]);
406  }
407  $this->storageRecord['is_online'] = 0;
408  $this->isOnline = false;
409  }
410 
419  public function markAsTemporaryOffline()
420  {
421  $registryObject = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Registry::class);
422  $registryObject->set('core', 'sys_file_storage-' . $this->getUid() . '-offline-until', time() + 60 * 5);
423  $this->storageRecord['is_online'] = 0;
424  $this->isOnline = false;
425  }
426 
427  /*********************************
428  * User Permissions / File Mounts
429  ********************************/
440  public function addFileMount($folderIdentifier, $additionalData = [])
441  {
442  // check for the folder before we add it as a filemount
443  if ($this->driver->folderExists($folderIdentifier) === false) {
444  // if there is an error, this is important and should be handled
445  // as otherwise the user would see the whole storage without any restrictions for the filemounts
446  throw new Exception\FolderDoesNotExistException('Folder for file mount ' . $folderIdentifier . ' does not exist.', 1334427099);
447  }
448  $data = $this->driver->getFolderInfoByIdentifier($folderIdentifier);
449  $folderObject = ResourceFactory::getInstance()->createFolderObject($this, $data['identifier'], $data['name']);
450  // Use the canonical identifier instead of the user provided one!
451  $folderIdentifier = $folderObject->getIdentifier();
452  if (
453  !empty($this->fileMounts[$folderIdentifier])
454  && empty($this->fileMounts[$folderIdentifier]['read_only'])
455  && !empty($additionalData['read_only'])
456  ) {
457  // Do not overwrite a regular mount with a read only mount
458  return;
459  }
460  if (empty($additionalData)) {
461  $additionalData = [
462  'path' => $folderIdentifier,
463  'title' => $folderIdentifier,
464  'folder' => $folderObject
465  ];
466  } else {
467  $additionalData['folder'] = $folderObject;
468  if (!isset($additionalData['title'])) {
469  $additionalData['title'] = $folderIdentifier;
470  }
471  }
472  $this->fileMounts[$folderIdentifier] = $additionalData;
473  }
474 
480  public function getFileMounts()
481  {
482  return $this->fileMounts;
483  }
484 
493  public function isWithinFileMountBoundaries($subject, $checkWriteAccess = false)
494  {
495  if (!$this->evaluatePermissions) {
496  return true;
497  }
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->isWithinProcessingFolder($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  $writableFileMountAvailable = false;
511  foreach ($this->fileMounts as $fileMount) {
512  if ($this->driver->isWithin($fileMount['folder']->getIdentifier(), $identifier)) {
513  $isWithinFileMount = true;
514  if (!$checkWriteAccess) {
515  break;
516  } elseif (empty($fileMount['read_only'])) {
517  $writableFileMountAvailable = true;
518  break;
519  }
520  }
521  }
522  $isWithinFileMount = $checkWriteAccess ? $writableFileMountAvailable : $isWithinFileMount;
523  }
524  return $isWithinFileMount;
525  }
526 
534  {
535  $this->evaluatePermissions = (bool)$evaluatePermissions;
536  }
537 
544  public function getEvaluatePermissions()
545  {
547  }
548 
555  public function setUserPermissions(array $userPermissions)
556  {
557  $this->userPermissions = $userPermissions;
558  }
559 
568  public function checkUserActionPermission($action, $type)
569  {
570  if (!$this->evaluatePermissions) {
571  return true;
572  }
573 
574  $allow = false;
575  if (!empty($this->userPermissions[strtolower($action) . ucfirst(strtolower($type))])) {
576  $allow = true;
577  }
578 
579  return $allow;
580  }
581 
595  public function checkFileActionPermission($action, FileInterface $file)
596  {
597  $isProcessedFile = $file instanceof ProcessedFile;
598  // Check 1: Does the user have permission to perform the action? e.g. "readFile"
599  if (!$isProcessedFile && $this->checkUserActionPermission($action, 'File') === false) {
600  return false;
601  }
602  // Check 2: No action allowed on files for denied file extensions
603  if (!$this->checkFileExtensionPermission($file->getName())) {
604  return false;
605  }
606  $isReadCheck = false;
607  if (in_array($action, ['read', 'copy', 'move', 'replace'], true)) {
608  $isReadCheck = true;
609  }
610  $isWriteCheck = false;
611  if (in_array($action, ['add', 'write', 'move', 'rename', 'replace', 'unzip', 'delete'], true)) {
612  $isWriteCheck = true;
613  }
614  // Check 3: Does the user have the right to perform the action?
615  // (= is he within the file mount borders)
616  if (!$isProcessedFile && !$this->isWithinFileMountBoundaries($file, $isWriteCheck)) {
617  return false;
618  }
619 
620  $isMissing = false;
621  if (!$isProcessedFile && $file instanceof File) {
622  $isMissing = $file->isMissing();
623  }
624 
625  if ($this->driver->fileExists($file->getIdentifier()) === false) {
626  $file->setMissing(true);
627  $isMissing = true;
628  }
629 
630  // Check 4: Check the capabilities of the storage (and the driver)
631  if ($isWriteCheck && ($isMissing || !$this->isWritable())) {
632  return false;
633  }
634 
635  // Check 5: "File permissions" of the driver (only when file isn't marked as missing)
636  if (!$isMissing) {
637  $filePermissions = $this->driver->getPermissions($file->getIdentifier());
638  if ($isReadCheck && !$filePermissions['r']) {
639  return false;
640  }
641  if ($isWriteCheck && !$filePermissions['w']) {
642  return false;
643  }
644  }
645  return true;
646  }
647 
658  public function checkFolderActionPermission($action, Folder $folder = null)
659  {
660  // Check 1: Does the user have permission to perform the action? e.g. "writeFolder"
661  if ($this->checkUserActionPermission($action, 'Folder') === false) {
662  return false;
663  }
664 
665  // If we do not have a folder here, we cannot do further checks
666  if ($folder === null) {
667  return true;
668  }
669 
670  $isReadCheck = false;
671  if (in_array($action, ['read', 'copy'], true)) {
672  $isReadCheck = true;
673  }
674  $isWriteCheck = false;
675  if (in_array($action, ['add', 'move', 'write', 'delete', 'rename'], true)) {
676  $isWriteCheck = true;
677  }
678  // Check 2: Does the user has the right to perform the action?
679  // (= is he within the file mount borders)
680  if (!$this->isWithinFileMountBoundaries($folder, $isWriteCheck)) {
681  return false;
682  }
683  // Check 3: Check the capabilities of the storage (and the driver)
684  if ($isReadCheck && !$this->isBrowsable()) {
685  return false;
686  }
687  if ($isWriteCheck && !$this->isWritable()) {
688  return false;
689  }
690 
691  // Check 4: "Folder permissions" of the driver
692  $folderPermissions = $this->driver->getPermissions($folder->getIdentifier());
693  if ($isReadCheck && !$folderPermissions['r']) {
694  return false;
695  }
696  if ($isWriteCheck && !$folderPermissions['w']) {
697  return false;
698  }
699  return true;
700  }
701 
709  protected function checkFileExtensionPermission($fileName)
710  {
711  $fileName = $this->driver->sanitizeFileName($fileName);
712  $isAllowed = GeneralUtility::verifyFilenameAgainstDenyPattern($fileName);
713  if ($isAllowed && $this->evaluatePermissions) {
714  $fileExtension = strtolower(PathUtility::pathinfo($fileName, PATHINFO_EXTENSION));
715  // Set up the permissions for the file extension
716  $fileExtensionPermissions = $GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']['webspace'];
717  $fileExtensionPermissions['allow'] = GeneralUtility::uniqueList(strtolower($fileExtensionPermissions['allow']));
718  $fileExtensionPermissions['deny'] = GeneralUtility::uniqueList(strtolower($fileExtensionPermissions['deny']));
719  if ($fileExtension !== '') {
720  // If the extension is found amongst the allowed types, we return TRUE immediately
721  if ($fileExtensionPermissions['allow'] === '*' || GeneralUtility::inList($fileExtensionPermissions['allow'], $fileExtension)) {
722  return true;
723  }
724  // If the extension is found amongst the denied types, we return FALSE immediately
725  if ($fileExtensionPermissions['deny'] === '*' || GeneralUtility::inList($fileExtensionPermissions['deny'], $fileExtension)) {
726  return false;
727  }
728  // If no match we return TRUE
729  return true;
730  } else {
731  if ($fileExtensionPermissions['allow'] === '*') {
732  return true;
733  }
734  if ($fileExtensionPermissions['deny'] === '*') {
735  return false;
736  }
737  return true;
738  }
739  }
740  return $isAllowed;
741  }
742 
750  protected function assureFolderReadPermission(Folder $folder = null)
751  {
752  if (!$this->checkFolderActionPermission('read', $folder)) {
753  if ($folder === null) {
754  throw new Exception\InsufficientFolderAccessPermissionsException(
755  'You are not allowed to read folders',
756  1430657869
757  );
758  } else {
759  throw new Exception\InsufficientFolderAccessPermissionsException(
760  'You are not allowed to access the given folder: "' . $folder->getName() . '"',
761  1375955684
762  );
763  }
764  }
765  }
766 
777  protected function assureFolderDeletePermission(Folder $folder, $checkDeleteRecursively)
778  {
779  // Check user permissions for recursive deletion if it is requested
780  if ($checkDeleteRecursively && !$this->checkUserActionPermission('recursivedelete', 'Folder')) {
781  throw new Exception\InsufficientUserPermissionsException('You are not allowed to delete folders recursively', 1377779423);
782  }
783  // Check user action permission
784  if (!$this->checkFolderActionPermission('delete', $folder)) {
785  throw new Exception\InsufficientFolderAccessPermissionsException(
786  'You are not allowed to delete the given folder: "' . $folder->getName() . '"',
787  1377779039
788  );
789  }
790  // Check if the user has write permissions to folders
791  // Would be good if we could check for actual write permissions in the containig folder
792  // but we cannot since we have no access to the containing folder of this file.
793  if (!$this->checkUserActionPermission('write', 'Folder')) {
794  throw new Exception\InsufficientFolderWritePermissionsException('Writing to folders is not allowed.', 1377779111);
795  }
796  }
797 
806  protected function assureFileReadPermission(FileInterface $file)
807  {
808  if (!$this->checkFileActionPermission('read', $file)) {
809  throw new Exception\InsufficientFileAccessPermissionsException(
810  'You are not allowed to access that file: "' . $file->getName() . '"',
811  1375955429
812  );
813  }
814  if (!$this->checkFileExtensionPermission($file->getName())) {
815  throw new Exception\IllegalFileExtensionException(
816  'You are not allowed to use that file extension. File: "' . $file->getName() . '"',
817  1375955430
818  );
819  }
820  }
821 
831  protected function assureFileWritePermissions(FileInterface $file)
832  {
833  // Check if user is allowed to write the file and $file is writable
834  if (!$this->checkFileActionPermission('write', $file)) {
835  throw new Exception\InsufficientFileWritePermissionsException('Writing to file "' . $file->getIdentifier() . '" is not allowed.', 1330121088);
836  }
837  if (!$this->checkFileExtensionPermission($file->getName())) {
838  throw new Exception\IllegalFileExtensionException('You are not allowed to edit a file with extension "' . $file->getExtension() . '"', 1366711933);
839  }
840  }
841 
850  protected function assureFileReplacePermissions(FileInterface $file)
851  {
852  // Check if user is allowed to replace the file and $file is writable
853  if (!$this->checkFileActionPermission('replace', $file)) {
854  throw new Exception\InsufficientFileWritePermissionsException('Replacing file "' . $file->getIdentifier() . '" is not allowed.', 1436899571);
855  }
856  // Check if parentFolder is writable for the user
857  if (!$this->checkFolderActionPermission('write', $file->getParentFolder())) {
858  throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $file->getIdentifier() . '"', 1436899572);
859  }
860  }
861 
871  protected function assureFileDeletePermissions(FileInterface $file)
872  {
873  // Check for disallowed file extensions
874  if (!$this->checkFileExtensionPermission($file->getName())) {
875  throw new Exception\IllegalFileExtensionException('You are not allowed to delete a file with extension "' . $file->getExtension() . '"', 1377778916);
876  }
877  // Check further permissions if file is not a processed file
878  if (!$file instanceof ProcessedFile) {
879  // Check if user is allowed to delete the file and $file is writable
880  if (!$this->checkFileActionPermission('delete', $file)) {
881  throw new Exception\InsufficientFileWritePermissionsException('You are not allowed to delete the file "' . $file->getIdentifier() . '"', 1319550425);
882  }
883  // Check if the user has write permissions to folders
884  // Would be good if we could check for actual write permissions in the containig folder
885  // but we cannot since we have no access to the containing folder of this file.
886  if (!$this->checkUserActionPermission('write', 'Folder')) {
887  throw new Exception\InsufficientFolderWritePermissionsException('Writing to folders is not allowed.', 1377778702);
888  }
889  }
890  }
891 
904  protected function assureFileAddPermissions($targetFolder, $targetFileName)
905  {
906  // Check for a valid file extension
907  if (!$this->checkFileExtensionPermission($targetFileName)) {
908  throw new Exception\IllegalFileExtensionException('Extension of file name is not allowed in "' . $targetFileName . '"!', 1322120271);
909  }
910  // Makes sure the user is allowed to upload
911  if (!$this->checkUserActionPermission('add', 'File')) {
912  throw new Exception\InsufficientUserPermissionsException('You are not allowed to add files to this storage "' . $this->getUid() . '"', 1376992145);
913  }
914  // Check if targetFolder is writable
915  if (!$this->checkFolderActionPermission('write', $targetFolder)) {
916  throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $targetFolder->getIdentifier() . '"', 1322120356);
917  }
918  }
919 
936  protected function assureFileUploadPermissions($localFilePath, $targetFolder, $targetFileName, $uploadedFileSize)
937  {
938  // Makes sure this is an uploaded file
939  if (!is_uploaded_file($localFilePath)) {
940  throw new Exception\UploadException('The upload has failed, no uploaded file found!', 1322110455);
941  }
942  // Max upload size (kb) for files.
943  $maxUploadFileSize = GeneralUtility::getMaxUploadFileSize() * 1024;
944  if ($maxUploadFileSize > 0 && $uploadedFileSize >= $maxUploadFileSize) {
945  unlink($localFilePath);
946  throw new Exception\UploadSizeException('The uploaded file exceeds the size-limit of ' . $maxUploadFileSize . ' bytes', 1322110041);
947  }
948  $this->assureFileAddPermissions($targetFolder, $targetFileName);
949  }
950 
963  protected function assureFileMovePermissions(FileInterface $file, Folder $targetFolder, $targetFileName)
964  {
965  // Check if targetFolder is within this storage
966  if ($this->getUid() !== $targetFolder->getStorage()->getUid()) {
967  throw new \RuntimeException('The target folder is not in the same storage. Target folder given: "' . $targetFolder->getIdentifier() . '"', 1422553107);
968  }
969  // Check for a valid file extension
970  if (!$this->checkFileExtensionPermission($targetFileName)) {
971  throw new Exception\IllegalFileExtensionException('Extension of file name is not allowed in "' . $targetFileName . '"!', 1378243279);
972  }
973  // Check if user is allowed to move and $file is readable and writable
974  if (!$file->getStorage()->checkFileActionPermission('move', $file)) {
975  throw new Exception\InsufficientUserPermissionsException('You are not allowed to move files to storage "' . $this->getUid() . '"', 1319219349);
976  }
977  // Check if target folder is writable
978  if (!$this->checkFolderActionPermission('write', $targetFolder)) {
979  throw new Exception\InsufficientFolderAccessPermissionsException('You are not allowed to write to the target folder "' . $targetFolder->getIdentifier() . '"', 1319219350);
980  }
981  }
982 
994  protected function assureFileRenamePermissions(FileInterface $file, $targetFileName)
995  {
996  // Check if file extension is allowed
997  if (!$this->checkFileExtensionPermission($targetFileName) || !$this->checkFileExtensionPermission($file->getName())) {
998  throw new Exception\IllegalFileExtensionException('You are not allowed to rename a file with this extension. File given: "' . $file->getName() . '"', 1371466663);
999  }
1000  // Check if user is allowed to rename
1001  if (!$this->checkFileActionPermission('rename', $file)) {
1002  throw new Exception\InsufficientUserPermissionsException('You are not allowed to rename files. File given: "' . $file->getName() . '"', 1319219351);
1003  }
1004  // Check if the user is allowed to write to folders
1005  // Although it would be good to check, we cannot check here if the folder actually is writable
1006  // because we do not know in which folder the file resides.
1007  // So we rely on the driver to throw an exception in case the renaming failed.
1008  if (!$this->checkFolderActionPermission('write')) {
1009  throw new Exception\InsufficientFileWritePermissionsException('You are not allowed to write to folders', 1319219352);
1010  }
1011  }
1012 
1028  protected function assureFileCopyPermissions(FileInterface $file, Folder $targetFolder, $targetFileName)
1029  {
1030  // Check if targetFolder is within this storage, this should never happen
1031  if ($this->getUid() != $targetFolder->getStorage()->getUid()) {
1032  throw new Exception('The operation of the folder cannot be called by this storage "' . $this->getUid() . '"', 1319550405);
1033  }
1034  // Check if user is allowed to copy
1035  if (!$file->getStorage()->checkFileActionPermission('copy', $file)) {
1036  throw new Exception\InsufficientFileReadPermissionsException('You are not allowed to copy the file "' . $file->getIdentifier() . '"', 1319550426);
1037  }
1038  // Check if targetFolder is writable
1039  if (!$this->checkFolderActionPermission('write', $targetFolder)) {
1040  throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $targetFolder->getIdentifier() . '"', 1319550435);
1041  }
1042  // Check for a valid file extension
1043  if (!$this->checkFileExtensionPermission($targetFileName) || !$this->checkFileExtensionPermission($file->getName())) {
1044  throw new Exception\IllegalFileExtensionException('You are not allowed to copy a file of that type.', 1319553317);
1045  }
1046  }
1047 
1063  protected function assureFolderCopyPermissions(FolderInterface $folderToCopy, FolderInterface $targetParentFolder)
1064  {
1065  // Check if targetFolder is within this storage, this should never happen
1066  if ($this->getUid() !== $targetParentFolder->getStorage()->getUid()) {
1067  throw new Exception('The operation of the folder cannot be called by this storage "' . $this->getUid() . '"', 1377777624);
1068  }
1069  if (!$folderToCopy instanceof Folder) {
1070  throw new \RuntimeException('The folder "' . $folderToCopy->getIdentifier() . '" to copy is not of type folder.', 1384209020);
1071  }
1072  // Check if user is allowed to copy and the folder is readable
1073  if (!$folderToCopy->getStorage()->checkFolderActionPermission('copy', $folderToCopy)) {
1074  throw new Exception\InsufficientFileReadPermissionsException('You are not allowed to copy the folder "' . $folderToCopy->getIdentifier() . '"', 1377777629);
1075  }
1076  if (!$targetParentFolder instanceof Folder) {
1077  throw new \RuntimeException('The target folder "' . $targetParentFolder->getIdentifier() . '" is not of type folder.', 1384209021);
1078  }
1079  // Check if targetFolder is writable
1080  if (!$this->checkFolderActionPermission('write', $targetParentFolder)) {
1081  throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $targetParentFolder->getIdentifier() . '"', 1377777635);
1082  }
1083  }
1084 
1100  protected function assureFolderMovePermissions(FolderInterface $folderToMove, FolderInterface $targetParentFolder)
1101  {
1102  // Check if targetFolder is within this storage, this should never happen
1103  if ($this->getUid() !== $targetParentFolder->getStorage()->getUid()) {
1104  throw new \InvalidArgumentException('Cannot move a folder into a folder that does not belong to this storage.', 1325777289);
1105  }
1106  if (!$folderToMove instanceof Folder) {
1107  throw new \RuntimeException('The folder "' . $folderToMove->getIdentifier() . '" to move is not of type Folder.', 1384209022);
1108  }
1109  // Check if user is allowed to move and the folder is writable
1110  // In fact we would need to check if the parent folder of the folder to move is writable also
1111  // But as of now we cannot extract the parent folder from this folder
1112  if (!$folderToMove->getStorage()->checkFolderActionPermission('move', $folderToMove)) {
1113  throw new Exception\InsufficientFileReadPermissionsException('You are not allowed to copy the folder "' . $folderToMove->getIdentifier() . '"', 1377778045);
1114  }
1115  if (!$targetParentFolder instanceof Folder) {
1116  throw new \RuntimeException('The target folder "' . $targetParentFolder->getIdentifier() . '" is not of type Folder.', 1384209023);
1117  }
1118  // Check if targetFolder is writable
1119  if (!$this->checkFolderActionPermission('write', $targetParentFolder)) {
1120  throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $targetParentFolder->getIdentifier() . '"', 1377778049);
1121  }
1122  }
1123 
1134  public function sanitizeFileName($fileName, Folder $targetFolder = null)
1135  {
1136  $targetFolder = $targetFolder ?: $this->getDefaultFolder();
1137  $fileName = $this->driver->sanitizeFileName($fileName);
1138 
1139  // The file name could be changed by an external slot
1140  $fileName = $this->emitSanitizeFileNameSignal($fileName, $targetFolder);
1141 
1142  return $fileName;
1143  }
1144 
1145  /********************
1146  * FILE ACTIONS
1147  ********************/
1161  public function addFile($localFilePath, Folder $targetFolder, $targetFileName = '', $conflictMode = DuplicationBehavior::RENAME, $removeOriginal = true)
1162  {
1163  $localFilePath = PathUtility::getCanonicalPath($localFilePath);
1164  // File is not available locally NOR is it an uploaded file
1165  if (!is_uploaded_file($localFilePath) && !file_exists($localFilePath)) {
1166  throw new \InvalidArgumentException('File "' . $localFilePath . '" does not exist.', 1319552745);
1167  }
1168  $conflictMode = DuplicationBehavior::cast($conflictMode);
1169  $targetFolder = $targetFolder ?: $this->getDefaultFolder();
1170  $targetFileName = $this->sanitizeFileName($targetFileName ?: PathUtility::basename($localFilePath), $targetFolder);
1171 
1172  $targetFileName = $this->emitPreFileAddSignal($targetFileName, $targetFolder, $localFilePath);
1173 
1174  $this->assureFileAddPermissions($targetFolder, $targetFileName);
1175 
1176  $replaceExisting = false;
1177  if ($conflictMode->equals(DuplicationBehavior::CANCEL) && $this->driver->fileExistsInFolder($targetFileName, $targetFolder->getIdentifier())) {
1178  throw new Exception\ExistingTargetFileNameException('File "' . $targetFileName . '" already exists in folder ' . $targetFolder->getIdentifier(), 1322121068);
1179  } elseif ($conflictMode->equals(DuplicationBehavior::RENAME)) {
1180  $targetFileName = $this->getUniqueName($targetFolder, $targetFileName);
1181  } elseif ($conflictMode->equals(DuplicationBehavior::REPLACE) && $this->driver->fileExistsInFolder($targetFileName, $targetFolder->getIdentifier())) {
1182  $replaceExisting = true;
1183  }
1184 
1185  $fileIdentifier = $this->driver->addFile($localFilePath, $targetFolder->getIdentifier(), $targetFileName, $removeOriginal);
1186  $file = ResourceFactory::getInstance()->getFileObjectByStorageAndIdentifier($this->getUid(), $fileIdentifier);
1187 
1188  if ($replaceExisting && $file instanceof File) {
1189  $this->getIndexer()->updateIndexEntry($file);
1190  }
1191  if ($this->autoExtractMetadataEnabled()) {
1192  $this->getIndexer()->extractMetaData($file);
1193  }
1194 
1195  $this->emitPostFileAddSignal($file, $targetFolder);
1196 
1197  return $file;
1198  }
1199 
1210  public function updateProcessedFile($localFilePath, ProcessedFile $processedFile, Folder $processingFolder = null)
1211  {
1212  if (!file_exists($localFilePath)) {
1213  throw new \InvalidArgumentException('File "' . $localFilePath . '" does not exist.', 1319552746);
1214  }
1215  if ($processingFolder === null) {
1216  $processingFolder = $this->getProcessingFolder($processedFile->getOriginalFile());
1217  }
1218  $fileIdentifier = $this->driver->addFile($localFilePath, $processingFolder->getIdentifier(), $processedFile->getName());
1219  // @todo check if we have to update the processed file other then the identifier
1220  $processedFile->setIdentifier($fileIdentifier);
1221  return $processedFile;
1222  }
1223 
1231  public function hashFile(FileInterface $fileObject, $hash)
1232  {
1233  return $this->hashFileByIdentifier($fileObject->getIdentifier(), $hash);
1234  }
1235 
1244  public function hashFileByIdentifier($fileIdentifier, $hash)
1245  {
1246  return $this->driver->hash($fileIdentifier, $hash);
1247  }
1248 
1257  public function hashFileIdentifier($file)
1258  {
1259  if (is_object($file) && $file instanceof FileInterface) {
1261  $file = $file->getIdentifier();
1262  }
1263  return $this->driver->hashIdentifier($file);
1264  }
1265 
1276  public function getPublicUrl(ResourceInterface $resourceObject, $relativeToCurrentScript = false)
1277  {
1278  $publicUrl = null;
1279  if ($this->isOnline()) {
1280  // Pre-process the public URL by an accordant slot
1281  $this->emitPreGeneratePublicUrlSignal($resourceObject, $relativeToCurrentScript, ['publicUrl' => &$publicUrl]);
1282 
1283  if (
1284  $publicUrl === null
1285  && $resourceObject instanceof File
1286  && ($helper = OnlineMediaHelperRegistry::getInstance()->getOnlineMediaHelper($resourceObject)) !== false
1287  ) {
1288  $publicUrl = $helper->getPublicUrl($resourceObject, $relativeToCurrentScript);
1289  }
1290 
1291  // If slot did not handle the signal, use the default way to determine public URL
1292  if ($publicUrl === null) {
1293  if ($this->hasCapability(self::CAPABILITY_PUBLIC)) {
1294  $publicUrl = $this->driver->getPublicUrl($resourceObject->getIdentifier());
1295  }
1296 
1297  if ($publicUrl === null && $resourceObject instanceof FileInterface) {
1298  $queryParameterArray = ['eID' => 'dumpFile', 't' => ''];
1299  if ($resourceObject instanceof File) {
1300  $queryParameterArray['f'] = $resourceObject->getUid();
1301  $queryParameterArray['t'] = 'f';
1302  } elseif ($resourceObject instanceof ProcessedFile) {
1303  $queryParameterArray['p'] = $resourceObject->getUid();
1304  $queryParameterArray['t'] = 'p';
1305  }
1306 
1307  $queryParameterArray['token'] = GeneralUtility::hmac(implode('|', $queryParameterArray), 'resourceStorageDumpFile');
1308  $publicUrl = 'index.php?' . http_build_query($queryParameterArray, '', '&', PHP_QUERY_RFC3986);
1309  }
1310 
1311  // If requested, make the path relative to the current script in order to make it possible
1312  // to use the relative file
1313  if ($publicUrl !== null && $relativeToCurrentScript && !GeneralUtility::isValidUrl($publicUrl)) {
1314  $absolutePathToContainingFolder = PathUtility::dirname(PATH_site . $publicUrl);
1315  $pathPart = PathUtility::getRelativePathTo($absolutePathToContainingFolder);
1316  $filePart = substr(PATH_site . $publicUrl, strlen($absolutePathToContainingFolder) + 1);
1317  $publicUrl = $pathPart . $filePart;
1318  }
1319  }
1320  }
1321  return $publicUrl;
1322  }
1323 
1334  public function processFile(FileInterface $fileObject, $context, array $configuration)
1335  {
1336  if ($fileObject->getStorage() !== $this) {
1337  throw new \InvalidArgumentException('Cannot process files of foreign storage', 1353401835);
1338  }
1339  $processedFile = $this->getFileProcessingService()->processFile($fileObject, $this, $context, $configuration);
1340 
1341  return $processedFile;
1342  }
1343 
1351  public function getFileForLocalProcessing(FileInterface $fileObject, $writable = true)
1352  {
1353  $filePath = $this->driver->getFileForLocalProcessing($fileObject->getIdentifier(), $writable);
1354  return $filePath;
1355  }
1356 
1363  public function getFile($identifier)
1364  {
1365  $file = $this->getFileFactory()->getFileObjectByStorageAndIdentifier($this->getUid(), $identifier);
1366  if (!$this->driver->fileExists($identifier)) {
1367  $file->setMissing(true);
1368  }
1369  return $file;
1370  }
1371 
1379  public function getFileInfo(FileInterface $fileObject)
1380  {
1381  return $this->getFileInfoByIdentifier($fileObject->getIdentifier());
1382  }
1383 
1392  public function getFileInfoByIdentifier($identifier, array $propertiesToExtract = [])
1393  {
1394  return $this->driver->getFileInfoByIdentifier($identifier, $propertiesToExtract);
1395  }
1396 
1403  {
1404  $this->fileAndFolderNameFilters = [];
1405  }
1406 
1413  {
1414  $this->fileAndFolderNameFilters = $GLOBALS['TYPO3_CONF_VARS']['SYS']['fal']['defaultFilterCallbacks'];
1415  }
1416 
1423  {
1425  }
1426 
1431  public function setFileAndFolderNameFilters(array $filters)
1432  {
1433  $this->fileAndFolderNameFilters = $filters;
1434  return $this;
1435  }
1436 
1440  public function addFileAndFolderNameFilter($filter)
1441  {
1442  $this->fileAndFolderNameFilters[] = $filter;
1443  }
1444 
1450  public function getFolderIdentifierFromFileIdentifier($fileIdentifier)
1451  {
1452  return $this->driver->getParentFolderIdentifierOfIdentifier($fileIdentifier);
1453  }
1454 
1462  public function getFileInFolder($fileName, Folder $folder)
1463  {
1464  $identifier = $this->driver->getFileInFolder($fileName, $folder->getIdentifier());
1465  return $this->getFileFactory()->getFileObjectByStorageAndIdentifier($this->getUid(), $identifier);
1466  }
1467 
1483  public function getFilesInFolder(Folder $folder, $start = 0, $maxNumberOfItems = 0, $useFilters = true, $recursive = false, $sort = '', $sortRev = false)
1484  {
1485  $this->assureFolderReadPermission($folder);
1486 
1487  $rows = $this->getFileIndexRepository()->findByFolder($folder);
1488 
1489  $filters = $useFilters == true ? $this->fileAndFolderNameFilters : [];
1490  $fileIdentifiers = array_values($this->driver->getFilesInFolder($folder->getIdentifier(), $start, $maxNumberOfItems, $recursive, $filters, $sort, $sortRev));
1491 
1492  $items = [];
1493  foreach ($fileIdentifiers as $identifier) {
1494  if (isset($rows[$identifier])) {
1495  $fileObject = $this->getFileFactory()->getFileObject($rows[$identifier]['uid'], $rows[$identifier]);
1496  } else {
1497  $fileObject = $this->getFileFactory()->getFileObjectByStorageAndIdentifier($this->getUid(), $identifier);
1498  }
1499  if ($fileObject instanceof FileInterface) {
1500  $key = $fileObject->getName();
1501  while (isset($items[$key])) {
1502  $key .= 'z';
1503  }
1504  $items[$key] = $fileObject;
1505  }
1506  }
1507 
1508  return $items;
1509  }
1510 
1517  public function getFileIdentifiersInFolder($folderIdentifier, $useFilters = true, $recursive = false)
1518  {
1519  $filters = $useFilters == true ? $this->fileAndFolderNameFilters : [];
1520  return $this->driver->getFilesInFolder($folderIdentifier, 0, 0, $recursive, $filters);
1521  }
1522 
1530  public function countFilesInFolder(Folder $folder, $useFilters = true, $recursive = false)
1531  {
1532  $this->assureFolderReadPermission($folder);
1533  $filters = $useFilters ? $this->fileAndFolderNameFilters : [];
1534  return $this->driver->countFilesInFolder($folder->getIdentifier(), $recursive, $filters);
1535  }
1536 
1543  public function getFolderIdentifiersInFolder($folderIdentifier, $useFilters = true, $recursive = false)
1544  {
1545  $filters = $useFilters == true ? $this->fileAndFolderNameFilters : [];
1546  return $this->driver->getFoldersInFolder($folderIdentifier, 0, 0, $recursive, $filters);
1547  }
1548 
1555  public function hasFile($identifier)
1556  {
1557  // Allow if identifier is in processing folder
1558  if (!$this->isWithinProcessingFolder($identifier)) {
1559  $this->assureFolderReadPermission();
1560  }
1561  return $this->driver->fileExists($identifier);
1562  }
1563 
1569  public function getProcessingFolders()
1570  {
1571  if ($this->processingFolders === null) {
1572  $this->processingFolders = [];
1573  $this->processingFolders[] = $this->getProcessingFolder();
1575  $storageRepository = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\StorageRepository::class);
1576  $allStorages = $storageRepository->findAll();
1577  foreach ($allStorages as $storage) {
1578  // To circumvent the permission check of the folder, we use the factory to create it "manually" instead of directly using $storage->getProcessingFolder()
1579  // See #66695 for details
1580  list($storageUid, $processingFolderIdentifier) = GeneralUtility::trimExplode(':', $storage->getStorageRecord()['processingfolder']);
1581  if (empty($processingFolderIdentifier) || (int)$storageUid !== $this->getUid()) {
1582  continue;
1583  }
1584  $potentialProcessingFolder = ResourceFactory::getInstance()->getInstance()->createFolderObject($this, $processingFolderIdentifier, $processingFolderIdentifier);
1585  if ($potentialProcessingFolder->getStorage() === $this && $potentialProcessingFolder->getIdentifier() !== $this->getProcessingFolder()->getIdentifier()) {
1586  $this->processingFolders[] = $potentialProcessingFolder;
1587  }
1588  }
1589  }
1590 
1591  return $this->processingFolders;
1592  }
1593 
1601  public function isProcessingFolder(Folder $folder)
1602  {
1603  $isProcessingFolder = false;
1604  foreach ($this->getProcessingFolders() as $processingFolder) {
1605  if ($folder->getCombinedIdentifier() === $processingFolder->getCombinedIdentifier()) {
1606  $isProcessingFolder = true;
1607  break;
1608  }
1609  }
1610  return $isProcessingFolder;
1611  }
1612 
1620  public function hasFileInFolder($fileName, Folder $folder)
1621  {
1622  $this->assureFolderReadPermission($folder);
1623  return $this->driver->fileExistsInFolder($fileName, $folder->getIdentifier());
1624  }
1625 
1634  public function getFileContents($file)
1635  {
1636  $this->assureFileReadPermission($file);
1637  return $this->driver->getFileContents($file->getIdentifier());
1638  }
1639 
1650  public function dumpFileContents(FileInterface $file, $asDownload = false, $alternativeFilename = null, $overrideMimeType = null)
1651  {
1652  $downloadName = $alternativeFilename ?: $file->getName();
1653  $contentDisposition = $asDownload ? 'attachment' : 'inline';
1654  header('Content-Disposition: ' . $contentDisposition . '; filename="' . $downloadName . '"');
1655  header('Content-Type: ' . ($overrideMimeType ?: $file->getMimeType()));
1656  header('Content-Length: ' . $file->getSize());
1657 
1658  // Cache-Control header is needed here to solve an issue with browser IE8 and lower
1659  // See for more information: http://support.microsoft.com/kb/323308
1660  header("Cache-Control: ''");
1661  header('Last-Modified: ' .
1662  gmdate('D, d M Y H:i:s', array_pop($this->driver->getFileInfoByIdentifier($file->getIdentifier(), ['mtime']))) . ' GMT',
1663  true,
1664  200
1665  );
1666  ob_clean();
1667  flush();
1668  while (ob_get_level() > 0) {
1669  ob_end_clean();
1670  }
1671  $this->driver->dumpFileContents($file->getIdentifier());
1672  }
1673 
1685  public function setFileContents(AbstractFile $file, $contents)
1686  {
1687  // Check if user is allowed to edit
1688  $this->assureFileWritePermissions($file);
1689  // Call driver method to update the file and update file index entry afterwards
1690  $result = $this->driver->setFileContents($file->getIdentifier(), $contents);
1691  $this->getIndexer()->updateIndexEntry($file);
1692  $this->emitPostFileSetContentsSignal($file, $contents);
1693  return $result;
1694  }
1695 
1708  public function createFile($fileName, Folder $targetFolderObject)
1709  {
1710  $this->assureFileAddPermissions($targetFolderObject, $fileName);
1711  $newFileIdentifier = $this->driver->createFile($fileName, $targetFolderObject->getIdentifier());
1712  $this->emitPostFileCreateSignal($newFileIdentifier, $targetFolderObject);
1713  return ResourceFactory::getInstance()->getFileObjectByStorageAndIdentifier($this->getUid(), $newFileIdentifier);
1714  }
1715 
1724  public function deleteFile($fileObject)
1725  {
1726  $this->assureFileDeletePermissions($fileObject);
1727 
1728  $this->emitPreFileDeleteSignal($fileObject);
1729 
1730  if ($this->driver->fileExists($fileObject->getIdentifier())) {
1731  $result = $this->driver->deleteFile($fileObject->getIdentifier());
1732  if (!$result) {
1733  throw new Exception\FileOperationErrorException('Deleting the file "' . $fileObject->getIdentifier() . '\' failed.', 1329831691);
1734  }
1735  }
1736  // Mark the file object as deleted
1737  if ($fileObject instanceof AbstractFile) {
1738  $fileObject->setDeleted();
1739  }
1740 
1741  $this->emitPostFileDeleteSignal($fileObject);
1742 
1743  return true;
1744  }
1745 
1760  public function copyFile(FileInterface $file, Folder $targetFolder, $targetFileName = null, $conflictMode = DuplicationBehavior::RENAME)
1761  {
1762  $conflictMode = DuplicationBehavior::cast($conflictMode);
1763  if ($targetFileName === null) {
1764  $targetFileName = $file->getName();
1765  }
1766  $sanitizedTargetFileName = $this->driver->sanitizeFileName($targetFileName);
1767  $this->assureFileCopyPermissions($file, $targetFolder, $sanitizedTargetFileName);
1768  $this->emitPreFileCopySignal($file, $targetFolder);
1769  // File exists and we should abort, let's abort
1770  if ($conflictMode->equals(DuplicationBehavior::CANCEL) && $targetFolder->hasFile($sanitizedTargetFileName)) {
1771  throw new Exception\ExistingTargetFileNameException('The target file already exists.', 1320291064);
1772  }
1773  // File exists and we should find another name, let's find another one
1774  if ($conflictMode->equals(DuplicationBehavior::RENAME) && $targetFolder->hasFile($sanitizedTargetFileName)) {
1775  $sanitizedTargetFileName = $this->getUniqueName($targetFolder, $sanitizedTargetFileName);
1776  }
1777  $sourceStorage = $file->getStorage();
1778  // Call driver method to create a new file from an existing file object,
1779  // and return the new file object
1780  if ($sourceStorage === $this) {
1781  $newFileObjectIdentifier = $this->driver->copyFileWithinStorage($file->getIdentifier(), $targetFolder->getIdentifier(), $sanitizedTargetFileName);
1782  } else {
1783  $tempPath = $file->getForLocalProcessing();
1784  $newFileObjectIdentifier = $this->driver->addFile($tempPath, $targetFolder->getIdentifier(), $sanitizedTargetFileName);
1785  }
1786  $newFileObject = ResourceFactory::getInstance()->getFileObjectByStorageAndIdentifier($this->getUid(), $newFileObjectIdentifier);
1787  $this->emitPostFileCopySignal($file, $targetFolder);
1788  return $newFileObject;
1789  }
1790 
1806  public function moveFile($file, $targetFolder, $targetFileName = null, $conflictMode = DuplicationBehavior::RENAME)
1807  {
1808  $conflictMode = DuplicationBehavior::cast($conflictMode);
1809  if ($targetFileName === null) {
1810  $targetFileName = $file->getName();
1811  }
1812  $originalFolder = $file->getParentFolder();
1813  $sanitizedTargetFileName = $this->driver->sanitizeFileName($targetFileName);
1814  $this->assureFileMovePermissions($file, $targetFolder, $sanitizedTargetFileName);
1815  if ($targetFolder->hasFile($sanitizedTargetFileName)) {
1816  // File exists and we should abort, let's abort
1817  if ($conflictMode->equals(DuplicationBehavior::RENAME)) {
1818  $sanitizedTargetFileName = $this->getUniqueName($targetFolder, $sanitizedTargetFileName);
1819  } elseif ($conflictMode->equals(DuplicationBehavior::CANCEL)) {
1820  throw new Exception\ExistingTargetFileNameException('The target file already exists', 1329850997);
1821  }
1822  }
1823  $this->emitPreFileMoveSignal($file, $targetFolder);
1824  $sourceStorage = $file->getStorage();
1825  // Call driver method to move the file and update the index entry
1826  try {
1827  if ($sourceStorage === $this) {
1828  $newIdentifier = $this->driver->moveFileWithinStorage($file->getIdentifier(), $targetFolder->getIdentifier(), $sanitizedTargetFileName);
1829  if (!$file instanceof AbstractFile) {
1830  throw new \RuntimeException('The given file is not of type AbstractFile.', 1384209025);
1831  }
1832  $file->updateProperties(['identifier' => $newIdentifier]);
1833  } else {
1834  $tempPath = $file->getForLocalProcessing();
1835  $newIdentifier = $this->driver->addFile($tempPath, $targetFolder->getIdentifier(), $sanitizedTargetFileName);
1836  $sourceStorage->driver->deleteFile($file->getIdentifier());
1837  if ($file instanceof File) {
1838  $file->updateProperties(['storage' => $this->getUid(), 'identifier' => $newIdentifier]);
1839  }
1840  }
1841  $this->getIndexer()->updateIndexEntry($file);
1842  } catch (\TYPO3\CMS\Core\Exception $e) {
1843  echo $e->getMessage();
1844  }
1845  $this->emitPostFileMoveSignal($file, $targetFolder, $originalFolder);
1846  return $file;
1847  }
1848 
1860  public function renameFile($file, $targetFileName)
1861  {
1862  // @todo add $conflictMode setting
1863 
1864  // The name should be different from the current.
1865  if ($file->getName() === $targetFileName) {
1866  return $file;
1867  }
1868  $sanitizedTargetFileName = $this->driver->sanitizeFileName($targetFileName);
1869  $this->assureFileRenamePermissions($file, $sanitizedTargetFileName);
1870  $this->emitPreFileRenameSignal($file, $sanitizedTargetFileName);
1871 
1872  // Call driver method to rename the file and update the index entry
1873  try {
1874  $newIdentifier = $this->driver->renameFile($file->getIdentifier(), $sanitizedTargetFileName);
1875  if ($file instanceof File) {
1876  $file->updateProperties(['identifier' => $newIdentifier]);
1877  }
1878  $this->getIndexer()->updateIndexEntry($file);
1879  } catch (\RuntimeException $e) {
1880  }
1881 
1882  $this->emitPostFileRenameSignal($file, $sanitizedTargetFileName);
1883 
1884  return $file;
1885  }
1886 
1898  public function replaceFile(FileInterface $file, $localFilePath)
1899  {
1900  $this->assureFileReplacePermissions($file);
1901  if (!file_exists($localFilePath)) {
1902  throw new \InvalidArgumentException('File "' . $localFilePath . '" does not exist.', 1325842622);
1903  }
1904  $this->emitPreFileReplaceSignal($file, $localFilePath);
1905  $this->driver->replaceFile($file->getIdentifier(), $localFilePath);
1906  if ($file instanceof File) {
1907  $this->getIndexer()->updateIndexEntry($file);
1908  }
1909  if ($this->autoExtractMetadataEnabled()) {
1910  $this->getIndexer()->extractMetaData($file);
1911  }
1912  $this->emitPostFileReplaceSignal($file, $localFilePath);
1913 
1914  return $file;
1915  }
1916 
1926  public function addUploadedFile(array $uploadedFileData, Folder $targetFolder = null, $targetFileName = null, $conflictMode = DuplicationBehavior::CANCEL)
1927  {
1928  $conflictMode = DuplicationBehavior::cast($conflictMode);
1929  $localFilePath = $uploadedFileData['tmp_name'];
1930  if ($targetFolder === null) {
1931  $targetFolder = $this->getDefaultFolder();
1932  }
1933  if ($targetFileName === null) {
1934  $targetFileName = $uploadedFileData['name'];
1935  }
1936  $targetFileName = $this->driver->sanitizeFileName($targetFileName);
1937 
1938  $this->assureFileUploadPermissions($localFilePath, $targetFolder, $targetFileName, $uploadedFileData['size']);
1939  if ($this->hasFileInFolder($targetFileName, $targetFolder) && $conflictMode->equals(DuplicationBehavior::REPLACE)) {
1940  $file = $this->getFileInFolder($targetFileName, $targetFolder);
1941  $resultObject = $this->replaceFile($file, $localFilePath);
1942  } else {
1943  $resultObject = $this->addFile($localFilePath, $targetFolder, $targetFileName, (string)$conflictMode);
1944  }
1945  return $resultObject;
1946  }
1947 
1948  /********************
1949  * FOLDER ACTIONS
1950  ********************/
1957  protected function getAllFileObjectsInFolder(Folder $folder)
1958  {
1959  $files = [];
1960  $folderQueue = [$folder];
1961  while (!empty($folderQueue)) {
1962  $folder = array_shift($folderQueue);
1963  foreach ($folder->getSubfolders() as $subfolder) {
1964  $folderQueue[] = $subfolder;
1965  }
1966  foreach ($folder->getFiles() as $file) {
1968  $files[$file->getIdentifier()] = $file;
1969  }
1970  }
1971 
1972  return $files;
1973  }
1974 
1989  public function moveFolder(Folder $folderToMove, Folder $targetParentFolder, $newFolderName = null, $conflictMode = DuplicationBehavior::RENAME)
1990  {
1991  // @todo add tests
1992  $originalFolder = $folderToMove->getParentFolder();
1993  $this->assureFolderMovePermissions($folderToMove, $targetParentFolder);
1994  $sourceStorage = $folderToMove->getStorage();
1995  $returnObject = null;
1996  $sanitizedNewFolderName = $this->driver->sanitizeFileName($newFolderName ?: $folderToMove->getName());
1997  // @todo check if folder already exists in $targetParentFolder, handle this conflict then
1998  $this->emitPreFolderMoveSignal($folderToMove, $targetParentFolder, $sanitizedNewFolderName);
1999  // Get all file objects now so we are able to update them after moving the folder
2000  $fileObjects = $this->getAllFileObjectsInFolder($folderToMove);
2001  if ($sourceStorage === $this) {
2002  if ($this->isWithinFolder($folderToMove, $targetParentFolder)) {
2003  throw new InvalidTargetFolderException(
2004  sprintf(
2005  'Cannot move folder "%s" into target folder "%s", because the target folder is already within the folder to be moved!',
2006  $folderToMove->getName(),
2007  $targetParentFolder->getName()
2008  ),
2009  1422723050
2010  );
2011  }
2012  $fileMappings = $this->driver->moveFolderWithinStorage($folderToMove->getIdentifier(), $targetParentFolder->getIdentifier(), $sanitizedNewFolderName);
2013  } else {
2014  $fileMappings = $this->moveFolderBetweenStorages($folderToMove, $targetParentFolder, $sanitizedNewFolderName);
2015  }
2016  // Update the identifier and storage of all file objects
2017  foreach ($fileObjects as $oldIdentifier => $fileObject) {
2018  $newIdentifier = $fileMappings[$oldIdentifier];
2019  $fileObject->updateProperties(['storage' => $this->getUid(), 'identifier' => $newIdentifier]);
2020  $this->getIndexer()->updateIndexEntry($fileObject);
2021  }
2022  $returnObject = $this->getFolder($fileMappings[$folderToMove->getIdentifier()]);
2023  $this->emitPostFolderMoveSignal($folderToMove, $targetParentFolder, $returnObject->getName(), $originalFolder);
2024  return $returnObject;
2025  }
2026 
2037  protected function moveFolderBetweenStorages(Folder $folderToMove, Folder $targetParentFolder, $newFolderName)
2038  {
2039  throw new \RuntimeException('Not yet implemented');
2040  }
2041 
2052  public function copyFolder(FolderInterface $folderToCopy, FolderInterface $targetParentFolder, $newFolderName = null, $conflictMode = DuplicationBehavior::RENAME)
2053  {
2054  // @todo implement the $conflictMode handling
2055  $this->assureFolderCopyPermissions($folderToCopy, $targetParentFolder);
2056  $returnObject = null;
2057  $sanitizedNewFolderName = $this->driver->sanitizeFileName($newFolderName ?: $folderToCopy->getName());
2058  if ($folderToCopy instanceof Folder && $targetParentFolder instanceof Folder) {
2059  $this->emitPreFolderCopySignal($folderToCopy, $targetParentFolder, $sanitizedNewFolderName);
2060  }
2061  $sourceStorage = $folderToCopy->getStorage();
2062  // call driver method to move the file
2063  // that also updates the file object properties
2064  if ($sourceStorage === $this) {
2065  if ($this->isWithinFolder($folderToCopy, $targetParentFolder)) {
2066  throw new InvalidTargetFolderException(
2067  sprintf(
2068  'Cannot copy folder "%s" into target folder "%s", because the target folder is already within the folder to be copied!',
2069  $folderToCopy->getName(),
2070  $targetParentFolder->getName()
2071  ),
2072  1422723059
2073  );
2074  }
2075  $this->driver->copyFolderWithinStorage($folderToCopy->getIdentifier(), $targetParentFolder->getIdentifier(), $sanitizedNewFolderName);
2076  $returnObject = $this->getFolder($targetParentFolder->getSubfolder($sanitizedNewFolderName)->getIdentifier());
2077  } else {
2078  $this->copyFolderBetweenStorages($folderToCopy, $targetParentFolder, $sanitizedNewFolderName);
2079  }
2080  $this->emitPostFolderCopySignal($folderToCopy, $targetParentFolder, $returnObject->getName());
2081  return $returnObject;
2082  }
2083 
2094  protected function copyFolderBetweenStorages(Folder $folderToCopy, Folder $targetParentFolder, $newFolderName)
2095  {
2096  throw new \RuntimeException('Not yet implemented.');
2097  }
2098 
2108  public function renameFolder($folderObject, $newName)
2109  {
2110 
2111  // Renaming the folder should check if the parent folder is writable
2112  // We cannot do this however because we cannot extract the parent folder from a folder currently
2113  if (!$this->checkFolderActionPermission('rename', $folderObject)) {
2114  throw new Exception\InsufficientUserPermissionsException('You are not allowed to rename the folder "' . $folderObject->getIdentifier() . '\'', 1357811441);
2115  }
2116 
2117  $sanitizedNewName = $this->driver->sanitizeFileName($newName);
2118  $returnObject = null;
2119  if ($this->driver->folderExistsInFolder($sanitizedNewName, $folderObject->getIdentifier())) {
2120  throw new \InvalidArgumentException('The folder ' . $sanitizedNewName . ' already exists in folder ' . $folderObject->getIdentifier(), 1325418870);
2121  }
2122 
2123  $this->emitPreFolderRenameSignal($folderObject, $sanitizedNewName);
2124 
2125  $fileObjects = $this->getAllFileObjectsInFolder($folderObject);
2126  $fileMappings = $this->driver->renameFolder($folderObject->getIdentifier(), $sanitizedNewName);
2127  // Update the identifier of all file objects
2128  foreach ($fileObjects as $oldIdentifier => $fileObject) {
2129  $newIdentifier = $fileMappings[$oldIdentifier];
2130  $fileObject->updateProperties(['identifier' => $newIdentifier]);
2131  $this->getIndexer()->updateIndexEntry($fileObject);
2132  }
2133  $returnObject = $this->getFolder($fileMappings[$folderObject->getIdentifier()]);
2134 
2135  $this->emitPostFolderRenameSignal($folderObject, $returnObject->getName());
2136 
2137  return $returnObject;
2138  }
2139 
2152  public function deleteFolder($folderObject, $deleteRecursively = false)
2153  {
2154  $isEmpty = $this->driver->isFolderEmpty($folderObject->getIdentifier());
2155  $this->assureFolderDeletePermission($folderObject, ($deleteRecursively && !$isEmpty));
2156  if (!$isEmpty && !$deleteRecursively) {
2157  throw new \RuntimeException('Could not delete folder "' . $folderObject->getIdentifier() . '" because it is not empty.', 1325952534);
2158  }
2159 
2160  $this->emitPreFolderDeleteSignal($folderObject);
2161 
2162  foreach ($this->getFilesInFolder($folderObject, 0, 0, false, $deleteRecursively) as $file) {
2163  $this->deleteFile($file);
2164  }
2165 
2166  $result = $this->driver->deleteFolder($folderObject->getIdentifier(), $deleteRecursively);
2167 
2168  $this->emitPostFolderDeleteSignal($folderObject);
2169 
2170  return $result;
2171  }
2172 
2183  public function getFolderInFolder($folderName, Folder $parentFolder, $returnInaccessibleFolderObject = false)
2184  {
2185  $folderIdentifier = $this->driver->getFolderInFolder($folderName, $parentFolder->getIdentifier());
2186  return $this->getFolder($folderIdentifier, $returnInaccessibleFolderObject);
2187  }
2188 
2203  public function getFoldersInFolder(Folder $folder, $start = 0, $maxNumberOfItems = 0, $useFilters = true, $recursive = false, $sort = '', $sortRev = false)
2204  {
2205  $filters = $useFilters == true ? $this->fileAndFolderNameFilters : [];
2206 
2207  $folderIdentifiers = $this->driver->getFoldersInFolder($folder->getIdentifier(), $start, $maxNumberOfItems, $recursive, $filters, $sort, $sortRev);
2208 
2209  // Exclude processing folders
2210  foreach ($this->getProcessingFolders() as $processingFolder) {
2211  $processingIdentifier = $processingFolder->getIdentifier();
2212  if (isset($folderIdentifiers[$processingIdentifier])) {
2213  unset($folderIdentifiers[$processingIdentifier]);
2214  }
2215  }
2216  $folders = [];
2217  foreach ($folderIdentifiers as $folderIdentifier) {
2218  $folders[$folderIdentifier] = $this->getFolder($folderIdentifier, true);
2219  }
2220  return $folders;
2221  }
2222 
2230  public function countFoldersInFolder(Folder $folder, $useFilters = true, $recursive = false)
2231  {
2232  $this->assureFolderReadPermission($folder);
2233  $filters = $useFilters ? $this->fileAndFolderNameFilters : [];
2234  return $this->driver->countFoldersInFolder($folder->getIdentifier(), $recursive, $filters);
2235  }
2236 
2243  public function hasFolder($identifier)
2244  {
2245  $this->assureFolderReadPermission();
2246  return $this->driver->folderExists($identifier);
2247  }
2248 
2256  public function hasFolderInFolder($folderName, Folder $folder)
2257  {
2258  $this->assureFolderReadPermission($folder);
2259  return $this->driver->folderExistsInFolder($folderName, $folder->getIdentifier());
2260  }
2261 
2275  public function createFolder($folderName, Folder $parentFolder = null)
2276  {
2277  if ($parentFolder === null) {
2278  $parentFolder = $this->getRootLevelFolder();
2279  } elseif (!$this->driver->folderExists($parentFolder->getIdentifier())) {
2280  throw new \InvalidArgumentException('Parent folder "' . $parentFolder->getIdentifier() . '" does not exist.', 1325689164);
2281  }
2282  if (!$this->checkFolderActionPermission('add', $parentFolder)) {
2283  throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to create directories in the folder "' . $parentFolder->getIdentifier() . '"', 1323059807);
2284  }
2285  if ($this->driver->folderExistsInFolder($folderName, $parentFolder->getIdentifier())) {
2286  throw new Exception\ExistingTargetFolderException('Folder "' . $folderName . '" already exists.', 1423347324);
2287  }
2288 
2289  $this->emitPreFolderAddSignal($parentFolder, $folderName);
2290 
2291  $newFolder = $this->getDriver()->createFolder($folderName, $parentFolder->getIdentifier(), true);
2292  $newFolder = $this->getFolder($newFolder);
2293 
2294  $this->emitPostFolderAddSignal($newFolder);
2295 
2296  return $newFolder;
2297  }
2298 
2304  public function getDefaultFolder()
2305  {
2306  return $this->getFolder($this->driver->getDefaultFolder());
2307  }
2308 
2317  public function getFolder($identifier, $returnInaccessibleFolderObject = false)
2318  {
2319  $data = $this->driver->getFolderInfoByIdentifier($identifier);
2320  $folder = ResourceFactory::getInstance()->createFolderObject($this, $data['identifier'], $data['name']);
2321 
2322  try {
2323  $this->assureFolderReadPermission($folder);
2324  } catch (Exception\InsufficientFolderAccessPermissionsException $e) {
2325  $folder = null;
2326  if ($returnInaccessibleFolderObject) {
2327  // if parent folder is readable return inaccessible folder object
2328  $parentPermissions = $this->driver->getPermissions($this->driver->getParentFolderIdentifierOfIdentifier($identifier));
2329  if ($parentPermissions['r']) {
2330  $folder = GeneralUtility::makeInstance(
2331  \TYPO3\CMS\Core\Resource\InaccessibleFolder::class, $this, $data['identifier'], $data['name']
2332  );
2333  }
2334  }
2335 
2336  if ($folder === null) {
2337  throw $e;
2338  }
2339  }
2340  return $folder;
2341  }
2342 
2349  public function isWithinProcessingFolder($identifier)
2350  {
2351  $inProcessingFolder = false;
2352  foreach ($this->getProcessingFolders() as $processingFolder) {
2353  if ($this->driver->isWithin($processingFolder->getIdentifier(), $identifier)) {
2354  $inProcessingFolder = true;
2355  break;
2356  }
2357  }
2358  return $inProcessingFolder;
2359  }
2360 
2369  public function isWithinFolder(Folder $folder, ResourceInterface $resource)
2370  {
2371  if ($folder->getStorage() !== $this) {
2372  throw new \InvalidArgumentException('Given folder "' . $folder->getIdentifier() . '" is not part of this storage!', 1422709241);
2373  }
2374  if ($folder->getStorage() !== $resource->getStorage()) {
2375  return false;
2376  }
2377  return $this->driver->isWithin($folder->getIdentifier(), $resource->getIdentifier());
2378  }
2379 
2388  public function getRootLevelFolder($respectFileMounts = true)
2389  {
2390  if ($respectFileMounts && !empty($this->fileMounts)) {
2391  $mount = reset($this->fileMounts);
2392  return $mount['folder'];
2393  } else {
2394  return ResourceFactory::getInstance()->createFolderObject($this, $this->driver->getRootLevelFolder(), '');
2395  }
2396  }
2397 
2405  protected function emitSanitizeFileNameSignal($fileName, Folder $targetFolder)
2406  {
2407  list($fileName) = $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_SanitizeFileName, [$fileName, $targetFolder, $this, $this->driver]);
2408  return $fileName;
2409  }
2410 
2419  protected function emitPreFileAddSignal($targetFileName, Folder $targetFolder, $sourceFilePath)
2420  {
2421  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFileAdd, [&$targetFileName, $targetFolder, $sourceFilePath, $this, $this->driver]);
2422  return $targetFileName;
2423  }
2424 
2432  protected function emitPostFileAddSignal(FileInterface $file, Folder $targetFolder)
2433  {
2434  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileAdd, [$file, $targetFolder]);
2435  }
2436 
2444  protected function emitPreFileCopySignal(FileInterface $file, Folder $targetFolder)
2445  {
2446  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFileCopy, [$file, $targetFolder]);
2447  }
2448 
2456  protected function emitPostFileCopySignal(FileInterface $file, Folder $targetFolder)
2457  {
2458  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileCopy, [$file, $targetFolder]);
2459  }
2460 
2468  protected function emitPreFileMoveSignal(FileInterface $file, Folder $targetFolder)
2469  {
2470  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFileMove, [$file, $targetFolder]);
2471  }
2472 
2481  protected function emitPostFileMoveSignal(FileInterface $file, Folder $targetFolder, Folder $originalFolder)
2482  {
2483  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileMove, [$file, $targetFolder, $originalFolder]);
2484  }
2485 
2493  protected function emitPreFileRenameSignal(FileInterface $file, $targetFolder)
2494  {
2495  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFileRename, [$file, $targetFolder]);
2496  }
2497 
2505  protected function emitPostFileRenameSignal(FileInterface $file, $sanitizedTargetFileName)
2506  {
2507  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileRename, [$file, $sanitizedTargetFileName]);
2508  }
2509 
2517  protected function emitPreFileReplaceSignal(FileInterface $file, $localFilePath)
2518  {
2519  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFileReplace, [$file, $localFilePath]);
2520  }
2521 
2529  protected function emitPostFileReplaceSignal(FileInterface $file, $localFilePath)
2530  {
2531  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileReplace, [$file, $localFilePath]);
2532  }
2533 
2540  protected function emitPostFileCreateSignal($newFileIdentifier, Folder $targetFolder)
2541  {
2542  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileCreate, [$newFileIdentifier, $targetFolder]);
2543  }
2544 
2551  protected function emitPreFileDeleteSignal(FileInterface $file)
2552  {
2553  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFileDelete, [$file]);
2554  }
2555 
2562  protected function emitPostFileDeleteSignal(FileInterface $file)
2563  {
2564  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileDelete, [$file]);
2565  }
2566 
2574  protected function emitPostFileSetContentsSignal(FileInterface $file, $content)
2575  {
2576  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFileSetContents, [$file, $content]);
2577  }
2578 
2586  protected function emitPreFolderAddSignal(Folder $targetFolder, $name)
2587  {
2588  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFolderAdd, [$targetFolder, $name]);
2589  }
2590 
2597  protected function emitPostFolderAddSignal(Folder $folder)
2598  {
2599  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFolderAdd, [$folder]);
2600  }
2601 
2610  protected function emitPreFolderCopySignal(Folder $folder, Folder $targetFolder, $newName)
2611  {
2612  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFolderCopy, [$folder, $targetFolder, $newName]);
2613  }
2614 
2623  protected function emitPostFolderCopySignal(Folder $folder, Folder $targetFolder, $newName)
2624  {
2625  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFolderCopy, [$folder, $targetFolder, $newName]);
2626  }
2627 
2636  protected function emitPreFolderMoveSignal(Folder $folder, Folder $targetFolder, $newName)
2637  {
2638  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFolderMove, [$folder, $targetFolder, $newName]);
2639  }
2640 
2650  protected function emitPostFolderMoveSignal(Folder $folder, Folder $targetFolder, $newName, Folder $originalFolder)
2651  {
2652  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFolderMove, [$folder, $targetFolder, $newName, $originalFolder]);
2653  }
2654 
2662  protected function emitPreFolderRenameSignal(Folder $folder, $newName)
2663  {
2664  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFolderRename, [$folder, $newName]);
2665  }
2666 
2674  protected function emitPostFolderRenameSignal(Folder $folder, $newName)
2675  {
2676  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFolderRename, [$folder, $newName]);
2677  }
2678 
2685  protected function emitPreFolderDeleteSignal(Folder $folder)
2686  {
2687  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreFolderDelete, [$folder]);
2688  }
2689 
2696  protected function emitPostFolderDeleteSignal(Folder $folder)
2697  {
2698  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PostFolderDelete, [$folder]);
2699  }
2700 
2708  protected function emitPreGeneratePublicUrlSignal(ResourceInterface $resourceObject, $relativeToCurrentScript, array $urlData)
2709  {
2710  $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Resource\ResourceStorage::class, self::SIGNAL_PreGeneratePublicUrl, [$this, $this->driver, $resourceObject, $relativeToCurrentScript, $urlData]);
2711  }
2712 
2726  protected function getUniqueName(Folder $folder, $theFile, $dontCheckForUnique = false)
2727  {
2728  static $maxNumber = 99, $uniqueNamePrefix = '';
2729  // Fetches info about path, name, extension of $theFile
2730  $origFileInfo = PathUtility::pathinfo($theFile);
2731  // Adds prefix
2732  if ($uniqueNamePrefix) {
2733  $origFileInfo['basename'] = $uniqueNamePrefix . $origFileInfo['basename'];
2734  $origFileInfo['filename'] = $uniqueNamePrefix . $origFileInfo['filename'];
2735  }
2736  // Check if the file exists and if not - return the fileName...
2737  // The destinations file
2738  $theDestFile = $origFileInfo['basename'];
2739  // If the file does NOT exist we return this fileName
2740  if (!$this->driver->fileExistsInFolder($theDestFile, $folder->getIdentifier()) || $dontCheckForUnique) {
2741  return $theDestFile;
2742  }
2743  // Well the fileName in its pure form existed. Now we try to append
2744  // numbers / unique-strings and see if we can find an available fileName
2745  // This removes _xx if appended to the file
2746  $theTempFileBody = preg_replace('/_[0-9][0-9]$/', '', $origFileInfo['filename']);
2747  $theOrigExt = $origFileInfo['extension'] ? '.' . $origFileInfo['extension'] : '';
2748  for ($a = 1; $a <= $maxNumber + 1; $a++) {
2749  // First we try to append numbers
2750  if ($a <= $maxNumber) {
2751  $insert = '_' . sprintf('%02d', $a);
2752  } else {
2753  $insert = '_' . substr(md5(uniqid('', true)), 0, 6);
2754  }
2755  $theTestFile = $theTempFileBody . $insert . $theOrigExt;
2756  // The destinations file
2757  $theDestFile = $theTestFile;
2758  // If the file does NOT exist we return this fileName
2759  if (!$this->driver->fileExistsInFolder($theDestFile, $folder->getIdentifier())) {
2760  return $theDestFile;
2761  }
2762  }
2763  throw new \RuntimeException('Last possible name "' . $theDestFile . '" is already taken.', 1325194291);
2764  }
2765 
2771  protected function getSignalSlotDispatcher()
2772  {
2773  if (!isset($this->signalSlotDispatcher)) {
2774  $this->signalSlotDispatcher = $this->getObjectManager()->get(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class);
2775  }
2777  }
2778 
2784  protected function getObjectManager()
2785  {
2786  return GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
2787  }
2788 
2792  protected function getFileFactory()
2793  {
2794  return GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\ResourceFactory::class);
2795  }
2796 
2800  protected function getFileIndexRepository()
2801  {
2803  }
2804 
2808  protected function getFileProcessingService()
2809  {
2810  if (!$this->fileProcessingService) {
2811  $this->fileProcessingService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\Service\FileProcessingService::class, $this, $this->driver);
2812  }
2814  }
2815 
2822  public function getRole(FolderInterface $folder)
2823  {
2824  $folderRole = FolderInterface::ROLE_DEFAULT;
2825  $identifier = $folder->getIdentifier();
2826  if (method_exists($this->driver, 'getRole')) {
2827  $folderRole = $this->driver->getRole($folder->getIdentifier());
2828  }
2829  if (isset($this->fileMounts[$identifier])) {
2830  $folderRole = FolderInterface::ROLE_MOUNT;
2831 
2832  if (!empty($this->fileMounts[$identifier]['read_only'])) {
2834  }
2835  if ($this->fileMounts[$identifier]['user_mount']) {
2836  $folderRole = FolderInterface::ROLE_USER_MOUNT;
2837  }
2838  }
2839  if ($folder instanceof Folder && $this->isProcessingFolder($folder)) {
2840  $folderRole = FolderInterface::ROLE_PROCESSING;
2841  }
2842 
2843  return $folderRole;
2844  }
2845 
2853  public function getProcessingFolder(File $file = null)
2854  {
2855  if (!isset($this->processingFolder)) {
2856  $processingFolder = self::DEFAULT_ProcessingFolder;
2857  if (!empty($this->storageRecord['processingfolder'])) {
2858  $processingFolder = $this->storageRecord['processingfolder'];
2859  }
2860  try {
2861  if (strpos($processingFolder, ':') !== false) {
2862  list($storageUid, $processingFolderIdentifier) = explode(':', $processingFolder, 2);
2863  $storage = ResourceFactory::getInstance()->getStorageObject($storageUid);
2864  if ($storage->hasFolder($processingFolderIdentifier)) {
2865  $this->processingFolder = $storage->getFolder($processingFolderIdentifier);
2866  } else {
2867  $rootFolder = $storage->getRootLevelFolder(false);
2868  $currentEvaluatePermissions = $storage->getEvaluatePermissions();
2869  $storage->setEvaluatePermissions(false);
2870  $this->processingFolder = $storage->createFolder(
2871  ltrim($processingFolderIdentifier, '/'),
2872  $rootFolder
2873  );
2874  $storage->setEvaluatePermissions($currentEvaluatePermissions);
2875  }
2876  } else {
2877  if ($this->driver->folderExists($processingFolder) === false) {
2878  $rootFolder = $this->getRootLevelFolder(false);
2879  $currentEvaluatePermissions = $this->evaluatePermissions;
2880  $this->evaluatePermissions = false;
2881  $this->processingFolder = $this->createFolder(
2883  $rootFolder
2884  );
2885  $this->evaluatePermissions = $currentEvaluatePermissions;
2886  } else {
2887  $data = $this->driver->getFolderInfoByIdentifier($processingFolder);
2888  $this->processingFolder = ResourceFactory::getInstance()->createFolderObject($this, $data['identifier'], $data['name']);
2889  }
2890  }
2891  } catch (Exception\InsufficientFolderWritePermissionsException $e) {
2892  $this->processingFolder = GeneralUtility::makeInstance(
2893  InaccessibleFolder::class, $this, $processingFolder, $processingFolder
2894  );
2895  } catch (Exception\ResourcePermissionsUnavailableException $e) {
2896  $this->processingFolder = GeneralUtility::makeInstance(
2897  InaccessibleFolder::class, $this, $processingFolder, $processingFolder
2898  );
2899  }
2900  }
2901 
2903  if (!empty($file)) {
2905  }
2906  return $processingFolder;
2907  }
2908 
2918  protected function getNestedProcessingFolder(File $file, Folder $rootProcessingFolder)
2919  {
2920  $processingFolder = $rootProcessingFolder;
2921  $nestedFolderNames = $this->getNamesForNestedProcessingFolder(
2922  $file->getIdentifier(),
2923  self::PROCESSING_FOLDER_LEVELS
2924  );
2925 
2926  try {
2927  foreach ($nestedFolderNames as $folderName) {
2928  if ($processingFolder->hasFolder($folderName)) {
2929  $processingFolder = $processingFolder->getSubfolder($folderName);
2930  } else {
2931  $currentEvaluatePermissions = $processingFolder->getStorage()->getEvaluatePermissions();
2932  $processingFolder->getStorage()->setEvaluatePermissions(false);
2933  $processingFolder = $processingFolder->createFolder($folderName);
2934  $processingFolder->getStorage()->setEvaluatePermissions($currentEvaluatePermissions);
2935  }
2936  }
2937  } catch (Exception\FolderDoesNotExistException $e) {
2938  }
2939 
2940  return $processingFolder;
2941  }
2942 
2950  protected function getNamesForNestedProcessingFolder($fileIdentifier, $levels)
2951  {
2952  $names = [];
2953  if ($levels === 0) {
2954  return $names;
2955  }
2956  $hash = md5($fileIdentifier);
2957  for ($i = 1; $i <= $levels; $i++) {
2958  $names[] = substr($hash, $i, 1);
2959  }
2960  return $names;
2961  }
2962 
2968  public function getDriverType()
2969  {
2970  return $this->storageRecord['driver'];
2971  }
2972 
2978  protected function getIndexer()
2979  {
2980  return GeneralUtility::makeInstance(Index\Indexer::class, $this);
2981  }
2982 
2987  public function setDefault($isDefault)
2988  {
2989  $this->isDefault = (bool)$isDefault;
2990  }
2991 
2995  public function isDefault()
2996  {
2997  return $this->isDefault;
2998  }
2999 
3005  protected function getBackendUser()
3006  {
3007  return $GLOBALS['BE_USER'];
3008  }
3009 
3013  protected function getLogger()
3014  {
3016  $logManager = GeneralUtility::makeInstance(
3017  \TYPO3\CMS\Core\Log\LogManager::class
3018  );
3019  return $logManager->getLogger(get_class($this));
3020  }
3021 }
assureFolderCopyPermissions(FolderInterface $folderToCopy, FolderInterface $targetParentFolder)
emitPreFileMoveSignal(FileInterface $file, Folder $targetFolder)
moveFolder(Folder $folderToMove, Folder $targetParentFolder, $newFolderName=null, $conflictMode=DuplicationBehavior::RENAME)
replaceFile(FileInterface $file, $localFilePath)
moveFile($file, $targetFolder, $targetFileName=null, $conflictMode=DuplicationBehavior::RENAME)
emitPreFileReplaceSignal(FileInterface $file, $localFilePath)
isWithinFolder(Folder $folder, ResourceInterface $resource)
checkFileActionPermission($action, FileInterface $file)
assureFileReplacePermissions(FileInterface $file)
setFileContents(AbstractFile $file, $contents)
hasFileInFolder($fileName, Folder $folder)
emitPreFolderMoveSignal(Folder $folder, Folder $targetFolder, $newName)
processFile(FileInterface $fileObject, $context, array $configuration)
addFileMount($folderIdentifier, $additionalData=[])
static getRelativePathTo($targetPath)
Definition: PathUtility.php:29
__construct(Driver\DriverInterface $driver, array $storageRecord)
emitPostFileCreateSignal($newFileIdentifier, Folder $targetFolder)
getFileInfoByIdentifier($identifier, array $propertiesToExtract=[])
getFolderInFolder($folderName, Folder $parentFolder, $returnInaccessibleFolderObject=false)
assureFileAddPermissions($targetFolder, $targetFileName)
getFolderIdentifiersInFolder($folderIdentifier, $useFilters=true, $recursive=false)
emitPreFileCopySignal(FileInterface $file, Folder $targetFolder)
hashFileByIdentifier($fileIdentifier, $hash)
emitPreFolderRenameSignal(Folder $folder, $newName)
dumpFileContents(FileInterface $file, $asDownload=false, $alternativeFilename=null, $overrideMimeType=null)
emitPreFileRenameSignal(FileInterface $file, $targetFolder)
static hmac($input, $additionalSecret='')
isWithinFileMountBoundaries($subject, $checkWriteAccess=false)
checkFolderActionPermission($action, Folder $folder=null)
emitPostFolderRenameSignal(Folder $folder, $newName)
assureFolderMovePermissions(FolderInterface $folderToMove, FolderInterface $targetParentFolder)
setUserPermissions(array $userPermissions)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
emitPostFileReplaceSignal(FileInterface $file, $localFilePath)
getUniqueName(Folder $folder, $theFile, $dontCheckForUnique=false)
copyFolderBetweenStorages(Folder $folderToCopy, Folder $targetParentFolder, $newFolderName)
emitPostFileRenameSignal(FileInterface $file, $sanitizedTargetFileName)
static verifyFilenameAgainstDenyPattern($filename)
getFilesInFolder(Folder $folder, $start=0, $maxNumberOfItems=0, $useFilters=true, $recursive=false, $sort='', $sortRev=false)
hashFile(FileInterface $fileObject, $hash)
getFolder($identifier, $returnInaccessibleFolderObject=false)
getNestedProcessingFolder(File $file, Folder $rootProcessingFolder)
getRootLevelFolder($respectFileMounts=true)
emitPostFileSetContentsSignal(FileInterface $file, $content)
emitPostFileCopySignal(FileInterface $file, Folder $targetFolder)
getPublicUrl(ResourceInterface $resourceObject, $relativeToCurrentScript=false)
getFileInFolder($fileName, Folder $folder)
sanitizeFileName($fileName, Folder $targetFolder=null)
emitPreGeneratePublicUrlSignal(ResourceInterface $resourceObject, $relativeToCurrentScript, array $urlData)
addUploadedFile(array $uploadedFileData, Folder $targetFolder=null, $targetFileName=null, $conflictMode=DuplicationBehavior::CANCEL)
static pathinfo($path, $options=null)
addFile($localFilePath, Folder $targetFolder, $targetFileName='', $conflictMode=DuplicationBehavior::RENAME, $removeOriginal=true)
assureFileCopyPermissions(FileInterface $file, Folder $targetFolder, $targetFileName)
moveFolderBetweenStorages(Folder $folderToMove, Folder $targetParentFolder, $newFolderName)
assureFolderDeletePermission(Folder $folder, $checkDeleteRecursively)
emitPostFileAddSignal(FileInterface $file, Folder $targetFolder)
getFileForLocalProcessing(FileInterface $fileObject, $writable=true)
createFolder($folderName, Folder $parentFolder=null)
getFiles($start=0, $numberOfItems=0, $filterMode=self::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, $recursive=false, $sort='', $sortRev=false)
Definition: Folder.php:218
assureFileDeletePermissions(FileInterface $file)
setDriver(Driver\DriverInterface $driver)
emitPreFolderAddSignal(Folder $targetFolder, $name)
deleteFolder($folderObject, $deleteRecursively=false)
createFile($fileName, Folder $targetFolderObject)
countFilesInFolder(Folder $folder, $useFilters=true, $recursive=false)
emitPostFolderMoveSignal(Folder $folder, Folder $targetFolder, $newName, Folder $originalFolder)
getSubfolders($start=0, $numberOfItems=0, $filterMode=self::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, $recursive=false)
Definition: Folder.php:273
assureFileUploadPermissions($localFilePath, $targetFolder, $targetFileName, $uploadedFileSize)
getFoldersInFolder(Folder $folder, $start=0, $maxNumberOfItems=0, $useFilters=true, $recursive=false, $sort='', $sortRev=false)
emitPreFileAddSignal($targetFileName, Folder $targetFolder, $sourceFilePath)
getNamesForNestedProcessingFolder($fileIdentifier, $levels)
emitPostFolderCopySignal(Folder $folder, Folder $targetFolder, $newName)
emitPreFolderCopySignal(Folder $folder, Folder $targetFolder, $newName)
hasFolderInFolder($folderName, Folder $folder)
emitSanitizeFileNameSignal($fileName, Folder $targetFolder)
getFileIdentifiersInFolder($folderIdentifier, $useFilters=true, $recursive=false)
updateProcessedFile($localFilePath, ProcessedFile $processedFile, Folder $processingFolder=null)
assureFileRenamePermissions(FileInterface $file, $targetFileName)
copyFolder(FolderInterface $folderToCopy, FolderInterface $targetParentFolder, $newFolderName=null, $conflictMode=DuplicationBehavior::RENAME)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
countFoldersInFolder(Folder $folder, $useFilters=true, $recursive=false)
assureFileMovePermissions(FileInterface $file, Folder $targetFolder, $targetFileName)
emitPostFileMoveSignal(FileInterface $file, Folder $targetFolder, Folder $originalFolder)
getFileInfo(FileInterface $fileObject)
static uniqueList($in_list, $secondParameter=null)