‪TYPO3CMS  11.5
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 {
53 
58 
62  public const ‪CONFIGURATION_UPLOAD_SEED = 3;
63 
68 
72  protected ‪$defaultUploadFolder = '1:/user_upload/';
73 
79  protected ‪$defaultConflictMode = 'rename';
80 
84  protected ‪$sourceTypes = ['array'];
85 
89  protected ‪$targetType = PseudoFileReference::class;
90 
96  protected ‪$priority = 12;
97 
101  protected ‪$convertedResources = [];
102 
106  protected ‪$resourceFactory;
107 
111  protected ‪$hashService;
112 
116  protected ‪$persistenceManager;
117 
123  {
124  $this->resourceFactory = ‪$resourceFactory;
125  }
126 
132  {
133  $this->hashService = ‪$hashService;
134  }
135 
141  {
142  $this->persistenceManager = ‪$persistenceManager;
143  }
144 
156  public function ‪convertFrom($source, ‪$targetType, array $convertedChildProperties = [], PropertyMappingConfigurationInterface $configuration = null)
157  {
158  // slot/listener using `FileDumpController` instead of direct public URL in (later) rendering process
159  $resourcePublicationSlot = GeneralUtility::makeInstance(ResourcePublicationSlot::class);
160  if (!isset($source['error']) || $source['error'] === \UPLOAD_ERR_NO_FILE) {
161  if (isset($source['submittedFile']['resourcePointer'])) {
162  try {
163  // File references use numeric resource pointers, direct
164  // file relations are using "file:" prefix (e.g. "file:5")
165  $resourcePointer = $this->hashService->validateAndStripHmac($source['submittedFile']['resourcePointer']);
166  if (strpos($resourcePointer, 'file:') === 0) {
167  $fileUid = (int)substr($resourcePointer, 5);
168  $resource = $this->‪createFileReferenceFromFalFileObject($this->resourceFactory->getFileObject($fileUid));
169  } else {
171  $this->resourceFactory->getFileReferenceObject($resourcePointer),
172  (int)$resourcePointer
173  );
174  }
175  $resourcePublicationSlot->add($resource->getOriginalResource()->getOriginalFile());
176  return $resource;
177  } catch (\InvalidArgumentException $e) {
178  // Nothing to do. No file is uploaded and resource pointer is invalid. Discard!
179  }
180  }
181  return null;
182  }
183 
184  if ($source['error'] !== \UPLOAD_ERR_OK) {
185  return GeneralUtility::makeInstance(Error::class, $this->‪getUploadErrorMessage($source['error']), 1471715915);
186  }
187 
188  if (isset($this->convertedResources[$source['tmp_name']])) {
189  return $this->convertedResources[$source['tmp_name']];
190  }
191 
192  if ($configuration === null) {
193  throw new \InvalidArgumentException('Argument $configuration must not be null', 1589183114);
194  }
195 
196  try {
197  $resource = $this->‪importUploadedResource($source, $configuration);
198  $resourcePublicationSlot->add($resource->getOriginalResource()->getOriginalFile());
199  } catch (TypeConverterException $e) {
200  return $e->getError();
201  } catch (\Exception $e) {
202  return GeneralUtility::makeInstance(Error::class, $e->getMessage(), $e->getCode());
203  }
204 
205  $this->convertedResources[$source['tmp_name']] = $resource;
206  return $resource;
207  }
208 
216  protected function ‪importUploadedResource(
217  array $uploadInfo,
220  if (!GeneralUtility::makeInstance(FileNameValidator::class)->isValid($uploadInfo['name'])) {
221  throw new ‪TypeConverterException('Uploading files with PHP file extensions is not allowed!', 1471710357);
222  }
223  // `CONFIGURATION_UPLOAD_SEED` is expected to be defined
224  // if it's not given any random seed is generated, instead of throwing an exception
225  $seed = $configuration->‪getConfigurationValue(self::class, self::CONFIGURATION_UPLOAD_SEED)
226  ?: GeneralUtility::makeInstance(Random::class)->generateRandomHexString(40);
227  $uploadFolderId = $configuration->‪getConfigurationValue(self::class, self::CONFIGURATION_UPLOAD_FOLDER) ?: ‪$this->defaultUploadFolder;
228  $conflictMode = $configuration->‪getConfigurationValue(self::class, self::CONFIGURATION_UPLOAD_CONFLICT_MODE) ?: ‪$this->defaultConflictMode;
229  $pseudoFile = GeneralUtility::makeInstance(PseudoFile::class, $uploadInfo);
230 
231  $validators = $configuration->‪getConfigurationValue(self::class, self::CONFIGURATION_FILE_VALIDATORS);
232  if (is_array($validators)) {
233  foreach ($validators as ‪$validator) {
234  if (‪$validator instanceof AbstractValidator) {
235  $validationResult = ‪$validator->validate($pseudoFile);
236  if ($validationResult->hasErrors()) {
237  throw ‪TypeConverterException::fromError($validationResult->getErrors()[0]);
238  }
239  }
240  }
241  }
242 
243  $uploadFolder = $this->‪provideUploadFolder($uploadFolderId);
244  // current folder name, derived from public random seed (`formSession`)
245  $currentName = 'form_' . GeneralUtility::hmac($seed, self::class);
246  $uploadFolder = $this->‪provideTargetFolder($uploadFolder, $currentName);
247  // sub-folder in $uploadFolder with 160 bit of derived entropy (.../form_<40-chars-hash>/actual.file)
248  $uploadedFile = $uploadFolder->addUploadedFile($uploadInfo, $conflictMode);
249 
250  $resourcePointer = isset($uploadInfo['submittedFile']['resourcePointer']) && !str_contains($uploadInfo['submittedFile']['resourcePointer'], 'file:')
251  ? (int)$this->hashService->validateAndStripHmac($uploadInfo['submittedFile']['resourcePointer'])
252  : null;
253 
254  $fileReferenceModel = $this->‪createFileReferenceFromFalFileObject($uploadedFile, $resourcePointer);
255 
256  return $fileReferenceModel;
257  }
258 
264  protected function ‪createFileReferenceFromFalFileObject(
265  File $file,
266  int $resourcePointer = null
268  $fileReference = $this->resourceFactory->createFileReferenceObject(
269  [
270  'uid_local' => $file->getUid(),
271  'uid_foreign' => ‪StringUtility::getUniqueId('NEW_'),
272  'uid' => ‪StringUtility::getUniqueId('NEW_'),
273  'crop' => null,
274  ]
275  );
276  return $this->‪createFileReferenceFromFalFileReferenceObject($fileReference, $resourcePointer);
277  }
278 
289  CoreFileReference $falFileReference,
290  int $resourcePointer = null
291  ): PseudoFileReference {
292  if ($resourcePointer === null) {
293  $fileReference = GeneralUtility::makeInstance(PseudoFileReference::class);
294  } else {
295  $fileReference = $this->persistenceManager->getObjectByIdentifier($resourcePointer, PseudoFileReference::class, false);
296  }
297 
298  $fileReference->setOriginalResource($falFileReference);
299  return $fileReference;
300  }
301 
309  protected function ‪getUploadErrorMessage(int $errorCode): string
310  {
311  $logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(static::class);
312  switch ($errorCode) {
313  case \UPLOAD_ERR_INI_SIZE:
314  $logger->error('The uploaded file exceeds the upload_max_filesize directive in php.ini.', []);
315  return GeneralUtility::makeInstance(TranslationService::class)->translate('upload.error.150530345', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
316  case \UPLOAD_ERR_FORM_SIZE:
317  $logger->error('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.', []);
318  return GeneralUtility::makeInstance(TranslationService::class)->translate('upload.error.150530345', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
319  case \UPLOAD_ERR_PARTIAL:
320  $logger->error('The uploaded file was only partially uploaded.', []);
321  return GeneralUtility::makeInstance(TranslationService::class)->translate('upload.error.150530346', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
322  case \UPLOAD_ERR_NO_FILE:
323  $logger->error('No file was uploaded.', []);
324  return GeneralUtility::makeInstance(TranslationService::class)->translate('upload.error.150530347', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
325  case \UPLOAD_ERR_NO_TMP_DIR:
326  $logger->error('Missing a temporary folder.', []);
327  return GeneralUtility::makeInstance(TranslationService::class)->translate('upload.error.150530348', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
328  case \UPLOAD_ERR_CANT_WRITE:
329  $logger->error('Failed to write file to disk.', []);
330  return GeneralUtility::makeInstance(TranslationService::class)->translate('upload.error.150530348', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
331  case \UPLOAD_ERR_EXTENSION:
332  $logger->error('File upload stopped by extension.', []);
333  return GeneralUtility::makeInstance(TranslationService::class)->translate('upload.error.150530348', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
334  default:
335  $logger->error('Unknown upload error.', []);
336  return GeneralUtility::makeInstance(TranslationService::class)->translate('upload.error.150530348', null, 'EXT:form/Resources/Private/Language/locallang.xlf');
337  }
338  }
339 
346  protected function ‪provideUploadFolder(string $uploadFolderIdentifier): Folder
347  {
348  try {
349  return $this->resourceFactory->getFolderObjectFromCombinedIdentifier($uploadFolderIdentifier);
350  } catch (FolderDoesNotExistException $exception) {
351  [$storageId, $storagePath] = explode(':', $uploadFolderIdentifier, 2);
352  $storage = $this->resourceFactory->getStorageObject($storageId);
353  $folderNames = ‪GeneralUtility::trimExplode('/', $storagePath, true);
354  $uploadFolder = $this->‪provideTargetFolder($storage->getRootLevelFolder(), ...$folderNames);
355  $this->‪provideFolderInitialization($uploadFolder);
356  return $uploadFolder;
357  }
358  }
359 
367  protected function ‪provideTargetFolder(‪Folder $parentFolder, string $folderName): ‪Folder
368  {
369  return $parentFolder->‪hasFolder($folderName)
370  ? $parentFolder->‪getSubfolder($folderName)
371  : $parentFolder->‪createFolder($folderName);
372  }
373 
380  protected function ‪provideFolderInitialization(‪Folder $parentFolder): void
381  {
382  if (!$parentFolder->‪hasFile('index.html')) {
383  $parentFolder->‪createFile('index.html');
384  }
385  }
386 }
‪TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface\getConfigurationValue
‪mixed getConfigurationValue($typeConverterClassName, $key)
‪TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator
Definition: AbstractValidator.php:27
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:999
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter
Definition: UploadedFileReferenceConverter.php:48
‪TYPO3\CMS\Form\Mvc\Property\Exception\TypeConverterException\getError
‪getError()
Definition: TypeConverterException.php:37
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$targetType
‪string $targetType
Definition: UploadedFileReferenceConverter.php:85
‪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:91
‪TYPO3\CMS\Form\Service\TranslationService
Definition: TranslationService.php:45
‪TYPO3\CMS\Core\Resource\Folder\hasFile
‪bool hasFile($name)
Definition: Folder.php:400
‪TYPO3\CMS\Core\Resource\Security\FileNameValidator
Definition: FileNameValidator.php:25
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\injectHashService
‪injectHashService(HashService $hashService)
Definition: UploadedFileReferenceConverter.php:122
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\injectResourceFactory
‪injectResourceFactory(ResourceFactory $resourceFactory)
Definition: UploadedFileReferenceConverter.php:113
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$resourceFactory
‪TYPO3 CMS Core Resource ResourceFactory $resourceFactory
Definition: UploadedFileReferenceConverter.php:99
‪TYPO3\CMS\Core\Resource\Folder\createFile
‪File createFile($fileName)
Definition: Folder.php:352
‪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:95
‪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:207
‪TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface
Definition: PropertyMappingConfigurationInterface.php:22
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\injectPersistenceManager
‪injectPersistenceManager(PersistenceManagerInterface $persistenceManager)
Definition: UploadedFileReferenceConverter.php:131
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\CONFIGURATION_UPLOAD_FOLDER
‪const CONFIGURATION_UPLOAD_FOLDER
Definition: UploadedFileReferenceConverter.php:52
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\CONFIGURATION_UPLOAD_CONFLICT_MODE
‪const CONFIGURATION_UPLOAD_CONFLICT_MODE
Definition: UploadedFileReferenceConverter.php:57
‪TYPO3\CMS\Form\Exception
Definition: Exception.php:25
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$defaultConflictMode
‪string $defaultConflictMode
Definition: UploadedFileReferenceConverter.php:77
‪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:107
‪TYPO3\CMS\Form\Mvc\Property\Exception\TypeConverterException
Definition: TypeConverterException.php:24
‪TYPO3\CMS\Core\Resource\Folder\createFolder
‪Folder createFolder($folderName)
Definition: Folder.php:363
‪$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:21
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\convertFrom
‪AbstractFileFolder Error null convertFrom($source, $targetType, array $convertedChildProperties=[], PropertyMappingConfigurationInterface $configuration=null)
Definition: UploadedFileReferenceConverter.php:147
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\CONFIGURATION_UPLOAD_SEED
‪const CONFIGURATION_UPLOAD_SEED
Definition: UploadedFileReferenceConverter.php:62
‪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:255
‪TYPO3\CMS\Core\Resource\Folder\getSubfolder
‪Folder getSubfolder($name)
Definition: Folder.php:274
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\provideTargetFolder
‪Folder provideTargetFolder(Folder $parentFolder, string $folderName)
Definition: UploadedFileReferenceConverter.php:358
‪TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter
Definition: AbstractTypeConverter.php:34
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\provideFolderInitialization
‪provideFolderInitialization(Folder $parentFolder)
Definition: UploadedFileReferenceConverter.php:371
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$defaultUploadFolder
‪string $defaultUploadFolder
Definition: UploadedFileReferenceConverter.php:71
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static string getUniqueId($prefix='')
Definition: StringUtility.php:128
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$sourceTypes
‪array $sourceTypes
Definition: UploadedFileReferenceConverter.php:81
‪TYPO3\CMS\Core\Log\LogManager
Definition: LogManager.php:33
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\CONFIGURATION_FILE_VALIDATORS
‪const CONFIGURATION_FILE_VALIDATORS
Definition: UploadedFileReferenceConverter.php:67
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\$hashService
‪TYPO3 CMS Extbase Security Cryptography HashService $hashService
Definition: UploadedFileReferenceConverter.php:103
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\provideUploadFolder
‪Folder provideUploadFolder(string $uploadFolderIdentifier)
Definition: UploadedFileReferenceConverter.php:337
‪TYPO3\CMS\Core\Crypto\Random
Definition: Random.php:24
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪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:279
‪TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter\getUploadErrorMessage
‪string getUploadErrorMessage(int $errorCode)
Definition: UploadedFileReferenceConverter.php:300
‪TYPO3\CMS\Core\Resource\Folder\hasFolder
‪bool hasFolder($name)
Definition: Folder.php:425