‪TYPO3CMS  10.4
UploadedFileReferenceConverter.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
24 use ‪TYPO3\CMS\Core\Resource\FileReference as CoreFileReference;
40 
48 {
49 
54 
59 
64 
69 
73  protected ‪$defaultUploadFolder = '1:/user_upload/';
74 
80  protected ‪$defaultConflictMode = 'rename';
81 
85  protected ‪$sourceTypes = ['array'];
86 
90  protected ‪$targetType = PseudoFileReference::class;
91 
97  protected ‪$priority = 12;
98 
102  protected ‪$convertedResources = [];
103 
107  protected ‪$resourceFactory;
108 
112  protected ‪$hashService;
113 
117  protected ‪$persistenceManager;
118 
124  {
125  $this->resourceFactory = ‪$resourceFactory;
126  }
127 
133  {
134  $this->hashService = ‪$hashService;
135  }
136 
142  {
143  $this->persistenceManager = ‪$persistenceManager;
144  }
145 
157  public function ‪convertFrom($source, ‪$targetType, array $convertedChildProperties = [], PropertyMappingConfigurationInterface $configuration = null)
158  {
159  // slot/listener using `FileDumpController` instead of direct public URL in (later) rendering process
160  $resourcePublicationSlot = GeneralUtility::makeInstance(ResourcePublicationSlot::class);
161  if (!isset($source['error']) || $source['error'] === \UPLOAD_ERR_NO_FILE) {
162  if (isset($source['submittedFile']['resourcePointer'])) {
163  try {
164  // File references use numeric resource pointers, direct
165  // file relations are using "file:" prefix (e.g. "file:5")
166  $resourcePointer = $this->hashService->validateAndStripHmac($source['submittedFile']['resourcePointer']);
167  if (strpos($resourcePointer, 'file:') === 0) {
168  $fileUid = (int)substr($resourcePointer, 5);
169  $resource = $this->‪createFileReferenceFromFalFileObject($this->resourceFactory->getFileObject($fileUid));
170  } else {
172  $this->resourceFactory->getFileReferenceObject($resourcePointer),
173  (int)$resourcePointer
174  );
175  }
176  $resourcePublicationSlot->add($resource->getOriginalResource()->getOriginalFile());
177  return $resource;
178  } catch (\InvalidArgumentException $e) {
179  // Nothing to do. No file is uploaded and resource pointer is invalid. Discard!
180  }
181  }
182  return null;
183  }
184 
185  if ($source['error'] !== \UPLOAD_ERR_OK) {
186  return $this->objectManager->get(Error::class, $this->‪getUploadErrorMessage($source['error']), 1471715915);
187  }
188 
189  if (isset($this->convertedResources[$source['tmp_name']])) {
190  return $this->convertedResources[$source['tmp_name']];
191  }
192 
193  if ($configuration === null) {
194  throw new \InvalidArgumentException('Argument $configuration must not be null', 1589183114);
195  }
196 
197  try {
198  $resource = $this->‪importUploadedResource($source, $configuration);
199  $resourcePublicationSlot->add($resource->getOriginalResource()->getOriginalFile());
200  } catch (TypeConverterException $e) {
201  return $e->getError();
202  } catch (\Exception $e) {
203  return $this->objectManager->get(Error::class, $e->getMessage(), $e->getCode());
204  }
205 
206  $this->convertedResources[$source['tmp_name']] = $resource;
207  return $resource;
208  }
209 
217  protected function ‪importUploadedResource(
218  array $uploadInfo,
221  if (!GeneralUtility::makeInstance(FileNameValidator::class)->isValid($uploadInfo['name'])) {
222  throw new ‪TypeConverterException('Uploading files with PHP file extensions is not allowed!', 1471710357);
223  }
224  // `CONFIGURATION_UPLOAD_SEED` is expected to be defined
225  // if it's not given any random seed is generated, instead of throwing an exception
226  $seed = $configuration->‪getConfigurationValue(self::class, self::CONFIGURATION_UPLOAD_SEED)
227  ?: GeneralUtility::makeInstance(Random::class)->generateRandomHexString(40);
228  $uploadFolderId = $configuration->‪getConfigurationValue(self::class, self::CONFIGURATION_UPLOAD_FOLDER) ?: ‪$this->defaultUploadFolder;
229  $conflictMode = $configuration->‪getConfigurationValue(self::class, self::CONFIGURATION_UPLOAD_CONFLICT_MODE) ?: ‪$this->defaultConflictMode;
230  $pseudoFile = GeneralUtility::makeInstance(PseudoFile::class, $uploadInfo);
231 
232  $validators = $configuration->‪getConfigurationValue(self::class, self::CONFIGURATION_FILE_VALIDATORS);
233  if (is_array($validators)) {
234  foreach ($validators as ‪$validator) {
235  if (‪$validator instanceof AbstractValidator) {
236  $validationResult = ‪$validator->validate($pseudoFile);
237  if ($validationResult->hasErrors()) {
238  throw ‪TypeConverterException::fromError($validationResult->getErrors()[0]);
239  }
240  }
241  }
242  }
243 
244  $uploadFolder = $this->‪provideUploadFolder($uploadFolderId);
245  // current folder name, derived from public random seed (`formSession`)
246  $currentName = 'form_' . GeneralUtility::hmac($seed, self::class);
247  $uploadFolder = $this->‪provideTargetFolder($uploadFolder, $currentName);
248  // sub-folder in $uploadFolder with 160 bit of derived entropy (.../form_<40-chars-hash>/actual.file)
249  $uploadedFile = $uploadFolder->addUploadedFile($uploadInfo, $conflictMode);
250 
251  $resourcePointer = isset($uploadInfo['submittedFile']['resourcePointer']) && strpos($uploadInfo['submittedFile']['resourcePointer'], 'file:') === false
252  ? (int)$this->hashService->validateAndStripHmac($uploadInfo['submittedFile']['resourcePointer'])
253  : null;
254 
255  $fileReferenceModel = $this->‪createFileReferenceFromFalFileObject($uploadedFile, $resourcePointer);
256 
257  return $fileReferenceModel;
258  }
259 
265  protected function ‪createFileReferenceFromFalFileObject(
266  File $file,
267  int $resourcePointer = null
269  $fileReference = $this->resourceFactory->createFileReferenceObject(
270  [
271  'uid_local' => $file->getUid(),
272  'uid_foreign' => ‪StringUtility::getUniqueId('NEW_'),
273  'uid' => ‪StringUtility::getUniqueId('NEW_'),
274  'crop' => null,
275  ]
276  );
277  return $this->‪createFileReferenceFromFalFileReferenceObject($fileReference, $resourcePointer);
278  }
279 
290  CoreFileReference $falFileReference,
291  int $resourcePointer = null
292  ): PseudoFileReference {
293  if ($resourcePointer === null) {
294  $fileReference = $this->objectManager->get(PseudoFileReference::class);
295  } else {
296  $fileReference = $this->persistenceManager->getObjectByIdentifier($resourcePointer, PseudoFileReference::class, false);
297  }
298 
299  $fileReference->setOriginalResource($falFileReference);
300  return $fileReference;
301  }
302 
310  protected function ‪getUploadErrorMessage(int $errorCode): string
311  {
312  $logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(static::class);
313  switch ($errorCode) {
314  case \UPLOAD_ERR_INI_SIZE:
315  $logger->error('The uploaded file exceeds the upload_max_filesize directive in php.ini.', []);
316  return ‪TranslationService::getInstance()->‪translate('upload.error.150530345', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
317  case \UPLOAD_ERR_FORM_SIZE:
318  $logger->error('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.', []);
319  return ‪TranslationService::getInstance()->‪translate('upload.error.150530345', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
320  case \UPLOAD_ERR_PARTIAL:
321  $logger->error('The uploaded file was only partially uploaded.', []);
322  return ‪TranslationService::getInstance()->‪translate('upload.error.150530346', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
323  case \UPLOAD_ERR_NO_FILE:
324  $logger->error('No file was uploaded.', []);
325  return ‪TranslationService::getInstance()->‪translate('upload.error.150530347', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
326  case \UPLOAD_ERR_NO_TMP_DIR:
327  $logger->error('Missing a temporary folder.', []);
328  return ‪TranslationService::getInstance()->‪translate('upload.error.150530348', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
329  case \UPLOAD_ERR_CANT_WRITE:
330  $logger->error('Failed to write file to disk.', []);
331  return ‪TranslationService::getInstance()->‪translate('upload.error.150530348', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
332  case \UPLOAD_ERR_EXTENSION:
333  $logger->error('File upload stopped by extension.', []);
334  return ‪TranslationService::getInstance()->‪translate('upload.error.150530348', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
335  default:
336  $logger->error('Unknown upload error.', []);
337  return ‪TranslationService::getInstance()->‪translate('upload.error.150530348', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
338  }
339  }
340 
347  protected function ‪provideUploadFolder(string $uploadFolderIdentifier): Folder
348  {
349  try {
350  return $this->resourceFactory->getFolderObjectFromCombinedIdentifier($uploadFolderIdentifier);
351  } catch (FolderDoesNotExistException $exception) {
352  [$storageId, $storagePath] = explode(':', $uploadFolderIdentifier, 2);
353  $storage = $this->resourceFactory->getStorageObject($storageId);
354  $folderNames = ‪GeneralUtility::trimExplode('/', $storagePath, true);
355  $uploadFolder = $this->‪provideTargetFolder($storage->getRootLevelFolder(), ...$folderNames);
356  $this->‪provideFolderInitialization($uploadFolder);
357  return $uploadFolder;
358  }
359  }
360 
368  protected function ‪provideTargetFolder(‪Folder $parentFolder, string $folderName): ‪Folder
369  {
370  return $parentFolder->‪hasFolder($folderName)
371  ? $parentFolder->‪getSubfolder($folderName)
372  : $parentFolder->‪createFolder($folderName);
373  }
374 
381  protected function ‪provideFolderInitialization(‪Folder $parentFolder): void
382  {
383  if (!$parentFolder->‪hasFile('index.html')) {
384  $parentFolder->‪createFile('index.html');
385  }
386  }
387 }
‪TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface\getConfigurationValue
‪mixed getConfigurationValue($typeConverterClassName, $key)
‪TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator
Definition: AbstractValidator.php:27
‪TYPO3\CMS\Extbase\Domain\Model\FileReference\setOriginalResource
‪setOriginalResource(ResourceInterface $originalResource)
Definition: FileReference.php:39
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter
Definition: UploadedFileReferenceConverter.php:48
‪TYPO3\CMS\Form\Mvc\Property\Exception\TypeConverterException\getError
‪getError()
Definition: TypeConverterException.php:39
‪TYPO3\CMS\Form\Service\TranslationService\getInstance
‪static TranslationService getInstance()
Definition: TranslationService.php:83
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$targetType
‪string $targetType
Definition: UploadedFileReferenceConverter.php:86
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\PseudoFileReference
Definition: PseudoFileReference.php:36
‪TYPO3\CMS\Form\Mvc\Property\Exception\TypeConverterException\fromError
‪static fromError(Error $error)
Definition: TypeConverterException.php:29
‪TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface
Definition: PersistenceManagerInterface.php:22
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$priority
‪int $priority
Definition: UploadedFileReferenceConverter.php:92
‪TYPO3\CMS\Form\Service\TranslationService
Definition: TranslationService.php:44
‪TYPO3\CMS\Core\Resource\Folder\hasFile
‪bool hasFile($name)
Definition: Folder.php:401
‪TYPO3\CMS\Core\Resource\Security\FileNameValidator
Definition: FileNameValidator.php:25
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\injectHashService
‪injectHashService(HashService $hashService)
Definition: UploadedFileReferenceConverter.php:123
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\injectResourceFactory
‪injectResourceFactory(ResourceFactory $resourceFactory)
Definition: UploadedFileReferenceConverter.php:114
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$resourceFactory
‪TYPO3 CMS Core Resource ResourceFactory $resourceFactory
Definition: UploadedFileReferenceConverter.php:100
‪TYPO3\CMS\Core\Resource\Folder\createFile
‪File createFile($fileName)
Definition: Folder.php:353
‪TYPO3\CMS\Core\Resource\FileReference
Definition: FileReference.php:33
‪TYPO3\CMS\Form\Slot\ResourcePublicationSlot
Definition: ResourcePublicationSlot.php:36
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter
Definition: FormDefinitionArrayConverter.php:18
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$convertedResources
‪PseudoFileReference[] $convertedResources
Definition: UploadedFileReferenceConverter.php:96
‪TYPO3\CMS\Extbase\Security\Cryptography\HashService
Definition: HashService.php:31
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\importUploadedResource
‪PseudoFileReference importUploadedResource(array $uploadInfo, PropertyMappingConfigurationInterface $configuration)
Definition: UploadedFileReferenceConverter.php:208
‪TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface
Definition: PropertyMappingConfigurationInterface.php:22
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\injectPersistenceManager
‪injectPersistenceManager(PersistenceManagerInterface $persistenceManager)
Definition: UploadedFileReferenceConverter.php:132
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\CONFIGURATION_UPLOAD_FOLDER
‪const CONFIGURATION_UPLOAD_FOLDER
Definition: UploadedFileReferenceConverter.php:53
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\CONFIGURATION_UPLOAD_CONFLICT_MODE
‪const CONFIGURATION_UPLOAD_CONFLICT_MODE
Definition: UploadedFileReferenceConverter.php:58
‪TYPO3\CMS\Form\Exception
Definition: Exception.php:26
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$defaultConflictMode
‪string $defaultConflictMode
Definition: UploadedFileReferenceConverter.php:78
‪TYPO3\CMS\Extbase\Domain\Model\AbstractFileFolder
Definition: AbstractFileFolder.php:27
‪TYPO3\CMS\Extbase\Error\Error
Definition: Error.php:25
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$persistenceManager
‪TYPO3 CMS Extbase Persistence PersistenceManagerInterface $persistenceManager
Definition: UploadedFileReferenceConverter.php:108
‪TYPO3\CMS\Form\Mvc\Property\Exception\TypeConverterException
Definition: TypeConverterException.php:24
‪TYPO3\CMS\Core\Resource\Folder\createFolder
‪Folder createFolder($folderName)
Definition: Folder.php:364
‪$validator
‪if(isset($args['d'])) $validator
Definition: validateRstFiles.php:218
‪TYPO3\CMS\Core\Resource\Folder
Definition: Folder.php:37
‪TYPO3\CMS\Core\Resource\ResourceFactory
Definition: ResourceFactory.php:41
‪TYPO3\CMS\Core\Resource\Exception\FolderDoesNotExistException
Definition: FolderDoesNotExistException.php:22
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\convertFrom
‪AbstractFileFolder Error null convertFrom($source, $targetType, array $convertedChildProperties=[], PropertyMappingConfigurationInterface $configuration=null)
Definition: UploadedFileReferenceConverter.php:148
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\CONFIGURATION_UPLOAD_SEED
‪const CONFIGURATION_UPLOAD_SEED
Definition: UploadedFileReferenceConverter.php:63
‪TYPO3\CMS\Core\Resource\File
Definition: File.php:24
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\createFileReferenceFromFalFileObject
‪PseudoFileReference createFileReferenceFromFalFileObject(File $file, int $resourcePointer=null)
Definition: UploadedFileReferenceConverter.php:256
‪TYPO3\CMS\Form\Service\TranslationService\translate
‪mixed translate( $key, array $arguments=null, string $locallangPathAndFilename=null, string $language=null, $defaultValue='')
Definition: TranslationService.php:99
‪TYPO3\CMS\Core\Resource\Folder\getSubfolder
‪Folder getSubfolder($name)
Definition: Folder.php:273
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static string[] trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:1059
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\provideTargetFolder
‪Folder provideTargetFolder(Folder $parentFolder, string $folderName)
Definition: UploadedFileReferenceConverter.php:359
‪TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter
Definition: AbstractTypeConverter.php:34
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\provideFolderInitialization
‪provideFolderInitialization(Folder $parentFolder)
Definition: UploadedFileReferenceConverter.php:372
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$defaultUploadFolder
‪string $defaultUploadFolder
Definition: UploadedFileReferenceConverter.php:72
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static string getUniqueId($prefix='')
Definition: StringUtility.php:92
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$sourceTypes
‪array $sourceTypes
Definition: UploadedFileReferenceConverter.php:82
‪TYPO3\CMS\Core\Log\LogManager
Definition: LogManager.php:30
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\CONFIGURATION_FILE_VALIDATORS
‪const CONFIGURATION_FILE_VALIDATORS
Definition: UploadedFileReferenceConverter.php:68
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$hashService
‪TYPO3 CMS Extbase Security Cryptography HashService $hashService
Definition: UploadedFileReferenceConverter.php:104
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\provideUploadFolder
‪Folder provideUploadFolder(string $uploadFolderIdentifier)
Definition: UploadedFileReferenceConverter.php:338
‪TYPO3\CMS\Core\Crypto\Random
Definition: Random.php:24
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:22
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\createFileReferenceFromFalFileReferenceObject
‪PseudoFileReference createFileReferenceFromFalFileReferenceObject(CoreFileReference $falFileReference, int $resourcePointer=null)
Definition: UploadedFileReferenceConverter.php:280
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\getUploadErrorMessage
‪string getUploadErrorMessage(int $errorCode)
Definition: UploadedFileReferenceConverter.php:301
‪TYPO3\CMS\Core\Resource\Folder\hasFolder
‪bool hasFolder($name)
Definition: Folder.php:412