2 declare(strict_types
=1);
3 namespace TYPO3\CMS\Form\Mvc\Property\TypeConverter
;
6 * This file is part of the TYPO3 CMS project.
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.
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
15 * The TYPO3 project - inspiring people to share!
18 use TYPO3\CMS\Core\
Resource\File
as File
;
19 use TYPO3\CMS\Core\
Resource\FileReference
as CoreFileReference
;
20 use TYPO3\CMS\Core\Utility\GeneralUtility
;
21 use TYPO3\CMS\Extbase\Domain\Model\AbstractFileFolder
;
22 use TYPO3\CMS\Extbase\Domain\Model\FileReference
as ExtbaseFileReference
;
23 use TYPO3\CMS\Extbase\Error\Error
;
24 use TYPO3\CMS\Extbase\Property\Exception\TypeConverterException
;
25 use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface
;
26 use TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter
;
27 use TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator
;
30 * Class UploadedFileReferenceConverter
35 class UploadedFileReferenceConverter
extends AbstractTypeConverter
39 * Folder where the file upload should go to (including storage).
41 const CONFIGURATION_UPLOAD_FOLDER
= 1;
44 * How to handle a upload when the name of the uploaded file conflicts.
46 const CONFIGURATION_UPLOAD_CONFLICT_MODE
= 2;
49 * Validator for file types
51 const CONFIGURATION_FILE_VALIDATORS
= 4;
56 protected $defaultUploadFolder = '1:/user_upload/';
59 * One of 'cancel', 'replace', 'rename'
63 protected $defaultConflictMode = 'rename';
68 protected $sourceTypes = ['array'];
73 protected $targetType = ExtbaseFileReference
::class;
76 * Take precedence over the available FileReferenceConverter
80 protected $priority = 12;
83 * @var \TYPO3\CMS\Core\Resource\FileInterface[]
85 protected $convertedResources = [];
88 * @var \TYPO3\CMS\Core\Resource\ResourceFactory
90 protected $resourceFactory;
93 * @var \TYPO3\CMS\Extbase\Security\Cryptography\HashService
95 protected $hashService;
98 * @var \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface
100 protected $persistenceManager;
103 * @param \TYPO3\CMS\Core\Resource\ResourceFactory $resourceFactory
106 public function injectResourceFactory(\TYPO3\CMS\Core\
Resource\ResourceFactory
$resourceFactory)
108 $this->resourceFactory
= $resourceFactory;
112 * @param \TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService
115 public function injectHashService(\TYPO3\CMS\Extbase\Security\Cryptography\HashService
$hashService)
117 $this->hashService
= $hashService;
121 * @param \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager
124 public function injectPersistenceManager(\TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface
$persistenceManager)
126 $this->persistenceManager
= $persistenceManager;
130 * Actually convert from $source to $targetType, taking into account the fully
131 * built $convertedChildProperties and $configuration.
133 * @param string|int $source
134 * @param string $targetType
135 * @param array $convertedChildProperties
136 * @param PropertyMappingConfigurationInterface $configuration
137 * @return AbstractFileFolder
140 public function convertFrom($source, $targetType, array $convertedChildProperties = [], PropertyMappingConfigurationInterface
$configuration = null)
142 if (!isset($source['error']) ||
$source['error'] === \UPLOAD_ERR_NO_FILE
) {
143 if (isset($source['submittedFile']['resourcePointer'])) {
145 $resourcePointer = $this->hashService
->validateAndStripHmac($source['submittedFile']['resourcePointer']);
146 if (strpos($resourcePointer, 'file:') === 0) {
147 $fileUid = substr($resourcePointer, 5);
148 return $this->createFileReferenceFromFalFileObject($this->resourceFactory
->getFileObject($fileUid));
150 return $this->createFileReferenceFromFalFileReferenceObject($this->resourceFactory
->getFileReferenceObject($resourcePointer), $resourcePointer);
152 } catch (\InvalidArgumentException
$e) {
153 // Nothing to do. No file is uploaded and resource pointer is invalid. Discard!
159 if ($source['error'] !== \UPLOAD_ERR_OK
) {
160 return $this->objectManager
->get(Error
::class, $this->getUploadErrorMessage($source['error']), 1471715915);
163 if (isset($this->convertedResources
[$source['tmp_name']])) {
164 return $this->convertedResources
[$source['tmp_name']];
168 $resource = $this->importUploadedResource($source, $configuration);
169 } catch (\Exception
$e) {
170 return $this->objectManager
->get(Error
::class, $e->getMessage(), $e->getCode());
173 $this->convertedResources
[$source['tmp_name']] = $resource;
178 * Import a resource and respect configuration given for properties
180 * @param array $uploadInfo
181 * @param PropertyMappingConfigurationInterface $configuration
182 * @return ExtbaseFileReference
183 * @throws TypeConverterException
185 protected function importUploadedResource(
187 PropertyMappingConfigurationInterface
$configuration
188 ): ExtbaseFileReference
{
189 if (!GeneralUtility
::verifyFilenameAgainstDenyPattern($uploadInfo['name'])) {
190 throw new TypeConverterException('Uploading files with PHP file extensions is not allowed!', 1471710357);
193 $uploadFolderId = $configuration->getConfigurationValue(self
::class, self
::CONFIGURATION_UPLOAD_FOLDER
) ?
: $this->defaultUploadFolder
;
194 $conflictMode = $configuration->getConfigurationValue(self
::class, self
::CONFIGURATION_UPLOAD_CONFLICT_MODE
) ?
: $this->defaultConflictMode
;
196 $uploadFolder = $this->resourceFactory
->retrieveFileOrFolderObject($uploadFolderId);
197 $uploadedFile = $uploadFolder->addUploadedFile($uploadInfo, $conflictMode);
199 $validators = $configuration->getConfigurationValue(self
::class, self
::CONFIGURATION_FILE_VALIDATORS
);
200 if (is_array($validators)) {
201 foreach ($validators as $validator) {
202 if ($validator instanceof AbstractValidator
) {
203 $validationResult = $validator->validate($uploadedFile);
204 if ($validationResult->hasErrors()) {
205 $uploadedFile->getStorage()->deleteFile($uploadedFile);
206 throw new TypeConverterException($validationResult->getErrors()[0]->getMessage(), 1471708999);
212 $resourcePointer = isset($uploadInfo['submittedFile']['resourcePointer']) && strpos($uploadInfo['submittedFile']['resourcePointer'], 'file:') === false
213 ?
$this->hashService
->validateAndStripHmac($uploadInfo['submittedFile']['resourcePointer'])
216 $fileReferenceModel = $this->createFileReferenceFromFalFileObject($uploadedFile, $resourcePointer);
218 return $fileReferenceModel;
223 * @param int $resourcePointer
224 * @return ExtbaseFileReference
226 protected function createFileReferenceFromFalFileObject(
228 int $resourcePointer = null
229 ): ExtbaseFileReference
{
230 $fileReference = $this->resourceFactory
->createFileReferenceObject(
232 'uid_local' => $file->getUid(),
233 'uid_foreign' => uniqid('NEW_'),
234 'uid' => uniqid('NEW_'),
238 return $this->createFileReferenceFromFalFileReferenceObject($fileReference, $resourcePointer);
242 * @param CoreFileReference $falFileReference
243 * @param int $resourcePointer
244 * @return ExtbaseFileReference
246 protected function createFileReferenceFromFalFileReferenceObject(
247 CoreFileReference
$falFileReference,
248 int $resourcePointer = null
249 ): ExtbaseFileReference
{
250 if ($resourcePointer === null) {
251 $fileReference = $this->objectManager
->get(ExtbaseFileReference
::class);
253 $fileReference = $this->persistenceManager
->getObjectByIdentifier($resourcePointer, ExtbaseFileReference
::class, false);
256 $fileReference->setOriginalResource($falFileReference);
257 return $fileReference;
261 * Returns a human-readable message for the given PHP file upload error
264 * @param int $errorCode
267 protected function getUploadErrorMessage(int $errorCode): string
269 switch ($errorCode) {
270 case \UPLOAD_ERR_INI_SIZE
:
271 return 'The uploaded file exceeds the upload_max_filesize directive in php.ini';
272 case \UPLOAD_ERR_FORM_SIZE
:
273 return 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form';
274 case \UPLOAD_ERR_PARTIAL
:
275 return 'The uploaded file was only partially uploaded';
276 case \UPLOAD_ERR_NO_FILE
:
277 return 'No file was uploaded';
278 case \UPLOAD_ERR_NO_TMP_DIR
:
279 return 'Missing a temporary folder';
280 case \UPLOAD_ERR_CANT_WRITE
:
281 return 'Failed to write file to disk';
282 case \UPLOAD_ERR_EXTENSION
:
283 return 'File upload stopped by extension';
285 return 'Unknown upload error';