TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
ResourceStorage.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Core\Resource;
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 
27 
61 {
67  protected $driver;
68 
74  protected $storageRecord;
75 
81  protected $configuration;
82 
87 
96  protected $evaluatePermissions = false;
97 
103  protected $fileMounts = [];
104 
111  protected $userPermissions = [];
112 
119  protected $capabilities;
120 
125 
129  protected $processingFolder;
130 
137 
143  protected $isOnline = null;
144 
148  protected $isDefault = false;
149 
156 
161 
168  public function __construct(Driver\DriverInterface $driver, array $storageRecord)
169  {
170  $this->storageRecord = $storageRecord;
171  $this->configuration = ResourceFactory::getInstance()->convertFlexFormDataToConfigurationArray($storageRecord['configuration']);
172  $this->capabilities =
173  ($this->storageRecord['is_browsable'] ? self::CAPABILITY_BROWSABLE : 0) |
174  ($this->storageRecord['is_public'] ? self::CAPABILITY_PUBLIC : 0) |
175  ($this->storageRecord['is_writable'] ? self::CAPABILITY_WRITABLE : 0);
176 
177  $this->driver = $driver;
178  $this->driver->setStorageUid($storageRecord['uid']);
179  $this->driver->mergeConfigurationCapabilities($this->capabilities);
180  try {
181  $this->driver->processConfiguration();
182  } catch (Exception\InvalidConfigurationException $e) {
183  // Configuration error
184  $this->isOnline = false;
185 
186  $message = sprintf(
187  'Failed initializing storage [%d] "%s", error: %s',
188  $this->getUid(),
189  $this->getName(),
190  $e->getMessage()
191  );
192 
193  $this->getLogger()->error($message);
194  }
195  $this->driver->initialize();
196  $this->capabilities = $this->driver->getCapabilities();
197 
198  $this->isDefault = (isset($storageRecord['is_default']) && $storageRecord['is_default'] == 1);
200  }
201 
207  public function getConfiguration()
208  {
209  return $this->configuration;
210  }
211 
217  public function setConfiguration(array $configuration)
218  {
219  $this->configuration = $configuration;
220  }
221 
227  public function getStorageRecord()
228  {
229  return $this->storageRecord;
230  }
231 
238  public function setDriver(Driver\DriverInterface $driver)
239  {
240  $this->driver = $driver;
241  return $this;
242  }
243 
249  protected function getDriver()
250  {
251  return $this->driver;
252  }
253 
259  public function getName()
260  {
261  return $this->storageRecord['name'];
262  }
263 
269  public function getUid()
270  {
271  return (int)$this->storageRecord['uid'];
272  }
273 
279  public function hasChildren()
280  {
281  return true;
282  }
283 
284  /*********************************
285  * Capabilities
286  ********************************/
293  public function getCapabilities()
294  {
295  return (int)$this->capabilities;
296  }
297 
304  protected function hasCapability($capability)
305  {
306  return ($this->capabilities & $capability) == $capability;
307  }
308 
317  public function isPublic()
318  {
319  return $this->hasCapability(self::CAPABILITY_PUBLIC);
320  }
321 
328  public function isWritable()
329  {
330  return $this->hasCapability(self::CAPABILITY_WRITABLE);
331  }
332 
338  public function isBrowsable()
339  {
340  return $this->isOnline() && $this->hasCapability(self::CAPABILITY_BROWSABLE);
341  }
342 
349  {
350  return $this->driver->isCaseSensitiveFileSystem();
351  }
352 
358  public function isOnline()
359  {
360  if ($this->isOnline === null) {
361  if ($this->getUid() === 0) {
362  $this->isOnline = true;
363  }
364  // the storage is not marked as online for a longer time
365  if ($this->storageRecord['is_online'] == 0) {
366  $this->isOnline = false;
367  }
368  if ($this->isOnline !== false) {
369  // all files are ALWAYS available in the frontend
370  if (TYPO3_MODE === 'FE') {
371  $this->isOnline = true;
372  } else {
373  // check if the storage is disabled temporary for now
374  $registryObject = GeneralUtility::makeInstance(Registry::class);
375  $offlineUntil = $registryObject->get('core', 'sys_file_storage-' . $this->getUid() . '-offline-until');
376  if ($offlineUntil && $offlineUntil > time()) {
377  $this->isOnline = false;
378  } else {
379  $this->isOnline = true;
380  }
381  }
382  }
383  }
384  return $this->isOnline;
385  }
386 
392  public function autoExtractMetadataEnabled()
393  {
394  return !empty($this->storageRecord['auto_extract_metadata']);
395  }
396 
406  public function markAsPermanentlyOffline()
407  {
408  if ($this->getUid() > 0) {
409  // @todo: move this to the storage repository
410  GeneralUtility::makeInstance(ConnectionPool::class)
411  ->getConnectionForTable('sys_file_storage')
412  ->update(
413  'sys_file_storage',
414  ['is_online' => 0],
415  ['uid' => (int)$this->getUid()]
416  );
417  }
418  $this->storageRecord['is_online'] = 0;
419  $this->isOnline = false;
420  }
421 
430  public function markAsTemporaryOffline()
431  {
432  $registryObject = GeneralUtility::makeInstance(Registry::class);
433  $registryObject->set('core', 'sys_file_storage-' . $this->getUid() . '-offline-until', time() + 60 * 5);
434  $this->storageRecord['is_online'] = 0;
435  $this->isOnline = false;
436  }
437 
438  /*********************************
439  * User Permissions / File Mounts
440  ********************************/
451  public function addFileMount($folderIdentifier, $additionalData = [])
452  {
453  // check for the folder before we add it as a filemount
454  if ($this->driver->folderExists($folderIdentifier) === false) {
455  // if there is an error, this is important and should be handled
456  // as otherwise the user would see the whole storage without any restrictions for the filemounts
457  throw new Exception\FolderDoesNotExistException('Folder for file mount ' . $folderIdentifier . ' does not exist.', 1334427099);
458  }
459  $data = $this->driver->getFolderInfoByIdentifier($folderIdentifier);
460  $folderObject = ResourceFactory::getInstance()->createFolderObject($this, $data['identifier'], $data['name']);
461  // Use the canonical identifier instead of the user provided one!
462  $folderIdentifier = $folderObject->getIdentifier();
463  if (
464  !empty($this->fileMounts[$folderIdentifier])
465  && empty($this->fileMounts[$folderIdentifier]['read_only'])
466  && !empty($additionalData['read_only'])
467  ) {
468  // Do not overwrite a regular mount with a read only mount
469  return;
470  }
471  if (empty($additionalData)) {
472  $additionalData = [
473  'path' => $folderIdentifier,
474  'title' => $folderIdentifier,
475  'folder' => $folderObject
476  ];
477  } else {
478  $additionalData['folder'] = $folderObject;
479  if (!isset($additionalData['title'])) {
480  $additionalData['title'] = $folderIdentifier;
481  }
482  }
483  $this->fileMounts[$folderIdentifier] = $additionalData;
484  }
485 
491  public function getFileMounts()
492  {
493  return $this->fileMounts;
494  }
495 
504  public function isWithinFileMountBoundaries($subject, $checkWriteAccess = false)
505  {
506  if (!$this->evaluatePermissions) {
507  return true;
508  }
509  $isWithinFileMount = false;
510  if (!$subject) {
511  $subject = $this->getRootLevelFolder();
512  }
513  $identifier = $subject->getIdentifier();
514 
515  // Allow access to processing folder
516  if ($this->isWithinProcessingFolder($identifier)) {
517  $isWithinFileMount = true;
518  } else {
519  // Check if the identifier of the subject is within at
520  // least one of the file mounts
521  $writableFileMountAvailable = false;
522  foreach ($this->fileMounts as $fileMount) {
524  $folder = $fileMount['folder'];
525  if ($this->driver->isWithin($folder->getIdentifier(), $identifier)) {
526  $isWithinFileMount = true;
527  if (!$checkWriteAccess) {
528  break;
529  } elseif (empty($fileMount['read_only'])) {
530  $writableFileMountAvailable = true;
531  break;
532  }
533  }
534  }
535  $isWithinFileMount = $checkWriteAccess ? $writableFileMountAvailable : $isWithinFileMount;
536  }
537  return $isWithinFileMount;
538  }
539 
547  {
548  $this->evaluatePermissions = (bool)$evaluatePermissions;
549  }
550 
557  public function getEvaluatePermissions()
558  {
560  }
561 
568  public function setUserPermissions(array $userPermissions)
569  {
570  $this->userPermissions = $userPermissions;
571  }
572 
581  public function checkUserActionPermission($action, $type)
582  {
583  if (!$this->evaluatePermissions) {
584  return true;
585  }
586 
587  $allow = false;
588  if (!empty($this->userPermissions[strtolower($action) . ucfirst(strtolower($type))])) {
589  $allow = true;
590  }
591 
592  return $allow;
593  }
594 
608  public function checkFileActionPermission($action, FileInterface $file)
609  {
610  $isProcessedFile = $file instanceof ProcessedFile;
611  // Check 1: Does the user have permission to perform the action? e.g. "readFile"
612  if (!$isProcessedFile && $this->checkUserActionPermission($action, 'File') === false) {
613  return false;
614  }
615  // Check 2: No action allowed on files for denied file extensions
616  if (!$this->checkFileExtensionPermission($file->getName())) {
617  return false;
618  }
619  $isReadCheck = false;
620  if (in_array($action, ['read', 'copy', 'move', 'replace'], true)) {
621  $isReadCheck = true;
622  }
623  $isWriteCheck = false;
624  if (in_array($action, ['add', 'write', 'move', 'rename', 'replace', 'delete'], true)) {
625  $isWriteCheck = true;
626  }
627  // Check 3: Does the user have the right to perform the action?
628  // (= is he within the file mount borders)
629  if (!$isProcessedFile && !$this->isWithinFileMountBoundaries($file, $isWriteCheck)) {
630  return false;
631  }
632 
633  $isMissing = false;
634  if (!$isProcessedFile && $file instanceof File) {
635  $isMissing = $file->isMissing();
636  }
637 
638  if ($this->driver->fileExists($file->getIdentifier()) === false) {
639  $file->setMissing(true);
640  $isMissing = true;
641  }
642 
643  // Check 4: Check the capabilities of the storage (and the driver)
644  if ($isWriteCheck && ($isMissing || !$this->isWritable())) {
645  return false;
646  }
647 
648  // Check 5: "File permissions" of the driver (only when file isn't marked as missing)
649  if (!$isMissing) {
650  $filePermissions = $this->driver->getPermissions($file->getIdentifier());
651  if ($isReadCheck && !$filePermissions['r']) {
652  return false;
653  }
654  if ($isWriteCheck && !$filePermissions['w']) {
655  return false;
656  }
657  }
658  return true;
659  }
660 
671  public function checkFolderActionPermission($action, Folder $folder = null)
672  {
673  // Check 1: Does the user have permission to perform the action? e.g. "writeFolder"
674  if ($this->checkUserActionPermission($action, 'Folder') === false) {
675  return false;
676  }
677 
678  // If we do not have a folder here, we cannot do further checks
679  if ($folder === null) {
680  return true;
681  }
682 
683  $isReadCheck = false;
684  if (in_array($action, ['read', 'copy'], true)) {
685  $isReadCheck = true;
686  }
687  $isWriteCheck = false;
688  if (in_array($action, ['add', 'move', 'write', 'delete', 'rename'], true)) {
689  $isWriteCheck = true;
690  }
691  // Check 2: Does the user has the right to perform the action?
692  // (= is he within the file mount borders)
693  if (!$this->isWithinFileMountBoundaries($folder, $isWriteCheck)) {
694  return false;
695  }
696  // Check 3: Check the capabilities of the storage (and the driver)
697  if ($isReadCheck && !$this->isBrowsable()) {
698  return false;
699  }
700  if ($isWriteCheck && !$this->isWritable()) {
701  return false;
702  }
703 
704  // Check 4: "Folder permissions" of the driver
705  $folderPermissions = $this->driver->getPermissions($folder->getIdentifier());
706  if ($isReadCheck && !$folderPermissions['r']) {
707  return false;
708  }
709  if ($isWriteCheck && !$folderPermissions['w']) {
710  return false;
711  }
712  return true;
713  }
714 
722  protected function checkFileExtensionPermission($fileName)
723  {
724  $fileName = $this->driver->sanitizeFileName($fileName);
725  $isAllowed = GeneralUtility::verifyFilenameAgainstDenyPattern($fileName);
726  if ($isAllowed && $this->evaluatePermissions) {
727  $fileExtension = strtolower(PathUtility::pathinfo($fileName, PATHINFO_EXTENSION));
728  // Set up the permissions for the file extension
729  $fileExtensionPermissions = $GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']['webspace'];
730  $fileExtensionPermissions['allow'] = GeneralUtility::uniqueList(strtolower($fileExtensionPermissions['allow']));
731  $fileExtensionPermissions['deny'] = GeneralUtility::uniqueList(strtolower($fileExtensionPermissions['deny']));
732  if ($fileExtension !== '') {
733  // If the extension is found amongst the allowed types, we return TRUE immediately
734  if ($fileExtensionPermissions['allow'] === '*' || GeneralUtility::inList($fileExtensionPermissions['allow'], $fileExtension)) {
735  return true;
736  }
737  // If the extension is found amongst the denied types, we return FALSE immediately
738  if ($fileExtensionPermissions['deny'] === '*' || GeneralUtility::inList($fileExtensionPermissions['deny'], $fileExtension)) {
739  return false;
740  }
741  // If no match we return TRUE
742  return true;
743  } else {
744  if ($fileExtensionPermissions['allow'] === '*') {
745  return true;
746  }
747  if ($fileExtensionPermissions['deny'] === '*') {
748  return false;
749  }
750  return true;
751  }
752  }
753  return $isAllowed;
754  }
755 
763  protected function assureFolderReadPermission(Folder $folder = null)
764  {
765  if (!$this->checkFolderActionPermission('read', $folder)) {
766  if ($folder === null) {
767  throw new Exception\InsufficientFolderAccessPermissionsException(
768  'You are not allowed to read folders',
769  1430657869
770  );
771  } else {
772  throw new Exception\InsufficientFolderAccessPermissionsException(
773  'You are not allowed to access the given folder: "' . $folder->getName() . '"',
774  1375955684
775  );
776  }
777  }
778  }
779 
790  protected function assureFolderDeletePermission(Folder $folder, $checkDeleteRecursively)
791  {
792  // Check user permissions for recursive deletion if it is requested
793  if ($checkDeleteRecursively && !$this->checkUserActionPermission('recursivedelete', 'Folder')) {
794  throw new Exception\InsufficientUserPermissionsException('You are not allowed to delete folders recursively', 1377779423);
795  }
796  // Check user action permission
797  if (!$this->checkFolderActionPermission('delete', $folder)) {
798  throw new Exception\InsufficientFolderAccessPermissionsException(
799  'You are not allowed to delete the given folder: "' . $folder->getName() . '"',
800  1377779039
801  );
802  }
803  // Check if the user has write permissions to folders
804  // Would be good if we could check for actual write permissions in the containig folder
805  // but we cannot since we have no access to the containing folder of this file.
806  if (!$this->checkUserActionPermission('write', 'Folder')) {
807  throw new Exception\InsufficientFolderWritePermissionsException('Writing to folders is not allowed.', 1377779111);
808  }
809  }
810 
819  protected function assureFileReadPermission(FileInterface $file)
820  {
821  if (!$this->checkFileActionPermission('read', $file)) {
822  throw new Exception\InsufficientFileAccessPermissionsException(
823  'You are not allowed to access that file: "' . $file->getName() . '"',
824  1375955429
825  );
826  }
827  if (!$this->checkFileExtensionPermission($file->getName())) {
828  throw new Exception\IllegalFileExtensionException(
829  'You are not allowed to use that file extension. File: "' . $file->getName() . '"',
830  1375955430
831  );
832  }
833  }
834 
844  protected function assureFileWritePermissions(FileInterface $file)
845  {
846  // Check if user is allowed to write the file and $file is writable
847  if (!$this->checkFileActionPermission('write', $file)) {
848  throw new Exception\InsufficientFileWritePermissionsException('Writing to file "' . $file->getIdentifier() . '" is not allowed.', 1330121088);
849  }
850  if (!$this->checkFileExtensionPermission($file->getName())) {
851  throw new Exception\IllegalFileExtensionException('You are not allowed to edit a file with extension "' . $file->getExtension() . '"', 1366711933);
852  }
853  }
854 
863  protected function assureFileReplacePermissions(FileInterface $file)
864  {
865  // Check if user is allowed to replace the file and $file is writable
866  if (!$this->checkFileActionPermission('replace', $file)) {
867  throw new Exception\InsufficientFileWritePermissionsException('Replacing file "' . $file->getIdentifier() . '" is not allowed.', 1436899571);
868  }
869  // Check if parentFolder is writable for the user
870  if (!$this->checkFolderActionPermission('write', $file->getParentFolder())) {
871  throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $file->getIdentifier() . '"', 1436899572);
872  }
873  }
874 
884  protected function assureFileDeletePermissions(FileInterface $file)
885  {
886  // Check for disallowed file extensions
887  if (!$this->checkFileExtensionPermission($file->getName())) {
888  throw new Exception\IllegalFileExtensionException('You are not allowed to delete a file with extension "' . $file->getExtension() . '"', 1377778916);
889  }
890  // Check further permissions if file is not a processed file
891  if (!$file instanceof ProcessedFile) {
892  // Check if user is allowed to delete the file and $file is writable
893  if (!$this->checkFileActionPermission('delete', $file)) {
894  throw new Exception\InsufficientFileWritePermissionsException('You are not allowed to delete the file "' . $file->getIdentifier() . '"', 1319550425);
895  }
896  // Check if the user has write permissions to folders
897  // Would be good if we could check for actual write permissions in the containig folder
898  // but we cannot since we have no access to the containing folder of this file.
899  if (!$this->checkUserActionPermission('write', 'Folder')) {
900  throw new Exception\InsufficientFolderWritePermissionsException('Writing to folders is not allowed.', 1377778702);
901  }
902  }
903  }
904 
917  protected function assureFileAddPermissions($targetFolder, $targetFileName)
918  {
919  // Check for a valid file extension
920  if (!$this->checkFileExtensionPermission($targetFileName)) {
921  throw new Exception\IllegalFileExtensionException('Extension of file name is not allowed in "' . $targetFileName . '"!', 1322120271);
922  }
923  // Makes sure the user is allowed to upload
924  if (!$this->checkUserActionPermission('add', 'File')) {
925  throw new Exception\InsufficientUserPermissionsException('You are not allowed to add files to this storage "' . $this->getUid() . '"', 1376992145);
926  }
927  // Check if targetFolder is writable
928  if (!$this->checkFolderActionPermission('write', $targetFolder)) {
929  throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $targetFolder->getIdentifier() . '"', 1322120356);
930  }
931  }
932 
949  protected function assureFileUploadPermissions($localFilePath, $targetFolder, $targetFileName, $uploadedFileSize)
950  {
951  // Makes sure this is an uploaded file
952  if (!is_uploaded_file($localFilePath)) {
953  throw new Exception\UploadException('The upload has failed, no uploaded file found!', 1322110455);
954  }
955  // Max upload size (kb) for files.
956  $maxUploadFileSize = GeneralUtility::getMaxUploadFileSize() * 1024;
957  if ($maxUploadFileSize > 0 && $uploadedFileSize >= $maxUploadFileSize) {
958  unlink($localFilePath);
959  throw new Exception\UploadSizeException('The uploaded file exceeds the size-limit of ' . $maxUploadFileSize . ' bytes', 1322110041);
960  }
961  $this->assureFileAddPermissions($targetFolder, $targetFileName);
962  }
963 
976  protected function assureFileMovePermissions(FileInterface $file, Folder $targetFolder, $targetFileName)
977  {
978  // Check if targetFolder is within this storage
979  if ($this->getUid() !== $targetFolder->getStorage()->getUid()) {
980  throw new \RuntimeException('The target folder is not in the same storage. Target folder given: "' . $targetFolder . '"', 1422553107);
981  }
982  // Check for a valid file extension
983  if (!$this->checkFileExtensionPermission($targetFileName)) {
984  throw new Exception\IllegalFileExtensionException('Extension of file name is not allowed in "' . $targetFileName . '"!', 1378243279);
985  }
986  // Check if user is allowed to move and $file is readable and writable
987  if (!$file->getStorage()->checkFileActionPermission('move', $file)) {
988  throw new Exception\InsufficientUserPermissionsException('You are not allowed to move files to storage "' . $this->getUid() . '"', 1319219349);
989  }
990  // Check if target folder is writable
991  if (!$this->checkFolderActionPermission('write', $targetFolder)) {
992  throw new Exception\InsufficientFolderAccessPermissionsException('You are not allowed to write to the target folder "' . $targetFolder->getIdentifier() . '"', 1319219350);
993  }
994  }
995 
1007  protected function assureFileRenamePermissions(FileInterface $file, $targetFileName)
1008  {
1009  // Check if file extension is allowed
1010  if (!$this->checkFileExtensionPermission($targetFileName) || !$this->checkFileExtensionPermission($file->getName())) {
1011  throw new Exception\IllegalFileExtensionException('You are not allowed to rename a file with this extension. File given: "' . $file->getName() . '"', 1371466663);
1012  }
1013  // Check if user is allowed to rename
1014  if (!$this->checkFileActionPermission('rename', $file)) {
1015  throw new Exception\InsufficientUserPermissionsException('You are not allowed to rename files. File given: "' . $file->getName() . '"', 1319219351);
1016  }
1017  // Check if the user is allowed to write to folders
1018  // Although it would be good to check, we cannot check here if the folder actually is writable
1019  // because we do not know in which folder the file resides.
1020  // So we rely on the driver to throw an exception in case the renaming failed.
1021  if (!$this->checkFolderActionPermission('write')) {
1022  throw new Exception\InsufficientFileWritePermissionsException('You are not allowed to write to folders', 1319219352);
1023  }
1024  }
1025 
1041  protected function assureFileCopyPermissions(FileInterface $file, Folder $targetFolder, $targetFileName)
1042  {
1043  // Check if targetFolder is within this storage, this should never happen
1044  if ($this->getUid() != $targetFolder->getStorage()->getUid()) {
1045  throw new Exception('The operation of the folder cannot be called by this storage "' . $this->getUid() . '"', 1319550405);
1046  }
1047  // Check if user is allowed to copy
1048  if (!$file->getStorage()->checkFileActionPermission('copy', $file)) {
1049  throw new Exception\InsufficientFileReadPermissionsException('You are not allowed to copy the file "' . $file->getIdentifier() . '"', 1319550426);
1050  }
1051  // Check if targetFolder is writable
1052  if (!$this->checkFolderActionPermission('write', $targetFolder)) {
1053  throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $targetFolder->getIdentifier() . '"', 1319550435);
1054  }
1055  // Check for a valid file extension
1056  if (!$this->checkFileExtensionPermission($targetFileName) || !$this->checkFileExtensionPermission($file->getName())) {
1057  throw new Exception\IllegalFileExtensionException('You are not allowed to copy a file of that type.', 1319553317);
1058  }
1059  }
1060 
1076  protected function assureFolderCopyPermissions(FolderInterface $folderToCopy, FolderInterface $targetParentFolder)
1077  {
1078  // Check if targetFolder is within this storage, this should never happen
1079  if ($this->getUid() !== $targetParentFolder->getStorage()->getUid()) {
1080  throw new Exception('The operation of the folder cannot be called by this storage "' . $this->getUid() . '"', 1377777624);
1081  }
1082  if (!$folderToCopy instanceof Folder) {
1083  throw new \RuntimeException('The folder "' . $folderToCopy->getIdentifier() . '" to copy is not of type folder.', 1384209020);
1084  }
1085  // Check if user is allowed to copy and the folder is readable
1086  if (!$folderToCopy->getStorage()->checkFolderActionPermission('copy', $folderToCopy)) {
1087  throw new Exception\InsufficientFileReadPermissionsException('You are not allowed to copy the folder "' . $folderToCopy->getIdentifier() . '"', 1377777629);
1088  }
1089  if (!$targetParentFolder instanceof Folder) {
1090  throw new \RuntimeException('The target folder "' . $targetParentFolder->getIdentifier() . '" is not of type folder.', 1384209021);
1091  }
1092  // Check if targetFolder is writable
1093  if (!$this->checkFolderActionPermission('write', $targetParentFolder)) {
1094  throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $targetParentFolder->getIdentifier() . '"', 1377777635);
1095  }
1096  }
1097 
1113  protected function assureFolderMovePermissions(FolderInterface $folderToMove, FolderInterface $targetParentFolder)
1114  {
1115  // Check if targetFolder is within this storage, this should never happen
1116  if ($this->getUid() !== $targetParentFolder->getStorage()->getUid()) {
1117  throw new \InvalidArgumentException('Cannot move a folder into a folder that does not belong to this storage.', 1325777289);
1118  }
1119  if (!$folderToMove instanceof Folder) {
1120  throw new \RuntimeException('The folder "' . $folderToMove->getIdentifier() . '" to move is not of type Folder.', 1384209022);
1121  }
1122  // Check if user is allowed to move and the folder is writable
1123  // In fact we would need to check if the parent folder of the folder to move is writable also
1124  // But as of now we cannot extract the parent folder from this folder
1125  if (!$folderToMove->getStorage()->checkFolderActionPermission('move', $folderToMove)) {
1126  throw new Exception\InsufficientFileReadPermissionsException('You are not allowed to copy the folder "' . $folderToMove->getIdentifier() . '"', 1377778045);
1127  }
1128  if (!$targetParentFolder instanceof Folder) {
1129  throw new \RuntimeException('The target folder "' . $targetParentFolder->getIdentifier() . '" is not of type Folder.', 1384209023);
1130  }
1131  // Check if targetFolder is writable
1132  if (!$this->checkFolderActionPermission('write', $targetParentFolder)) {
1133  throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $targetParentFolder->getIdentifier() . '"', 1377778049);
1134  }
1135  }
1136 
1147  public function sanitizeFileName($fileName, Folder $targetFolder = null)
1148  {
1149  $targetFolder = $targetFolder ?: $this->getDefaultFolder();
1150  $fileName = $this->driver->sanitizeFileName($fileName);
1151 
1152  // The file name could be changed by an external slot
1153  $fileName = $this->emitSanitizeFileNameSignal($fileName, $targetFolder);
1154 
1155  return $fileName;
1156  }
1157 
1158  /********************
1159  * FILE ACTIONS
1160  ********************/
1174  public function addFile($localFilePath, Folder $targetFolder, $targetFileName = '', $conflictMode = DuplicationBehavior::RENAME, $removeOriginal = true)
1175  {
1176  $localFilePath = PathUtility::getCanonicalPath($localFilePath);
1177  // File is not available locally NOR is it an uploaded file
1178  if (!is_uploaded_file($localFilePath) && !file_exists($localFilePath)) {
1179  throw new \InvalidArgumentException('File "' . $localFilePath . '" does not exist.', 1319552745);
1180  }
1181  $conflictMode = DuplicationBehavior::cast($conflictMode);
1182  $targetFolder = $targetFolder ?: $this->getDefaultFolder();
1183  $targetFileName = $this->sanitizeFileName($targetFileName ?: PathUtility::basename($localFilePath), $targetFolder);
1184 
1185  $targetFileName = $this->emitPreFileAddSignal($targetFileName, $targetFolder, $localFilePath);
1186 
1187  $this->assureFileAddPermissions($targetFolder, $targetFileName);
1188  if ($conflictMode->equals(DuplicationBehavior::CANCEL) && $this->driver->fileExistsInFolder($targetFileName, $targetFolder->getIdentifier())) {
1189  throw new Exception\ExistingTargetFileNameException('File "' . $targetFileName . '" already exists in folder ' . $targetFolder->getIdentifier(), 1322121068);
1190  } elseif ($conflictMode->equals(DuplicationBehavior::RENAME)) {
1191  $targetFileName = $this->getUniqueName($targetFolder, $targetFileName);
1192  }
1193 
1194  $fileIdentifier = $this->driver->addFile($localFilePath, $targetFolder->getIdentifier(), $targetFileName, $removeOriginal);
1195  $file = ResourceFactory::getInstance()->getFileObjectByStorageAndIdentifier($this->getUid(), $fileIdentifier);
1196 
1197  if ($this->autoExtractMetadataEnabled()) {
1198  $indexer = GeneralUtility::makeInstance(Indexer::class, $this);
1199  $indexer->extractMetaData($file);
1200  }
1201 
1202  $this->emitPostFileAddSignal($file, $targetFolder);
1203 
1204  return $file;
1205  }
1206 
1217  public function updateProcessedFile($localFilePath, ProcessedFile $processedFile, Folder $processingFolder = null)
1218  {
1219  if (!file_exists($localFilePath)) {
1220  throw new \InvalidArgumentException('File "' . $localFilePath . '" does not exist.', 1319552746);
1221  }
1222  if ($processingFolder === null) {
1223  $processingFolder = $this->getProcessingFolder($processedFile->getOriginalFile());
1224  }
1225  $fileIdentifier = $this->driver->addFile($localFilePath, $processingFolder->getIdentifier(), $processedFile->getName());
1226  // @todo check if we have to update the processed file other then the identifier
1227  $processedFile->setIdentifier($fileIdentifier);
1228  return $processedFile;
1229  }
1230 
1238  public function hashFile(FileInterface $fileObject, $hash)
1239  {
1240  return $this->hashFileByIdentifier($fileObject->getIdentifier(), $hash);
1241  }
1242 
1251  public function hashFileByIdentifier($fileIdentifier, $hash)
1252  {
1253  return $this->driver->hash($fileIdentifier, $hash);
1254  }
1255 
1264  public function hashFileIdentifier($file)
1265  {
1266  if (is_object($file) && $file instanceof FileInterface) {
1268  $file = $file->getIdentifier();
1269  }
1270  return $this->driver->hashIdentifier($file);
1271  }
1272 
1283  public function getPublicUrl(ResourceInterface $resourceObject, $relativeToCurrentScript = false)
1284  {
1285  $publicUrl = null;
1286  if ($this->isOnline()) {
1287  // Pre-process the public URL by an accordant slot
1288  $this->emitPreGeneratePublicUrlSignal($resourceObject, $relativeToCurrentScript, ['publicUrl' => &$publicUrl]);
1289 
1290  if (
1291  $publicUrl === null
1292  && $resourceObject instanceof File
1293  && ($helper = OnlineMediaHelperRegistry::getInstance()->getOnlineMediaHelper($resourceObject)) !== false
1294  ) {
1295  $publicUrl = $helper->getPublicUrl($resourceObject, $relativeToCurrentScript);
1296  }
1297 
1298  // If slot did not handle the signal, use the default way to determine public URL
1299  if ($publicUrl === null) {
1300  if ($this->hasCapability(self::CAPABILITY_PUBLIC)) {
1301  $publicUrl = $this->driver->getPublicUrl($resourceObject->getIdentifier());
1302  }
1303 
1304  if ($publicUrl === null && $resourceObject instanceof FileInterface) {
1305  $queryParameterArray = ['eID' => 'dumpFile', 't' => ''];
1306  if ($resourceObject instanceof File) {
1307  $queryParameterArray['f'] = $resourceObject->getUid();
1308  $queryParameterArray['t'] = 'f';
1309  } elseif ($resourceObject instanceof ProcessedFile) {
1310  $queryParameterArray['p'] = $resourceObject->getUid();
1311  $queryParameterArray['t'] = 'p';
1312  }
1313 
1314  $queryParameterArray['token'] = GeneralUtility::hmac(implode('|', $queryParameterArray), 'resourceStorageDumpFile');
1315  $publicUrl = 'index.php?' . str_replace('+', '%20', http_build_query($queryParameterArray));
1316  }
1317 
1318  // If requested, make the path relative to the current script in order to make it possible
1319  // to use the relative file
1320  if ($publicUrl !== null && $relativeToCurrentScript && !GeneralUtility::isValidUrl($publicUrl)) {
1321  $absolutePathToContainingFolder = PathUtility::dirname(PATH_site . $publicUrl);
1322  $pathPart = PathUtility::getRelativePathTo($absolutePathToContainingFolder);
1323  $filePart = substr(PATH_site . $publicUrl, strlen($absolutePathToContainingFolder) + 1);
1324  $publicUrl = $pathPart . $filePart;
1325  }
1326  }
1327  }
1328  return $publicUrl;
1329  }
1330 
1341  public function processFile(FileInterface $fileObject, $context, array $configuration)
1342  {
1343  if ($fileObject->getStorage() !== $this) {
1344  throw new \InvalidArgumentException('Cannot process files of foreign storage', 1353401835);
1345  }
1346  $processedFile = $this->getFileProcessingService()->processFile($fileObject, $this, $context, $configuration);
1347 
1348  return $processedFile;
1349  }
1350 
1358  public function getFileForLocalProcessing(FileInterface $fileObject, $writable = true)
1359  {
1360  $filePath = $this->driver->getFileForLocalProcessing($fileObject->getIdentifier(), $writable);
1361  return $filePath;
1362  }
1363 
1370  public function getFile($identifier)
1371  {
1372  $file = $this->getFileFactory()->getFileObjectByStorageAndIdentifier($this->getUid(), $identifier);
1373  if (!$this->driver->fileExists($identifier)) {
1374  $file->setMissing(true);
1375  }
1376  return $file;
1377  }
1378 
1386  public function getFileInfo(FileInterface $fileObject)
1387  {
1388  return $this->getFileInfoByIdentifier($fileObject->getIdentifier());
1389  }
1390 
1399  public function getFileInfoByIdentifier($identifier, array $propertiesToExtract = [])
1400  {
1401  return $this->driver->getFileInfoByIdentifier($identifier, $propertiesToExtract);
1402  }
1403 
1410  {
1411  $this->fileAndFolderNameFilters = [];
1412  }
1413 
1420  {
1421  $this->fileAndFolderNameFilters = $GLOBALS['TYPO3_CONF_VARS']['SYS']['fal']['defaultFilterCallbacks'];
1422  }
1423 
1430  {
1432  }
1433 
1438  public function setFileAndFolderNameFilters(array $filters)
1439  {
1440  $this->fileAndFolderNameFilters = $filters;
1441  return $this;
1442  }
1443 
1447  public function addFileAndFolderNameFilter($filter)
1448  {
1449  $this->fileAndFolderNameFilters[] = $filter;
1450  }
1451 
1457  public function getFolderIdentifierFromFileIdentifier($fileIdentifier)
1458  {
1459  return $this->driver->getParentFolderIdentifierOfIdentifier($fileIdentifier);
1460  }
1461 
1469  public function getFileInFolder($fileName, Folder $folder)
1470  {
1471  $identifier = $this->driver->getFileInFolder($fileName, $folder->getIdentifier());
1472  return $this->getFileFactory()->getFileObjectByStorageAndIdentifier($this->getUid(), $identifier);
1473  }
1474 
1490  public function getFilesInFolder(Folder $folder, $start = 0, $maxNumberOfItems = 0, $useFilters = true, $recursive = false, $sort = '', $sortRev = false)
1491  {
1492  $this->assureFolderReadPermission($folder);
1493 
1494  $rows = $this->getFileIndexRepository()->findByFolder($folder);
1495 
1496  $filters = $useFilters == true ? $this->fileAndFolderNameFilters : [];
1497  $fileIdentifiers = array_values($this->driver->getFilesInFolder($folder->getIdentifier(), $start, $maxNumberOfItems, $recursive, $filters, $sort, $sortRev));
1498 
1499  $items = [];
1500  foreach ($fileIdentifiers as $identifier) {
1501  if (isset($rows[$identifier])) {
1502  $fileObject = $this->getFileFactory()->getFileObject($rows[$identifier]['uid'], $rows[$identifier]);
1503  } else {
1504  $fileObject = $this->getFileFactory()->getFileObjectByStorageAndIdentifier($this->getUid(), $identifier);
1505  }
1506  if ($fileObject instanceof FileInterface) {
1507  $key = $fileObject->getName();
1508  while (isset($items[$key])) {
1509  $key .= 'z';
1510  }
1511  $items[$key] = $fileObject;
1512  }
1513  }
1514 
1515  return $items;
1516  }
1517 
1524  public function getFileIdentifiersInFolder($folderIdentifier, $useFilters = true, $recursive = false)
1525  {
1526  $filters = $useFilters == true ? $this->fileAndFolderNameFilters : [];
1527  return $this->driver->getFilesInFolder($folderIdentifier, 0, 0, $recursive, $filters);
1528  }
1529 
1537  public function countFilesInFolder(Folder $folder, $useFilters = true, $recursive = false)
1538  {
1539  $this->assureFolderReadPermission($folder);
1540  $filters = $useFilters ? $this->fileAndFolderNameFilters : [];
1541  return $this->driver->countFilesInFolder($folder->getIdentifier(), $recursive, $filters);
1542  }
1543 
1550  public function getFolderIdentifiersInFolder($folderIdentifier, $useFilters = true, $recursive = false)
1551  {
1552  $filters = $useFilters == true ? $this->fileAndFolderNameFilters : [];
1553  return $this->driver->getFoldersInFolder($folderIdentifier, 0, 0, $recursive, $filters);
1554  }
1555 
1562  public function hasFile($identifier)
1563  {
1564  // Allow if identifier is in processing folder
1565  if (!$this->isWithinProcessingFolder($identifier)) {
1566  $this->assureFolderReadPermission();
1567  }
1568  return $this->driver->fileExists($identifier);
1569  }
1570 
1576  public function getProcessingFolders()
1577  {
1578  if ($this->processingFolders === null) {
1579  $this->processingFolders = [];
1580  $this->processingFolders[] = $this->getProcessingFolder();
1582  $storageRepository = GeneralUtility::makeInstance(StorageRepository::class);
1583  $allStorages = $storageRepository->findAll();
1584  foreach ($allStorages as $storage) {
1585  // To circumvent the permission check of the folder, we use the factory to create it "manually" instead of directly using $storage->getProcessingFolder()
1586  // See #66695 for details
1587  list($storageUid, $processingFolderIdentifier) = GeneralUtility::trimExplode(':', $storage->getStorageRecord()['processingfolder']);
1588  if (empty($processingFolderIdentifier) || (int)$storageUid !== $this->getUid()) {
1589  continue;
1590  }
1591  $potentialProcessingFolder = ResourceFactory::getInstance()->getInstance()->createFolderObject($this, $processingFolderIdentifier, $processingFolderIdentifier);
1592  if ($potentialProcessingFolder->getStorage() === $this && $potentialProcessingFolder->getIdentifier() !== $this->getProcessingFolder()->getIdentifier()) {
1593  $this->processingFolders[] = $potentialProcessingFolder;
1594  }
1595  }
1596  }
1597 
1598  return $this->processingFolders;
1599  }
1600 
1608  public function isProcessingFolder(Folder $folder)
1609  {
1610  $isProcessingFolder = false;
1611  foreach ($this->getProcessingFolders() as $processingFolder) {
1612  if ($folder->getCombinedIdentifier() === $processingFolder->getCombinedIdentifier()) {
1613  $isProcessingFolder = true;
1614  break;
1615  }
1616  }
1617  return $isProcessingFolder;
1618  }
1619 
1627  public function hasFileInFolder($fileName, Folder $folder)
1628  {
1629  $this->assureFolderReadPermission($folder);
1630  return $this->driver->fileExistsInFolder($fileName, $folder->getIdentifier());
1631  }
1632 
1641  public function getFileContents($file)
1642  {
1643  $this->assureFileReadPermission($file);
1644  return $this->driver->getFileContents($file->getIdentifier());
1645  }
1646 
1657  public function dumpFileContents(FileInterface $file, $asDownload = false, $alternativeFilename = null, $overrideMimeType = null)
1658  {
1659  $downloadName = $alternativeFilename ?: $file->getName();
1660  $contentDisposition = $asDownload ? 'attachment' : 'inline';
1661  header('Content-Disposition: ' . $contentDisposition . '; filename="' . $downloadName . '"');
1662  header('Content-Type: ' . ($overrideMimeType ?: $file->getMimeType()));
1663  header('Content-Length: ' . $file->getSize());
1664 
1665  // Cache-Control header is needed here to solve an issue with browser IE8 and lower
1666  // See for more information: http://support.microsoft.com/kb/323308
1667  header("Cache-Control: ''");
1668  header('Last-Modified: ' .
1669  gmdate('D, d M Y H:i:s', array_pop($this->driver->getFileInfoByIdentifier($file->getIdentifier(), ['mtime']))) . ' GMT',
1670  true,
1671  200
1672  );
1673  ob_clean();
1674  flush();
1675  while (ob_get_level() > 0) {
1676  ob_end_clean();
1677  }
1678  $this->driver->dumpFileContents($file->getIdentifier());
1679  }
1680 
1692  public function setFileContents(AbstractFile $file, $contents)
1693  {
1694  // Check if user is allowed to edit
1695  $this->assureFileWritePermissions($file);
1696  // Call driver method to update the file and update file index entry afterwards
1697  $result = $this->driver->setFileContents($file->getIdentifier(), $contents);
1698  if ($file instanceof File) {
1699  $this->getIndexer()->updateIndexEntry($file);
1700  }
1701  $this->emitPostFileSetContentsSignal($file, $contents);
1702  return $result;
1703  }
1704 
1717  public function createFile($fileName, Folder $targetFolderObject)
1718  {
1719  $this->assureFileAddPermissions($targetFolderObject, $fileName);
1720  $newFileIdentifier = $this->driver->createFile($fileName, $targetFolderObject->getIdentifier());
1721  $this->emitPostFileCreateSignal($newFileIdentifier, $targetFolderObject);
1722  return ResourceFactory::getInstance()->getFileObjectByStorageAndIdentifier($this->getUid(), $newFileIdentifier);
1723  }
1724 
1733  public function deleteFile($fileObject)
1734  {
1735  $this->assureFileDeletePermissions($fileObject);
1736 
1737  $this->emitPreFileDeleteSignal($fileObject);
1738 
1739  if ($this->driver->fileExists($fileObject->getIdentifier())) {
1740  $result = $this->driver->deleteFile($fileObject->getIdentifier());
1741  if (!$result) {
1742  throw new Exception\FileOperationErrorException('Deleting the file "' . $fileObject->getIdentifier() . '\' failed.', 1329831691);
1743  }
1744  }
1745  // Mark the file object as deleted
1746  if ($fileObject instanceof File) {
1747  $fileObject->setDeleted();
1748  }
1749 
1750  $this->emitPostFileDeleteSignal($fileObject);
1751 
1752  return true;
1753  }
1754 
1769  public function copyFile(FileInterface $file, Folder $targetFolder, $targetFileName = null, $conflictMode = DuplicationBehavior::RENAME)
1770  {
1771  $conflictMode = DuplicationBehavior::cast($conflictMode);
1772  if ($targetFileName === null) {
1773  $targetFileName = $file->getName();
1774  }
1775  $sanitizedTargetFileName = $this->driver->sanitizeFileName($targetFileName);
1776  $this->assureFileCopyPermissions($file, $targetFolder, $sanitizedTargetFileName);
1777  $this->emitPreFileCopySignal($file, $targetFolder);
1778  // File exists and we should abort, let's abort
1779  if ($conflictMode->equals(DuplicationBehavior::CANCEL) && $targetFolder->hasFile($sanitizedTargetFileName)) {
1780  throw new Exception\ExistingTargetFileNameException('The target file already exists.', 1320291064);
1781  }
1782  // File exists and we should find another name, let's find another one
1783  if ($conflictMode->equals(DuplicationBehavior::RENAME) && $targetFolder->hasFile($sanitizedTargetFileName)) {
1784  $sanitizedTargetFileName = $this->getUniqueName($targetFolder, $sanitizedTargetFileName);
1785  }
1786  $sourceStorage = $file->getStorage();
1787  // Call driver method to create a new file from an existing file object,
1788  // and return the new file object
1789  if ($sourceStorage === $this) {
1790  $newFileObjectIdentifier = $this->driver->copyFileWithinStorage($file->getIdentifier(), $targetFolder->getIdentifier(), $sanitizedTargetFileName);
1791  } else {
1792  $tempPath = $file->getForLocalProcessing();
1793  $newFileObjectIdentifier = $this->driver->addFile($tempPath, $targetFolder->getIdentifier(), $sanitizedTargetFileName);
1794  }
1795  $newFileObject = ResourceFactory::getInstance()->getFileObjectByStorageAndIdentifier($this->getUid(), $newFileObjectIdentifier);
1796  $this->emitPostFileCopySignal($file, $targetFolder);
1797  return $newFileObject;
1798  }
1799 
1815  public function moveFile($file, $targetFolder, $targetFileName = null, $conflictMode = DuplicationBehavior::RENAME)
1816  {
1817  $conflictMode = DuplicationBehavior::cast($conflictMode);
1818  if ($targetFileName === null) {
1819  $targetFileName = $file->getName();
1820  }
1821  $originalFolder = $file->getParentFolder();
1822  $sanitizedTargetFileName = $this->driver->sanitizeFileName($targetFileName);
1823  $this->assureFileMovePermissions($file, $targetFolder, $sanitizedTargetFileName);
1824  if ($targetFolder->hasFile($sanitizedTargetFileName)) {
1825  // File exists and we should abort, let's abort
1826  if ($conflictMode->equals(DuplicationBehavior::RENAME)) {
1827  $sanitizedTargetFileName = $this->getUniqueName($targetFolder, $sanitizedTargetFileName);
1828  } elseif ($conflictMode->equals(DuplicationBehavior::CANCEL)) {
1829  throw new Exception\ExistingTargetFileNameException('The target file already exists', 1329850997);
1830  }
1831  }
1832  $this->emitPreFileMoveSignal($file, $targetFolder);
1833  $sourceStorage = $file->getStorage();
1834  // Call driver method to move the file and update the index entry
1835  try {
1836  if ($sourceStorage === $this) {
1837  $newIdentifier = $this->driver->moveFileWithinStorage($file->getIdentifier(), $targetFolder->getIdentifier(), $sanitizedTargetFileName);
1838  if (!$file instanceof AbstractFile) {
1839  throw new \RuntimeException('The given file is not of type AbstractFile.', 1384209025);
1840  }
1841  $file->updateProperties(['identifier' => $newIdentifier]);
1842  } else {
1843  $tempPath = $file->getForLocalProcessing();
1844  $newIdentifier = $this->driver->addFile($tempPath, $targetFolder->getIdentifier(), $sanitizedTargetFileName);
1845  $sourceStorage->driver->deleteFile($file->getIdentifier());
1846  if ($file instanceof File) {
1847  $file->updateProperties(['storage' => $this->getUid(), 'identifier' => $newIdentifier]);
1848  }
1849  }
1850  $this->getIndexer()->updateIndexEntry($file);
1851  } catch (\TYPO3\CMS\Core\Exception $e) {
1852  echo $e->getMessage();
1853  }
1854  $this->emitPostFileMoveSignal($file, $targetFolder, $originalFolder);
1855  return $file;
1856  }
1857 
1869  public function renameFile($file, $targetFileName)
1870  {
1871  // @todo add $conflictMode setting
1872 
1873  // The name should be different from the current.
1874  if ($file->getName() === $targetFileName) {
1875  return $file;
1876  }
1877  $sanitizedTargetFileName = $this->driver->sanitizeFileName($targetFileName);
1878  $this->assureFileRenamePermissions($file, $sanitizedTargetFileName);
1879  $this->emitPreFileRenameSignal($file, $sanitizedTargetFileName);
1880 
1881  // Call driver method to rename the file and update the index entry
1882  try {
1883  $newIdentifier = $this->driver->renameFile($file->getIdentifier(), $sanitizedTargetFileName);
1884  if ($file instanceof File) {
1885  $file->updateProperties(['identifier' => $newIdentifier]);
1886  }
1887  $this->getIndexer()->updateIndexEntry($file);
1888  } catch (\RuntimeException $e) {
1889  }
1890 
1891  $this->emitPostFileRenameSignal($file, $sanitizedTargetFileName);
1892 
1893  return $file;
1894  }
1895 
1907  public function replaceFile(FileInterface $file, $localFilePath)
1908  {
1909  $this->assureFileReplacePermissions($file);
1910  if (!$this->checkFileExtensionPermission($localFilePath)) {
1911  throw new Exception\IllegalFileExtensionException('Source file extension not allowed.', 1378132239);
1912  }
1913  if (!file_exists($localFilePath)) {
1914  throw new \InvalidArgumentException('File "' . $localFilePath . '" does not exist.', 1325842622);
1915  }
1916  $this->emitPreFileReplaceSignal($file, $localFilePath);
1917  $this->driver->replaceFile($file->getIdentifier(), $localFilePath);
1918  if ($file instanceof File) {
1919  $this->getIndexer()->updateIndexEntry($file);
1920  }
1921  if ($this->autoExtractMetadataEnabled()) {
1922  $indexer = GeneralUtility::makeInstance(Indexer::class, $this);
1923  $indexer->extractMetaData($file);
1924  }
1925  $this->emitPostFileReplaceSignal($file, $localFilePath);
1926 
1927  return $file;
1928  }
1929 
1939  public function addUploadedFile(array $uploadedFileData, Folder $targetFolder = null, $targetFileName = null, $conflictMode = DuplicationBehavior::CANCEL)
1940  {
1941  $conflictMode = DuplicationBehavior::cast($conflictMode);
1942  $localFilePath = $uploadedFileData['tmp_name'];
1943  if ($targetFolder === null) {
1944  $targetFolder = $this->getDefaultFolder();
1945  }
1946  if ($targetFileName === null) {
1947  $targetFileName = $uploadedFileData['name'];
1948  }
1949  $targetFileName = $this->driver->sanitizeFileName($targetFileName);
1950 
1951  $this->assureFileUploadPermissions($localFilePath, $targetFolder, $targetFileName, $uploadedFileData['size']);
1952  if ($this->hasFileInFolder($targetFileName, $targetFolder) && $conflictMode->equals(DuplicationBehavior::REPLACE)) {
1953  $file = $this->getFileInFolder($targetFileName, $targetFolder);
1954  $resultObject = $this->replaceFile($file, $localFilePath);
1955  } else {
1956  $resultObject = $this->addFile($localFilePath, $targetFolder, $targetFileName, (string)$conflictMode);
1957  }
1958  return $resultObject;
1959  }
1960 
1961  /********************
1962  * FOLDER ACTIONS
1963  ********************/
1970  protected function getAllFileObjectsInFolder(Folder $folder)
1971  {
1972  $files = [];
1973  $folderQueue = [$folder];
1974  while (!empty($folderQueue)) {
1975  $folder = array_shift($folderQueue);
1976  foreach ($folder->getSubfolders() as $subfolder) {
1977  $folderQueue[] = $subfolder;
1978  }
1979  foreach ($folder->getFiles() as $file) {
1981  $files[$file->getIdentifier()] = $file;
1982  }
1983  }
1984 
1985  return $files;
1986  }
1987 
2002  public function moveFolder(Folder $folderToMove, Folder $targetParentFolder, $newFolderName = null, $conflictMode = DuplicationBehavior::RENAME)
2003  {
2004  // @todo add tests
2005  $originalFolder = $folderToMove->getParentFolder();
2006  $this->assureFolderMovePermissions($folderToMove, $targetParentFolder);
2007  $sourceStorage = $folderToMove->getStorage();
2008  $returnObject = null;
2009  $sanitizedNewFolderName = $this->driver->sanitizeFileName($newFolderName ?: $folderToMove->getName());
2010  // @todo check if folder already exists in $targetParentFolder, handle this conflict then
2011  $this->emitPreFolderMoveSignal($folderToMove, $targetParentFolder, $sanitizedNewFolderName);
2012  // Get all file objects now so we are able to update them after moving the folder
2013  $fileObjects = $this->getAllFileObjectsInFolder($folderToMove);
2014  if ($sourceStorage === $this) {
2015  if ($this->isWithinFolder($folderToMove, $targetParentFolder)) {
2016  throw new InvalidTargetFolderException(
2017  sprintf(
2018  'Cannot move folder "%s" into target folder "%s", because the target folder is already within the folder to be moved!',
2019  $folderToMove->getName(),
2020  $targetParentFolder->getName()
2021  ),
2022  1422723050
2023  );
2024  }
2025  $fileMappings = $this->driver->moveFolderWithinStorage($folderToMove->getIdentifier(), $targetParentFolder->getIdentifier(), $sanitizedNewFolderName);
2026  } else {
2027  $fileMappings = $this->moveFolderBetweenStorages($folderToMove, $targetParentFolder, $sanitizedNewFolderName);
2028  }
2029  // Update the identifier and storage of all file objects
2030  foreach ($fileObjects as $oldIdentifier => $fileObject) {
2031  $newIdentifier = $fileMappings[$oldIdentifier];
2032  $fileObject->updateProperties(['storage' => $this->getUid(), 'identifier' => $newIdentifier]);
2033  $this->getIndexer()->updateIndexEntry($fileObject);
2034  }
2035  $returnObject = $this->getFolder($fileMappings[$folderToMove->getIdentifier()]);
2036  $this->emitPostFolderMoveSignal($folderToMove, $targetParentFolder, $returnObject->getName(), $originalFolder);
2037  return $returnObject;
2038  }
2039 
2050  protected function moveFolderBetweenStorages(Folder $folderToMove, Folder $targetParentFolder, $newFolderName)
2051  {
2052  throw new \RuntimeException('Not yet implemented', 1476046361);
2053  }
2054 
2065  public function copyFolder(FolderInterface $folderToCopy, FolderInterface $targetParentFolder, $newFolderName = null, $conflictMode = DuplicationBehavior::RENAME)
2066  {
2067  // @todo implement the $conflictMode handling
2068  $this->assureFolderCopyPermissions($folderToCopy, $targetParentFolder);
2069  $returnObject = null;
2070  $sanitizedNewFolderName = $this->driver->sanitizeFileName($newFolderName ?: $folderToCopy->getName());
2071  if ($folderToCopy instanceof Folder && $targetParentFolder instanceof Folder) {
2072  $this->emitPreFolderCopySignal($folderToCopy, $targetParentFolder, $sanitizedNewFolderName);
2073  }
2074  $sourceStorage = $folderToCopy->getStorage();
2075  // call driver method to move the file
2076  // that also updates the file object properties
2077  if ($sourceStorage === $this) {
2078  if ($this->isWithinFolder($folderToCopy, $targetParentFolder)) {
2079  throw new InvalidTargetFolderException(
2080  sprintf(
2081  'Cannot copy folder "%s" into target folder "%s", because the target folder is already within the folder to be copied!',
2082  $folderToCopy->getName(),
2083  $targetParentFolder->getName()
2084  ),
2085  1422723059
2086  );
2087  }
2088  $this->driver->copyFolderWithinStorage($folderToCopy->getIdentifier(), $targetParentFolder->getIdentifier(), $sanitizedNewFolderName);
2089  $returnObject = $this->getFolder($targetParentFolder->getSubfolder($sanitizedNewFolderName)->getIdentifier());
2090  } else {
2091  $this->copyFolderBetweenStorages($folderToCopy, $targetParentFolder, $sanitizedNewFolderName);
2092  }
2093  $this->emitPostFolderCopySignal($folderToCopy, $targetParentFolder, $returnObject->getName());
2094  return $returnObject;
2095  }
2096 
2107  protected function copyFolderBetweenStorages(Folder $folderToCopy, Folder $targetParentFolder, $newFolderName)
2108  {
2109  throw new \RuntimeException('Not yet implemented.', 1476046386);
2110  }
2111 
2121  public function renameFolder($folderObject, $newName)
2122  {
2123 
2124  // Renaming the folder should check if the parent folder is writable
2125  // We cannot do this however because we cannot extract the parent folder from a folder currently
2126  if (!$this->checkFolderActionPermission('rename', $folderObject)) {
2127  throw new Exception\InsufficientUserPermissionsException('You are not allowed to rename the folder "' . $folderObject->getIdentifier() . '\'', 1357811441);
2128  }
2129 
2130  $sanitizedNewName = $this->driver->sanitizeFileName($newName);
2131  $returnObject = null;
2132  if ($this->driver->folderExistsInFolder($sanitizedNewName, $folderObject->getIdentifier())) {
2133  throw new \InvalidArgumentException('The folder ' . $sanitizedNewName . ' already exists in folder ' . $folderObject->getIdentifier(), 1325418870);
2134  }
2135 
2136  $this->emitPreFolderRenameSignal($folderObject, $sanitizedNewName);
2137 
2138  $fileObjects = $this->getAllFileObjectsInFolder($folderObject);
2139  $fileMappings = $this->driver->renameFolder($folderObject->getIdentifier(), $sanitizedNewName);
2140  // Update the identifier of all file objects
2141  foreach ($fileObjects as $oldIdentifier => $fileObject) {
2142  $newIdentifier = $fileMappings[$oldIdentifier];
2143  $fileObject->updateProperties(['identifier' => $newIdentifier]);
2144  $this->getIndexer()->updateIndexEntry($fileObject);
2145  }
2146  $returnObject = $this->getFolder($fileMappings[$folderObject->getIdentifier()]);
2147 
2148  $this->emitPostFolderRenameSignal($folderObject, $returnObject->getName());
2149 
2150  return $returnObject;
2151  }
2152 
2165  public function deleteFolder($folderObject, $deleteRecursively = false)
2166  {
2167  $isEmpty = $this->driver->isFolderEmpty($folderObject->getIdentifier());
2168  $this->assureFolderDeletePermission($folderObject, ($deleteRecursively && !$isEmpty));
2169  if (!$isEmpty && !$deleteRecursively) {
2170  throw new \RuntimeException('Could not delete folder "' . $folderObject->getIdentifier() . '" because it is not empty.', 1325952534);
2171  }
2172 
2173  $this->emitPreFolderDeleteSignal($folderObject);
2174 
2175  foreach ($this->getFilesInFolder($folderObject, 0, 0, false, $deleteRecursively) as $file) {
2176  $this->deleteFile($file);
2177  }
2178 
2179  $result = $this->driver->deleteFolder($folderObject->getIdentifier(), $deleteRecursively);
2180 
2181  $this->emitPostFolderDeleteSignal($folderObject);
2182 
2183  return $result;
2184  }
2185 
2196  public function getFolderInFolder($folderName, Folder $parentFolder, $returnInaccessibleFolderObject = false)
2197  {
2198  $folderIdentifier = $this->driver->getFolderInFolder($folderName, $parentFolder->getIdentifier());
2199  return $this->getFolder($folderIdentifier, $returnInaccessibleFolderObject);
2200  }
2201 
2216  public function getFoldersInFolder(Folder $folder, $start = 0, $maxNumberOfItems = 0, $useFilters = true, $recursive = false, $sort = '', $sortRev = false)
2217  {
2218  $filters = $useFilters == true ? $this->fileAndFolderNameFilters : [];
2219 
2220  $folderIdentifiers = $this->driver->getFoldersInFolder($folder->getIdentifier(), $start, $maxNumberOfItems, $recursive, $filters, $sort, $sortRev);
2221 
2222  // Exclude processing folders
2223  foreach ($this->getProcessingFolders() as $processingFolder) {
2224  $processingIdentifier = $processingFolder->getIdentifier();
2225  if (isset($folderIdentifiers[$processingIdentifier])) {
2226  unset($folderIdentifiers[$processingIdentifier]);
2227  }
2228  }
2229  $folders = [];
2230  foreach ($folderIdentifiers as $folderIdentifier) {
2231  $folders[$folderIdentifier] = $this->getFolder($folderIdentifier, true);
2232  }
2233  return $folders;
2234  }
2235 
2243  public function countFoldersInFolder(Folder $folder, $useFilters = true, $recursive = false)
2244  {
2245  $this->assureFolderReadPermission($folder);
2246  $filters = $useFilters ? $this->fileAndFolderNameFilters : [];
2247  return $this->driver->countFoldersInFolder($folder->getIdentifier(), $recursive, $filters);
2248  }
2249 
2256  public function hasFolder($identifier)
2257  {
2258  $this->assureFolderReadPermission();
2259  return $this->driver->folderExists($identifier);
2260  }
2261 
2269  public function hasFolderInFolder($folderName, Folder $folder)
2270  {
2271  $this->assureFolderReadPermission($folder);
2272  return $this->driver->folderExistsInFolder($folderName, $folder->getIdentifier());
2273  }
2274 
2288  public function createFolder($folderName, Folder $parentFolder = null)
2289  {
2290  if ($parentFolder === null) {
2291  $parentFolder = $this->getRootLevelFolder();
2292  } elseif (!$this->driver->folderExists($parentFolder->getIdentifier())) {
2293  throw new \InvalidArgumentException('Parent folder "' . $parentFolder->getIdentifier() . '" does not exist.', 1325689164);
2294  }
2295  if (!$this->checkFolderActionPermission('add', $parentFolder)) {
2296  throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to create directories in the folder "' . $parentFolder->getIdentifier() . '"', 1323059807);
2297  }
2298  if ($this->driver->folderExistsInFolder($folderName, $parentFolder->getIdentifier())) {
2299  throw new Exception\ExistingTargetFolderException('Folder "' . $folderName . '" already exists.', 1423347324);
2300  }
2301 
2302  $this->emitPreFolderAddSignal($parentFolder, $folderName);
2303 
2304  $newFolder = $this->getDriver()->createFolder($folderName, $parentFolder->getIdentifier(), true);
2305  $newFolder = $this->getFolder($newFolder);
2306 
2307  $this->emitPostFolderAddSignal($newFolder);
2308 
2309  return $newFolder;
2310  }
2311 
2318  public function getFolderInfo(Folder $folder)
2319  {
2320  return $this->driver->getFolderInfoByIdentifier($folder->getIdentifier());
2321  }
2322 
2328  public function getDefaultFolder()
2329  {
2330  return $this->getFolder($this->driver->getDefaultFolder());
2331  }
2332 
2341  public function getFolder($identifier, $returnInaccessibleFolderObject = false)
2342  {
2343  $data = $this->driver->getFolderInfoByIdentifier($identifier);
2344  $folder = ResourceFactory::getInstance()->createFolderObject($this, $data['identifier'], $data['name']);
2345 
2346  try {
2347  $this->assureFolderReadPermission($folder);
2348  } catch (Exception\InsufficientFolderAccessPermissionsException $e) {
2349  $folder = null;
2350  if ($returnInaccessibleFolderObject) {
2351  // if parent folder is readable return inaccessible folder object
2352  $parentPermissions = $this->driver->getPermissions($this->driver->getParentFolderIdentifierOfIdentifier($identifier));
2353  if ($parentPermissions['r']) {
2354  $folder = GeneralUtility::makeInstance(
2355  InaccessibleFolder::class, $this, $data['identifier'], $data['name']
2356  );
2357  }
2358  }
2359 
2360  if ($folder === null) {
2361  throw $e;
2362  }
2363  }
2364  return $folder;
2365  }
2366 
2373  public function isWithinProcessingFolder($identifier)
2374  {
2375  $inProcessingFolder = false;
2376  foreach ($this->getProcessingFolders() as $processingFolder) {
2377  if ($this->driver->isWithin($processingFolder->getIdentifier(), $identifier)) {
2378  $inProcessingFolder = true;
2379  break;
2380  }
2381  }
2382  return $inProcessingFolder;
2383  }
2384 
2393  public function isWithinFolder(Folder $folder, ResourceInterface $resource)
2394  {
2395  if ($folder->getStorage() !== $this) {
2396  throw new \InvalidArgumentException('Given folder "' . $folder->getIdentifier() . '" is not part of this storage!', 1422709241);
2397  }
2398  if ($folder->getStorage() !== $resource->getStorage()) {
2399  return false;
2400  }
2401  return $this->driver->isWithin($folder->getIdentifier(), $resource->getIdentifier());
2402  }
2403 
2412  public function getRootLevelFolder($respectFileMounts = true)
2413  {
2414  if ($respectFileMounts && !empty($this->fileMounts)) {
2415  $mount = reset($this->fileMounts);
2416  return $mount['folder'];
2417  } else {
2418  return ResourceFactory::getInstance()->createFolderObject($this, $this->driver->getRootLevelFolder(), '');
2419  }
2420  }
2421 
2429  protected function emitSanitizeFileNameSignal($fileName, Folder $targetFolder)
2430  {
2431  list($fileName) = $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_SanitizeFileName, [$fileName, $targetFolder, $this, $this->driver]);
2432  return $fileName;
2433  }
2434 
2443  protected function emitPreFileAddSignal($targetFileName, Folder $targetFolder, $sourceFilePath)
2444  {
2445  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PreFileAdd, [&$targetFileName, $targetFolder, $sourceFilePath, $this, $this->driver]);
2446  return $targetFileName;
2447  }
2448 
2456  protected function emitPostFileAddSignal(FileInterface $file, Folder $targetFolder)
2457  {
2458  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PostFileAdd, [$file, $targetFolder]);
2459  }
2460 
2468  protected function emitPreFileCopySignal(FileInterface $file, Folder $targetFolder)
2469  {
2470  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PreFileCopy, [$file, $targetFolder]);
2471  }
2472 
2480  protected function emitPostFileCopySignal(FileInterface $file, Folder $targetFolder)
2481  {
2482  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PostFileCopy, [$file, $targetFolder]);
2483  }
2484 
2492  protected function emitPreFileMoveSignal(FileInterface $file, Folder $targetFolder)
2493  {
2494  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PreFileMove, [$file, $targetFolder]);
2495  }
2496 
2505  protected function emitPostFileMoveSignal(FileInterface $file, Folder $targetFolder, FolderInterface $originalFolder)
2506  {
2507  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PostFileMove, [$file, $targetFolder, $originalFolder]);
2508  }
2509 
2517  protected function emitPreFileRenameSignal(FileInterface $file, $targetFolder)
2518  {
2519  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PreFileRename, [$file, $targetFolder]);
2520  }
2521 
2529  protected function emitPostFileRenameSignal(FileInterface $file, $sanitizedTargetFileName)
2530  {
2531  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PostFileRename, [$file, $sanitizedTargetFileName]);
2532  }
2533 
2541  protected function emitPreFileReplaceSignal(FileInterface $file, $localFilePath)
2542  {
2543  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PreFileReplace, [$file, $localFilePath]);
2544  }
2545 
2553  protected function emitPostFileReplaceSignal(FileInterface $file, $localFilePath)
2554  {
2555  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PostFileReplace, [$file, $localFilePath]);
2556  }
2557 
2564  protected function emitPostFileCreateSignal($newFileIdentifier, Folder $targetFolder)
2565  {
2566  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PostFileCreate, [$newFileIdentifier, $targetFolder]);
2567  }
2568 
2575  protected function emitPreFileDeleteSignal(FileInterface $file)
2576  {
2577  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PreFileDelete, [$file]);
2578  }
2579 
2586  protected function emitPostFileDeleteSignal(FileInterface $file)
2587  {
2588  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PostFileDelete, [$file]);
2589  }
2590 
2598  protected function emitPostFileSetContentsSignal(FileInterface $file, $content)
2599  {
2600  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PostFileSetContents, [$file, $content]);
2601  }
2602 
2610  protected function emitPreFolderAddSignal(Folder $targetFolder, $name)
2611  {
2612  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PreFolderAdd, [$targetFolder, $name]);
2613  }
2614 
2621  protected function emitPostFolderAddSignal(Folder $folder)
2622  {
2623  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PostFolderAdd, [$folder]);
2624  }
2625 
2634  protected function emitPreFolderCopySignal(Folder $folder, Folder $targetFolder, $newName)
2635  {
2636  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PreFolderCopy, [$folder, $targetFolder, $newName]);
2637  }
2638 
2647  protected function emitPostFolderCopySignal(Folder $folder, Folder $targetFolder, $newName)
2648  {
2649  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PostFolderCopy, [$folder, $targetFolder, $newName]);
2650  }
2651 
2660  protected function emitPreFolderMoveSignal(Folder $folder, Folder $targetFolder, $newName)
2661  {
2662  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PreFolderMove, [$folder, $targetFolder, $newName]);
2663  }
2664 
2674  protected function emitPostFolderMoveSignal(Folder $folder, Folder $targetFolder, $newName, Folder $originalFolder)
2675  {
2676  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PostFolderMove, [$folder, $targetFolder, $newName, $originalFolder]);
2677  }
2678 
2686  protected function emitPreFolderRenameSignal(Folder $folder, $newName)
2687  {
2688  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PreFolderRename, [$folder, $newName]);
2689  }
2690 
2698  protected function emitPostFolderRenameSignal(Folder $folder, $newName)
2699  {
2700  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PostFolderRename, [$folder, $newName]);
2701  }
2702 
2709  protected function emitPreFolderDeleteSignal(Folder $folder)
2710  {
2711  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PreFolderDelete, [$folder]);
2712  }
2713 
2720  protected function emitPostFolderDeleteSignal(Folder $folder)
2721  {
2722  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PostFolderDelete, [$folder]);
2723  }
2724 
2732  protected function emitPreGeneratePublicUrlSignal(ResourceInterface $resourceObject, $relativeToCurrentScript, array $urlData)
2733  {
2734  $this->getSignalSlotDispatcher()->dispatch(self::class, self::SIGNAL_PreGeneratePublicUrl, [$this, $this->driver, $resourceObject, $relativeToCurrentScript, $urlData]);
2735  }
2736 
2750  protected function getUniqueName(Folder $folder, $theFile, $dontCheckForUnique = false)
2751  {
2752  static $maxNumber = 99, $uniqueNamePrefix = '';
2753  // Fetches info about path, name, extension of $theFile
2754  $origFileInfo = PathUtility::pathinfo($theFile);
2755  // Adds prefix
2756  if ($uniqueNamePrefix) {
2757  $origFileInfo['basename'] = $uniqueNamePrefix . $origFileInfo['basename'];
2758  $origFileInfo['filename'] = $uniqueNamePrefix . $origFileInfo['filename'];
2759  }
2760  // Check if the file exists and if not - return the fileName...
2761  // The destinations file
2762  $theDestFile = $origFileInfo['basename'];
2763  // If the file does NOT exist we return this fileName
2764  if (!$this->driver->fileExistsInFolder($theDestFile, $folder->getIdentifier()) || $dontCheckForUnique) {
2765  return $theDestFile;
2766  }
2767  // Well the fileName in its pure form existed. Now we try to append
2768  // numbers / unique-strings and see if we can find an available fileName
2769  // This removes _xx if appended to the file
2770  $theTempFileBody = preg_replace('/_[0-9][0-9]$/', '', $origFileInfo['filename']);
2771  $theOrigExt = $origFileInfo['extension'] ? '.' . $origFileInfo['extension'] : '';
2772  for ($a = 1; $a <= $maxNumber + 1; $a++) {
2773  // First we try to append numbers
2774  if ($a <= $maxNumber) {
2775  $insert = '_' . sprintf('%02d', $a);
2776  } else {
2777  $insert = '_' . substr(md5(uniqid('', true)), 0, 6);
2778  }
2779  $theTestFile = $theTempFileBody . $insert . $theOrigExt;
2780  // The destinations file
2781  $theDestFile = $theTestFile;
2782  // If the file does NOT exist we return this fileName
2783  if (!$this->driver->fileExistsInFolder($theDestFile, $folder->getIdentifier())) {
2784  return $theDestFile;
2785  }
2786  }
2787  throw new \RuntimeException('Last possible name "' . $theDestFile . '" is already taken.', 1325194291);
2788  }
2789 
2795  protected function getSignalSlotDispatcher()
2796  {
2797  if (!isset($this->signalSlotDispatcher)) {
2798  $this->signalSlotDispatcher = $this->getObjectManager()->get(Dispatcher::class);
2799  }
2801  }
2802 
2808  protected function getObjectManager()
2809  {
2810  return GeneralUtility::makeInstance(ObjectManager::class);
2811  }
2812 
2816  protected function getFileFactory()
2817  {
2818  return GeneralUtility::makeInstance(ResourceFactory::class);
2819  }
2820 
2824  protected function getFileIndexRepository()
2825  {
2827  }
2828 
2832  protected function getFileProcessingService()
2833  {
2834  if (!$this->fileProcessingService) {
2835  $this->fileProcessingService = GeneralUtility::makeInstance(Service\FileProcessingService::class, $this, $this->driver);
2836  }
2838  }
2839 
2846  public function getRole(FolderInterface $folder)
2847  {
2848  $folderRole = FolderInterface::ROLE_DEFAULT;
2849  $identifier = $folder->getIdentifier();
2850  if (method_exists($this->driver, 'getRole')) {
2851  $folderRole = $this->driver->getRole($folder->getIdentifier());
2852  }
2853  if (isset($this->fileMounts[$identifier])) {
2854  $folderRole = FolderInterface::ROLE_MOUNT;
2855 
2856  if (!empty($this->fileMounts[$identifier]['read_only'])) {
2858  }
2859  if ($this->fileMounts[$identifier]['user_mount']) {
2860  $folderRole = FolderInterface::ROLE_USER_MOUNT;
2861  }
2862  }
2863  if ($folder instanceof Folder && $this->isProcessingFolder($folder)) {
2864  $folderRole = FolderInterface::ROLE_PROCESSING;
2865  }
2866 
2867  return $folderRole;
2868  }
2869 
2877  public function getProcessingFolder(File $file = null)
2878  {
2879  if (!isset($this->processingFolder)) {
2880  $processingFolder = self::DEFAULT_ProcessingFolder;
2881  if (!empty($this->storageRecord['processingfolder'])) {
2882  $processingFolder = $this->storageRecord['processingfolder'];
2883  }
2884  try {
2885  if (strpos($processingFolder, ':') !== false) {
2886  list($storageUid, $processingFolderIdentifier) = explode(':', $processingFolder, 2);
2887  $storage = ResourceFactory::getInstance()->getStorageObject($storageUid);
2888  if ($storage->hasFolder($processingFolderIdentifier)) {
2889  $this->processingFolder = $storage->getFolder($processingFolderIdentifier);
2890  } else {
2891  $rootFolder = $storage->getRootLevelFolder(false);
2892  $currentEvaluatePermissions = $storage->getEvaluatePermissions();
2893  $storage->setEvaluatePermissions(false);
2894  $this->processingFolder = $storage->createFolder(
2895  ltrim($processingFolderIdentifier, '/'),
2896  $rootFolder
2897  );
2898  $storage->setEvaluatePermissions($currentEvaluatePermissions);
2899  }
2900  } else {
2901  if ($this->driver->folderExists($processingFolder) === false) {
2902  $rootFolder = $this->getRootLevelFolder(false);
2903  $currentEvaluatePermissions = $this->evaluatePermissions;
2904  $this->evaluatePermissions = false;
2905  $this->processingFolder = $this->createFolder(
2907  $rootFolder
2908  );
2909  $this->evaluatePermissions = $currentEvaluatePermissions;
2910  } else {
2911  $data = $this->driver->getFolderInfoByIdentifier($processingFolder);
2912  $this->processingFolder = ResourceFactory::getInstance()->createFolderObject($this, $data['identifier'], $data['name']);
2913  }
2914  }
2915  } catch (Exception\InsufficientFolderWritePermissionsException $e) {
2916  $this->processingFolder = GeneralUtility::makeInstance(
2917  InaccessibleFolder::class, $this, $processingFolder, $processingFolder
2918  );
2919  } catch (Exception\ResourcePermissionsUnavailableException $e) {
2920  $this->processingFolder = GeneralUtility::makeInstance(
2921  InaccessibleFolder::class, $this, $processingFolder, $processingFolder
2922  );
2923  }
2924  }
2925 
2927  if (!empty($file)) {
2929  }
2930  return $processingFolder;
2931  }
2932 
2942  protected function getNestedProcessingFolder(File $file, Folder $rootProcessingFolder)
2943  {
2944  $processingFolder = $rootProcessingFolder;
2945  $nestedFolderNames = $this->getNamesForNestedProcessingFolder(
2946  $file->getIdentifier(),
2947  self::PROCESSING_FOLDER_LEVELS
2948  );
2949 
2950  try {
2951  foreach ($nestedFolderNames as $folderName) {
2952  if ($processingFolder->hasFolder($folderName)) {
2953  $processingFolder = $processingFolder->getSubfolder($folderName);
2954  } else {
2955  $currentEvaluatePermissions = $processingFolder->getStorage()->getEvaluatePermissions();
2956  $processingFolder->getStorage()->setEvaluatePermissions(false);
2957  $processingFolder = $processingFolder->createFolder($folderName);
2958  $processingFolder->getStorage()->setEvaluatePermissions($currentEvaluatePermissions);
2959  }
2960  }
2961  } catch (Exception\FolderDoesNotExistException $e) {
2962  }
2963 
2964  return $processingFolder;
2965  }
2966 
2974  protected function getNamesForNestedProcessingFolder($fileIdentifier, $levels)
2975  {
2976  $names = [];
2977  if ($levels === 0) {
2978  return $names;
2979  }
2980  $hash = md5($fileIdentifier);
2981  for ($i = 1; $i <= $levels; $i++) {
2982  $names[] = substr($hash, $i, 1);
2983  }
2984  return $names;
2985  }
2986 
2992  public function getDriverType()
2993  {
2994  return $this->storageRecord['driver'];
2995  }
2996 
3002  protected function getIndexer()
3003  {
3004  return GeneralUtility::makeInstance(Index\Indexer::class, $this);
3005  }
3006 
3011  public function setDefault($isDefault)
3012  {
3013  $this->isDefault = (bool)$isDefault;
3014  }
3015 
3019  public function isDefault()
3020  {
3021  return $this->isDefault;
3022  }
3023 
3029  protected function getBackendUser()
3030  {
3031  return $GLOBALS['BE_USER'];
3032  }
3033 
3037  protected function getLogger()
3038  {
3040  $logManager = GeneralUtility::makeInstance(
3041  \TYPO3\CMS\Core\Log\LogManager::class
3042  );
3043  return $logManager->getLogger(get_class($this));
3044  }
3045 }
getNestedProcessingFolder(File $file, Folder $rootProcessingFolder)
setFileContents(AbstractFile $file, $contents)
emitPreFileCopySignal(FileInterface $file, Folder $targetFolder)
getFilesInFolder(Folder $folder, $start=0, $maxNumberOfItems=0, $useFilters=true, $recursive=false, $sort= '', $sortRev=false)
getFileForLocalProcessing(FileInterface $fileObject, $writable=true)
emitPostFileSetContentsSignal(FileInterface $file, $content)
getFileInFolder($fileName, Folder $folder)
deleteFolder($folderObject, $deleteRecursively=false)
updateProcessedFile($localFilePath, ProcessedFile $processedFile, Folder $processingFolder=null)
countFilesInFolder(Folder $folder, $useFilters=true, $recursive=false)
assureFileCopyPermissions(FileInterface $file, Folder $targetFolder, $targetFileName)
getFileIdentifiersInFolder($folderIdentifier, $useFilters=true, $recursive=false)
emitPreFolderMoveSignal(Folder $folder, Folder $targetFolder, $newName)
processFile(FileInterface $fileObject, $context, array $configuration)
assureFolderMovePermissions(FolderInterface $folderToMove, FolderInterface $targetParentFolder)
emitPostFileAddSignal(FileInterface $file, Folder $targetFolder)
getSubfolders($start=0, $numberOfItems=0, $filterMode=self::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, $recursive=false)
emitPreFileMoveSignal(FileInterface $file, Folder $targetFolder)
getFiles($start=0, $numberOfItems=0, $filterMode=self::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, $recursive=false, $sort= '', $sortRev=false)
getFolderInFolder($folderName, Folder $parentFolder, $returnInaccessibleFolderObject=false)
emitPreFolderRenameSignal(Folder $folder, $newName)
emitPostFileRenameSignal(FileInterface $file, $sanitizedTargetFileName)
copyFolderBetweenStorages(Folder $folderToCopy, Folder $targetParentFolder, $newFolderName)
getUniqueName(Folder $folder, $theFile, $dontCheckForUnique=false)
checkFileActionPermission($action, FileInterface $file)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
static getRelativePathTo($targetPath)
Definition: PathUtility.php:29
assureFileAddPermissions($targetFolder, $targetFileName)
dumpFileContents(FileInterface $file, $asDownload=false, $alternativeFilename=null, $overrideMimeType=null)
getFolder($identifier, $returnInaccessibleFolderObject=false)
getFileInfoByIdentifier($identifier, array $propertiesToExtract=[])
createFile($fileName, Folder $targetFolderObject)
isWithinFolder(Folder $folder, ResourceInterface $resource)
assureFileDeletePermissions(FileInterface $file)
emitPostFolderRenameSignal(Folder $folder, $newName)
assureFolderDeletePermission(Folder $folder, $checkDeleteRecursively)
moveFolder(Folder $folderToMove, Folder $targetParentFolder, $newFolderName=null, $conflictMode=DuplicationBehavior::RENAME)
getRootLevelFolder($respectFileMounts=true)
moveFolderBetweenStorages(Folder $folderToMove, Folder $targetParentFolder, $newFolderName)
emitPostFolderMoveSignal(Folder $folder, Folder $targetFolder, $newName, Folder $originalFolder)
emitPreFolderAddSignal(Folder $targetFolder, $name)
assureFileUploadPermissions($localFilePath, $targetFolder, $targetFileName, $uploadedFileSize)
setUserPermissions(array $userPermissions)
countFoldersInFolder(Folder $folder, $useFilters=true, $recursive=false)
addUploadedFile(array $uploadedFileData, Folder $targetFolder=null, $targetFileName=null, $conflictMode=DuplicationBehavior::CANCEL)
assureFileReplacePermissions(FileInterface $file)
assureFolderCopyPermissions(FolderInterface $folderToCopy, FolderInterface $targetParentFolder)
static verifyFilenameAgainstDenyPattern($filename)
hashFileByIdentifier($fileIdentifier, $hash)
replaceFile(FileInterface $file, $localFilePath)
copyFolder(FolderInterface $folderToCopy, FolderInterface $targetParentFolder, $newFolderName=null, $conflictMode=DuplicationBehavior::RENAME)
emitSanitizeFileNameSignal($fileName, Folder $targetFolder)
emitPostFolderCopySignal(Folder $folder, Folder $targetFolder, $newName)
addFileMount($folderIdentifier, $additionalData=[])
emitPostFileReplaceSignal(FileInterface $file, $localFilePath)
__construct(Driver\DriverInterface $driver, array $storageRecord)
addFile($localFilePath, Folder $targetFolder, $targetFileName= '', $conflictMode=DuplicationBehavior::RENAME, $removeOriginal=true)
emitPreFolderCopySignal(Folder $folder, Folder $targetFolder, $newName)
emitPreFileRenameSignal(FileInterface $file, $targetFolder)
static uniqueList($in_list, $secondParameter=null)
getNamesForNestedProcessingFolder($fileIdentifier, $levels)
getFolderIdentifiersInFolder($folderIdentifier, $useFilters=true, $recursive=false)
emitPreFileReplaceSignal(FileInterface $file, $localFilePath)
emitPreGeneratePublicUrlSignal(ResourceInterface $resourceObject, $relativeToCurrentScript, array $urlData)
assureFileMovePermissions(FileInterface $file, Folder $targetFolder, $targetFileName)
hashFile(FileInterface $fileObject, $hash)
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static pathinfo($path, $options=null)
createFolder($folderName, Folder $parentFolder=null)
static makeInstance($className,...$constructorArguments)
setDriver(Driver\DriverInterface $driver)
hasFileInFolder($fileName, Folder $folder)
hasFolderInFolder($folderName, Folder $folder)
moveFile($file, $targetFolder, $targetFileName=null, $conflictMode=DuplicationBehavior::RENAME)
emitPostFileMoveSignal(FileInterface $file, Folder $targetFolder, FolderInterface $originalFolder)
static hmac($input, $additionalSecret= '')
assureFileRenamePermissions(FileInterface $file, $targetFileName)
getFileInfo(FileInterface $fileObject)
emitPostFileCopySignal(FileInterface $file, Folder $targetFolder)
getFoldersInFolder(Folder $folder, $start=0, $maxNumberOfItems=0, $useFilters=true, $recursive=false, $sort= '', $sortRev=false)
sanitizeFileName($fileName, Folder $targetFolder=null)
getPublicUrl(ResourceInterface $resourceObject, $relativeToCurrentScript=false)
emitPostFileCreateSignal($newFileIdentifier, Folder $targetFolder)
emitPreFileAddSignal($targetFileName, Folder $targetFolder, $sourceFilePath)
checkFolderActionPermission($action, Folder $folder=null)