[CLEANUP] Ensure variables initalized and fix code smell
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Utility / File / ExtendedFileUtility.php
1 <?php
2 namespace TYPO3\CMS\Core\Utility\File;
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
17 use TYPO3\CMS\Backend\Utility\BackendUtility;
18 use TYPO3\CMS\Core\Database\Connection;
19 use TYPO3\CMS\Core\Database\ConnectionPool;
20 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
21 use TYPO3\CMS\Core\Messaging\FlashMessage;
22 use TYPO3\CMS\Core\Messaging\FlashMessageService;
23 use TYPO3\CMS\Core\Resource\DuplicationBehavior;
24 use TYPO3\CMS\Core\Resource\Exception;
25 use TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException;
26 use TYPO3\CMS\Core\Resource\Exception\ExistingTargetFolderException;
27 use TYPO3\CMS\Core\Resource\Exception\IllegalFileExtensionException;
28 use TYPO3\CMS\Core\Resource\Exception\InsufficientFileWritePermissionsException;
29 use TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException;
30 use TYPO3\CMS\Core\Resource\Exception\InsufficientFolderWritePermissionsException;
31 use TYPO3\CMS\Core\Resource\Exception\InsufficientUserPermissionsException;
32 use TYPO3\CMS\Core\Resource\Exception\InvalidFileNameException;
33 use TYPO3\CMS\Core\Resource\Exception\NotInMountPointException;
34 use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
35 use TYPO3\CMS\Core\Resource\Exception\UploadException;
36 use TYPO3\CMS\Core\Resource\Exception\UploadSizeException;
37 use TYPO3\CMS\Core\Resource\File;
38 use TYPO3\CMS\Core\Resource\Folder;
39 use TYPO3\CMS\Core\Resource\ResourceFactory;
40 use TYPO3\CMS\Core\Resource\ResourceStorage;
41 use TYPO3\CMS\Core\Type\Exception\InvalidEnumerationValueException;
42 use TYPO3\CMS\Core\Utility\GeneralUtility;
43 use TYPO3\CMS\Lang\LanguageService;
44
45 /**
46 * Contains functions for performing file operations like copying, pasting, uploading, moving,
47 * deleting etc. through the TCE
48 *
49 * See document "TYPO3 Core API" for syntax
50 *
51 * This class contains functions primarily used by tce_file.php (TYPO3 Core Engine for file manipulation)
52 * Functions include copying, moving, deleting, uploading and so on...
53 *
54 * Important internal variables:
55 *
56 * $fileExtensionPermissions (see basicFileFunctions)
57 *
58 * All fileoperations must be within the filemount paths of the user. Further the fileextension
59 * MUST validate TRUE with the fileExtensionPermissions array
60 */
61 class ExtendedFileUtility extends BasicFileUtility
62 {
63 /**
64 * Defines behaviour when uploading files with names that already exist; possible values are
65 * the values of the \TYPO3\CMS\Core\Resource\DuplicationBehavior enumeration
66 *
67 * @var \TYPO3\CMS\Core\Resource\DuplicationBehavior
68 */
69 protected $existingFilesConflictMode;
70
71 /**
72 * This array is self-explaining (look in the class below).
73 * It grants access to the functions. This could be set from outside in order to enabled functions to users.
74 * See also the function setActionPermissions() which takes input directly from the user-record
75 *
76 * @var array
77 */
78 public $actionPerms = [
79 // File permissions
80 'addFile' => false,
81 'readFile' => false,
82 'writeFile' => false,
83 'copyFile' => false,
84 'moveFile' => false,
85 'renameFile' => false,
86 'deleteFile' => false,
87 // Folder permissions
88 'addFolder' => false,
89 'readFolder' => false,
90 'writeFolder' => false,
91 'copyFolder' => false,
92 'moveFolder' => false,
93 'renameFolder' => false,
94 'deleteFolder' => false,
95 'recursivedeleteFolder' => false
96 ];
97
98 /**
99 * Will contain map between upload ID and the final filename
100 *
101 * @var array
102 */
103 public $internalUploadMap = [];
104
105 /**
106 * All error messages from the file operations of this script instance
107 *
108 * @var array
109 */
110 protected $errorMessages = [];
111
112 /**
113 * Container for FlashMessages so they can be localized
114 *
115 * @var array
116 */
117 protected $flashMessages = [];
118
119 /**
120 * @var array
121 */
122 protected $fileCmdMap;
123
124 /**
125 * The File Factory
126 *
127 * @var \TYPO3\CMS\Core\Resource\ResourceFactory
128 */
129 protected $fileFactory;
130
131 /**
132 * Get existingFilesConflictMode
133 *
134 * @return string
135 */
136 public function getExistingFilesConflictMode()
137 {
138 return (string)$this->existingFilesConflictMode;
139 }
140
141 /**
142 * Set existingFilesConflictMode
143 *
144 * @param \TYPO3\CMS\Core\Resource\DuplicationBehavior|string $existingFilesConflictMode Instance or constant of \TYPO3\CMS\Core\Resource\DuplicationBehavior
145 * @return void
146 * @throws Exception
147 */
148 public function setExistingFilesConflictMode($existingFilesConflictMode)
149 {
150 try {
151 $this->existingFilesConflictMode = DuplicationBehavior::cast($existingFilesConflictMode);
152 } catch (InvalidEnumerationValueException $e) {
153 throw new Exception(
154 sprintf(
155 'Invalid argument, received: "%s", expected a value from enumeration \TYPO3\CMS\Core\Resource\DuplicationBehavior (%s)',
156 $existingFilesConflictMode,
157 implode(', ', DuplicationBehavior::getConstants())
158 ),
159 1476046229
160 );
161 }
162 }
163
164 /**
165 * Initialization of the class
166 *
167 * @param array $fileCmds Array with the commands to execute. See "TYPO3 Core API" document
168 * @return void
169 */
170 public function start($fileCmds)
171 {
172 // Initialize Object Factory
173 $this->fileFactory = ResourceFactory::getInstance();
174 // Initializing file processing commands:
175 $this->fileCmdMap = $fileCmds;
176 }
177
178 /**
179 * Sets the file action permissions.
180 * If no argument is given, permissions of the currently logged in backend user are taken into account.
181 *
182 * @param array $permissions File Permissions.
183 * @return void
184 */
185 public function setActionPermissions(array $permissions = [])
186 {
187 if (empty($permissions)) {
188 $permissions = $this->getBackendUser()->getFilePermissions();
189 }
190 $this->actionPerms = $permissions;
191 }
192
193 /**
194 * Processing the command array in $this->fileCmdMap
195 *
196 * @return mixed FALSE, if the file functions were not initialized
197 * @throws \UnexpectedValueException
198 */
199 public function processData()
200 {
201 $result = [];
202 if (is_array($this->fileCmdMap)) {
203 // Check if there were uploads expected, but no one made
204 if ($this->fileCmdMap['upload']) {
205 $uploads = $this->fileCmdMap['upload'];
206 foreach ($uploads as $upload) {
207 if (empty($_FILES['upload_' . $upload['data']]['name'])
208 || (is_array($_FILES['upload_' . $upload['data']]['name'])
209 && empty($_FILES['upload_' . $upload['data']]['name'][0])
210 )
211 ) {
212 unset($this->fileCmdMap['upload'][$upload['data']]);
213 }
214 }
215 if (empty($this->fileCmdMap['upload'])) {
216 $this->writeLog(1, 1, 108, 'No file was uploaded!', []);
217 $this->addMessageToFlashMessageQueue('FileUtility.NoFileWasUploaded');
218 }
219 }
220
221 // Check if there were new folder names expected, but non given
222 if ($this->fileCmdMap['newfolder']) {
223 foreach ($this->fileCmdMap['newfolder'] as $key => $cmdArr) {
224 if (empty($cmdArr['data'])) {
225 unset($this->fileCmdMap['newfolder'][$key]);
226 }
227 }
228 if (empty($this->fileCmdMap['newfolder'])) {
229 $this->writeLog(6, 1, 108, 'No name for new folder given!', []);
230 $this->addMessageToFlashMessageQueue('FileUtility.NoNameForNewFolderGiven');
231 }
232 }
233
234 // Traverse each set of actions
235 foreach ($this->fileCmdMap as $action => $actionData) {
236 // Traverse all action data. More than one file might be affected at the same time.
237 if (is_array($actionData)) {
238 $result[$action] = [];
239 foreach ($actionData as $cmdArr) {
240 // Clear file stats
241 clearstatcache();
242 // Branch out based on command:
243 switch ($action) {
244 case 'delete':
245 $result[$action][] = $this->func_delete($cmdArr);
246 break;
247 case 'copy':
248 $result[$action][] = $this->func_copy($cmdArr);
249 break;
250 case 'move':
251 $result[$action][] = $this->func_move($cmdArr);
252 break;
253 case 'rename':
254 $result[$action][] = $this->func_rename($cmdArr);
255 break;
256 case 'newfolder':
257 $result[$action][] = $this->func_newfolder($cmdArr);
258 break;
259 case 'newfile':
260 $result[$action][] = $this->func_newfile($cmdArr);
261 break;
262 case 'editfile':
263 $result[$action][] = $this->func_edit($cmdArr);
264 break;
265 case 'upload':
266 $result[$action][] = $this->func_upload($cmdArr);
267 break;
268 case 'replace':
269 $result[$action][] = $this->replaceFile($cmdArr);
270 break;
271 }
272 // Hook for post-processing the action
273 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_extfilefunc.php']['processData'])) {
274 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_extfilefunc.php']['processData'] as $classRef) {
275 $hookObject = GeneralUtility::getUserObj($classRef);
276 if (!$hookObject instanceof ExtendedFileUtilityProcessDataHookInterface) {
277 throw new \UnexpectedValueException($classRef . ' must implement interface ' . ExtendedFileUtilityProcessDataHookInterface::class, 1279719168);
278 }
279 $hookObject->processData_postProcessAction($action, $cmdArr, $result[$action], $this);
280 }
281 }
282 }
283 }
284 }
285 }
286 return $result;
287 }
288
289 /**
290 * Adds all log error messages from the operations of this script instance to the FlashMessageQueue
291 *
292 * @return void
293 * @deprecated since TYPO3 CMS 8, will be removed in TYPO3 CMS 9
294 */
295 public function pushErrorMessagesToFlashMessageQueue()
296 {
297 GeneralUtility::logDeprecatedFunction();
298 foreach ($this->getErrorMessages() as $msg) {
299 $flashMessage = GeneralUtility::makeInstance(
300 FlashMessage::class,
301 $msg,
302 '',
303 FlashMessage::ERROR,
304 true
305 );
306 $this->addFlashMessage($flashMessage);
307 }
308 }
309
310 /**
311 * Return all error messages from the file operations of this script instance
312 *
313 * @return array all errorMessages as a numerical array
314 */
315 public function getErrorMessages()
316 {
317 return $this->errorMessages;
318 }
319
320 /**
321 * @param int $action The action number. See the functions in the class for a hint. Eg. edit is '9', upload is '1' ...
322 * @param int $error The severity: 0 = message, 1 = error, 2 = System Error, 3 = security notice (admin)
323 * @param int $details_nr This number is unique for every combination of $type and $action. This is the error-message number, which can later be used to translate error messages.
324 * @param string $details This is the default, raw error message in english
325 * @param array $data Array with special information that may go into $details by "%s" marks / sprintf() when the log is shown
326 * @return void
327 */
328 public function writeLog($action, $error, $details_nr, $details, $data)
329 {
330 // Type value for tce_file.php
331 $type = 2;
332 if (is_object($this->getBackendUser())) {
333 $this->getBackendUser()->writelog($type, $action, $error, $details_nr, $details, $data);
334 }
335 if ($error > 0) {
336 $this->errorMessages[] = vsprintf($details, $data);
337 }
338 }
339
340 /**
341 * Adds a localized FlashMessage to the message queue
342 *
343 * @param string $localizationKey
344 * @param array $replaceMarkers
345 * @param int $severity
346 * @return void
347 * @throws \InvalidArgumentException
348 */
349 protected function addMessageToFlashMessageQueue($localizationKey, array $replaceMarkers = [], $severity = FlashMessage::ERROR)
350 {
351 if (TYPO3_MODE !== 'BE') {
352 return;
353 }
354 $label = $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/fileMessages.xlf:' . $localizationKey);
355 $message = vsprintf($label, $replaceMarkers);
356 $flashMessage = GeneralUtility::makeInstance(
357 FlashMessage::class,
358 $message,
359 '',
360 $severity,
361 true
362 );
363 $this->addFlashMessage($flashMessage);
364 }
365
366 /*************************************
367 *
368 * File operation functions
369 *
370 **************************************/
371 /**
372 * Deleting files and folders (action=4)
373 *
374 * @param array $cmds $cmds['data'] is the file/folder to delete
375 * @return bool Returns TRUE upon success
376 */
377 public function func_delete(array $cmds)
378 {
379 $result = false;
380 // Example identifier for $cmds['data'] => "4:mypath/tomyfolder/myfile.jpg"
381 // for backwards compatibility: the combined file identifier was the path+filename
382 try {
383 $fileObject = $this->getFileObject($cmds['data']);
384 } catch (ResourceDoesNotExistException $e) {
385 $flashMessage = GeneralUtility::makeInstance(
386 FlashMessage::class,
387 sprintf(
388 $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:message.description.fileNotFound'),
389 $cmds['data']
390 ),
391 $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:message.header.fileNotFound'),
392 FlashMessage::ERROR,
393 true
394 );
395 $this->addFlashMessage($flashMessage);
396
397 return false;
398 }
399 // checks to delete the file
400 if ($fileObject instanceof File) {
401 // check if the file still has references
402 // Exclude sys_file_metadata records as these are no use references
403 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
404 $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
405 $refIndexRecords = $queryBuilder
406 ->select('tablename', 'recuid', 'ref_uid')
407 ->from('sys_refindex')
408 ->where(
409 $queryBuilder->expr()->eq(
410 'ref_table',
411 $queryBuilder->createNamedParameter('sys_file', \PDO::PARAM_STR)
412 ),
413 $queryBuilder->expr()->eq(
414 'ref_uid',
415 $queryBuilder->createNamedParameter($fileObject->getUid(), \PDO::PARAM_INT)
416 ),
417 $queryBuilder->expr()->neq(
418 'tablename',
419 $queryBuilder->createNamedParameter('sys_file_metadata', \PDO::PARAM_STR)
420 )
421 )
422 ->execute()
423 ->fetchAll();
424 $deleteFile = true;
425 if (!empty($refIndexRecords)) {
426 $shortcutContent = [];
427 $brokenReferences = [];
428
429 foreach ($refIndexRecords as $fileReferenceRow) {
430 if ($fileReferenceRow['tablename'] === 'sys_file_reference') {
431 $row = $this->transformFileReferenceToRecordReference($fileReferenceRow);
432 $shortcutRecord = BackendUtility::getRecord($row['tablename'], $row['recuid']);
433
434 if ($shortcutRecord) {
435 $shortcutContent[] = '[record:' . $row['tablename'] . ':' . $row['recuid'] . ']';
436 } else {
437 $brokenReferences[] = $fileReferenceRow['ref_uid'];
438 }
439 }
440 }
441 if (!empty($brokenReferences)) {
442 // render a message that the file has broken references
443 $flashMessage = GeneralUtility::makeInstance(
444 FlashMessage::class,
445 sprintf($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:message.description.fileHasBrokenReferences'), count($brokenReferences)),
446 $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:message.header.fileHasBrokenReferences'),
447 FlashMessage::INFO,
448 true
449 );
450 $this->addFlashMessage($flashMessage);
451 }
452 if (!empty($shortcutContent)) {
453 // render a message that the file could not be deleted
454 $flashMessage = GeneralUtility::makeInstance(
455 FlashMessage::class,
456 sprintf($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:message.description.fileNotDeletedHasReferences'), $fileObject->getName()) . ' ' . implode(', ', $shortcutContent),
457 $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:message.header.fileNotDeletedHasReferences'),
458 FlashMessage::WARNING,
459 true
460 );
461 $this->addFlashMessage($flashMessage);
462 $deleteFile = false;
463 }
464 }
465
466 if ($deleteFile) {
467 try {
468 $result = $fileObject->delete();
469
470 // show the user that the file was deleted
471 $flashMessage = GeneralUtility::makeInstance(
472 FlashMessage::class,
473 sprintf($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:message.description.fileDeleted'), $fileObject->getName()),
474 $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:message.header.fileDeleted'),
475 FlashMessage::OK,
476 true
477 );
478 $this->addFlashMessage($flashMessage);
479 // Log success
480 $this->writeLog(4, 0, 1, 'File "%s" deleted', [$fileObject->getIdentifier()]);
481 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientFileAccessPermissionsException $e) {
482 $this->writeLog(4, 1, 112, 'You are not allowed to access the file', [$fileObject->getIdentifier()]);
483 $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToAccessTheFile', [$fileObject->getIdentifier()]);
484 } catch (NotInMountPointException $e) {
485 $this->writeLog(4, 1, 111, 'Target was not within your mountpoints! T="%s"', [$fileObject->getIdentifier()]);
486 $this->addMessageToFlashMessageQueue('FileUtility.TargetWasNotWithinYourMountpoints', [$fileObject->getIdentifier()]);
487 } catch (\RuntimeException $e) {
488 $this->writeLog(4, 1, 110, 'Could not delete file "%s". Write-permission problem?', [$fileObject->getIdentifier()]);
489 $this->addMessageToFlashMessageQueue('FileUtility.CouldNotDeleteFile', [$fileObject->getIdentifier()]);
490 }
491 }
492 } else {
493 /** @var Folder $fileObject */
494 if (!$this->folderHasFilesInUse($fileObject)) {
495 try {
496 $result = $fileObject->delete(true);
497 if ($result) {
498 // notify the user that the folder was deleted
499 /** @var FlashMessage $flashMessage */
500 $flashMessage = GeneralUtility::makeInstance(
501 FlashMessage::class,
502 sprintf($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:message.description.folderDeleted'), $fileObject->getName()),
503 $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:message.header.folderDeleted'),
504 FlashMessage::OK,
505 true
506 );
507 $this->addFlashMessage($flashMessage);
508 // Log success
509 $this->writeLog(4, 0, 3, 'Directory "%s" deleted', [$fileObject->getIdentifier()]);
510 }
511 } catch (InsufficientUserPermissionsException $e) {
512 $this->writeLog(4, 1, 120, 'Could not delete directory! Is directory "%s" empty? (You are not allowed to delete directories recursively).', [$fileObject->getIdentifier()]);
513 $this->addMessageToFlashMessageQueue('FileUtility.CouldNotDeleteDirectory', [$fileObject->getIdentifier()]);
514 } catch (InsufficientFolderAccessPermissionsException $e) {
515 $this->writeLog(4, 1, 123, 'You are not allowed to access the directory', [$fileObject->getIdentifier()]);
516 $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToAccessTheDirectory', [$fileObject->getIdentifier()]);
517 } catch (NotInMountPointException $e) {
518 $this->writeLog(4, 1, 121, 'Target was not within your mountpoints! T="%s"', [$fileObject->getIdentifier()]);
519 $this->addMessageToFlashMessageQueue('FileUtility.TargetWasNotWithinYourMountpoints', [$fileObject->getIdentifier()]);
520 } catch (\TYPO3\CMS\Core\Resource\Exception\FileOperationErrorException $e) {
521 $this->writeLog(4, 1, 120, 'Could not delete directory "%s"! Write-permission problem?', [$fileObject->getIdentifier()]);
522 $this->addMessageToFlashMessageQueue('FileUtility.CouldNotDeleteDirectory', [$fileObject->getIdentifier()]);
523 }
524 }
525 }
526
527 return $result;
528 }
529
530 /**
531 * Checks files in given folder recursively for for existing references.
532 *
533 * Creates a flash message if there are references.
534 *
535 * @param Folder $folder
536 * @return bool TRUE if folder has files in use, FALSE otherwise
537 */
538 public function folderHasFilesInUse(Folder $folder)
539 {
540 $files = $folder->getFiles(0, 0, Folder::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, true);
541 if (empty($files)) {
542 return false;
543 }
544
545 /** @var int[] $fileUids */
546 $fileUids = [];
547 foreach ($files as $file) {
548 $fileUids[] = $file->getUid();
549 }
550
551 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
552 $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
553 $numberOfReferences = $queryBuilder
554 ->count('hash')
555 ->from('sys_refindex')
556 ->where(
557 $queryBuilder->expr()->eq(
558 'ref_table',
559 $queryBuilder->createNamedParameter('sys_file', \PDO::PARAM_STR)
560 ),
561 $queryBuilder->expr()->in(
562 'ref_uid',
563 $queryBuilder->createNamedParameter($fileUids, Connection::PARAM_INT_ARRAY)
564 ),
565 $queryBuilder->expr()->neq(
566 'tablename',
567 $queryBuilder->createNamedParameter('sys_file_metadata', \PDO::PARAM_STR)
568 )
569 )->execute()->fetchColumn(0);
570
571 $hasReferences = $numberOfReferences > 0;
572 if ($hasReferences) {
573 /** @var FlashMessage $flashMessage */
574 $flashMessage = GeneralUtility::makeInstance(
575 FlashMessage::class,
576 $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:message.description.folderNotDeletedHasFilesWithReferences'),
577 $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:message.header.folderNotDeletedHasFilesWithReferences'),
578 FlashMessage::WARNING,
579 true
580 );
581 $this->addFlashMessage($flashMessage);
582 }
583
584 return $hasReferences;
585 }
586
587 /**
588 * Maps results from the fal file reference table on the
589 * structure of the normal reference index table.
590 *
591 * @param array $referenceRecord
592 * @return array
593 */
594 protected function transformFileReferenceToRecordReference(array $referenceRecord)
595 {
596 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
597 $queryBuilder->getRestrictions()->removeAll();
598 $fileReference = $queryBuilder
599 ->select('uid_foreign', 'tablenames', 'fieldname', 'sorting_foreign')
600 ->from('sys_file_reference')
601 ->where(
602 $queryBuilder->expr()->eq(
603 'uid',
604 $queryBuilder->createNamedParameter($referenceRecord['recuid'], \PDO::PARAM_INT)
605 )
606 )
607 ->execute()
608 ->fetch();
609
610 return [
611 'recuid' => $fileReference['uid_foreign'],
612 'tablename' => $fileReference['tablenames'],
613 'field' => $fileReference['fieldname'],
614 'flexpointer' => '',
615 'softref_key' => '',
616 'sorting' => $fileReference['sorting_foreign']
617 ];
618 }
619
620 /**
621 * Gets a File or a Folder object from an identifier [storage]:[fileId]
622 *
623 * @param string $identifier
624 * @return File|Folder
625 * @throws Exception\InsufficientFileAccessPermissionsException
626 * @throws Exception\InvalidFileException
627 */
628 protected function getFileObject($identifier)
629 {
630 $object = $this->fileFactory->retrieveFileOrFolderObject($identifier);
631 if (!is_object($object)) {
632 throw new \TYPO3\CMS\Core\Resource\Exception\InvalidFileException('The item ' . $identifier . ' was not a file or directory!!', 1320122453);
633 }
634 if ($object->getStorage()->getUid() === 0) {
635 throw new \TYPO3\CMS\Core\Resource\Exception\InsufficientFileAccessPermissionsException('You are not allowed to access files outside your storages', 1375889830);
636 }
637 return $object;
638 }
639
640 /**
641 * Copying files and folders (action=2)
642 *
643 * $cmds['data'] (string): The file/folder to copy
644 * + example "4:mypath/tomyfolder/myfile.jpg")
645 * + for backwards compatibility: the identifier was the path+filename
646 * $cmds['target'] (string): The path where to copy to.
647 * + example "2:targetpath/targetfolder/"
648 * $cmds['altName'] (string): Use an alternative name if the target already exists
649 *
650 * @param array $cmds Command details as described above
651 * @return \TYPO3\CMS\Core\Resource\File
652 */
653 protected function func_copy($cmds)
654 {
655 $sourceFileObject = $this->getFileObject($cmds['data']);
656 /** @var $targetFolderObject \TYPO3\CMS\Core\Resource\Folder */
657 $targetFolderObject = $this->getFileObject($cmds['target']);
658 // Basic check
659 if (!$targetFolderObject instanceof Folder) {
660 $this->writeLog(2, 2, 100, 'Destination "%s" was not a directory', [$cmds['target']]);
661 $this->addMessageToFlashMessageQueue('FileUtility.DestinationWasNotADirectory', [$cmds['target']]);
662 return false;
663 }
664 // If this is TRUE, we append _XX to the file name if
665 $appendSuffixOnConflict = (string)$cmds['altName'];
666 $resultObject = null;
667 $conflictMode = $appendSuffixOnConflict !== '' ? DuplicationBehavior::RENAME : DuplicationBehavior::CANCEL;
668 // Copying the file
669 if ($sourceFileObject instanceof File) {
670 try {
671 $resultObject = $sourceFileObject->copyTo($targetFolderObject, null, $conflictMode);
672 } catch (InsufficientUserPermissionsException $e) {
673 $this->writeLog(2, 1, 114, 'You are not allowed to copy files', []);
674 $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToCopyFiles');
675 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientFileAccessPermissionsException $e) {
676 $this->writeLog(2, 1, 110, 'Could not access all necessary resources. Source file or destination maybe was not within your mountpoints? T="%s", D="%s"', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
677 $this->addMessageToFlashMessageQueue('FileUtility.CouldNotAccessAllNecessaryResources', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
678 } catch (IllegalFileExtensionException $e) {
679 $this->writeLog(2, 1, 111, 'Extension of file name "%s" is not allowed in "%s"!', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
680 $this->addMessageToFlashMessageQueue('FileUtility.ExtensionOfFileNameIsNotAllowedIn', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
681 } catch (ExistingTargetFileNameException $e) {
682 $this->writeLog(2, 1, 112, 'File "%s" already exists in folder "%s"!', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
683 $this->addMessageToFlashMessageQueue('FileUtility.FileAlreadyExistsInFolder', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
684 } catch (\BadMethodCallException $e) {
685 $this->writeLog(3, 1, 128, 'The function to copy a file between storages is not yet implemented', []);
686 $this->addMessageToFlashMessageQueue('FileUtility.TheFunctionToCopyAFileBetweenStoragesIsNotYetImplemented');
687 } catch (\RuntimeException $e) {
688 $this->writeLog(2, 2, 109, 'File "%s" WAS NOT copied to "%s"! Write-permission problem?', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
689 $this->addMessageToFlashMessageQueue('FileUtility.FileWasNotCopiedTo', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
690 }
691 if ($resultObject) {
692 $this->writeLog(2, 0, 1, 'File "%s" copied to "%s"', [$sourceFileObject->getIdentifier(), $resultObject->getIdentifier()]);
693 $this->addMessageToFlashMessageQueue('FileUtility.FileCopiedTo', [$sourceFileObject->getIdentifier(), $resultObject->getIdentifier()], FlashMessage::OK);
694 }
695 } else {
696 // Else means this is a Folder
697 $sourceFolderObject = $sourceFileObject;
698 try {
699 $resultObject = $sourceFolderObject->copyTo($targetFolderObject, null, $conflictMode);
700 } catch (InsufficientUserPermissionsException $e) {
701 $this->writeLog(2, 1, 125, 'You are not allowed to copy directories', []);
702 $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToCopyDirectories');
703 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientFileAccessPermissionsException $e) {
704 $this->writeLog(2, 1, 110, 'Could not access all necessary resources. Source file or destination maybe was not within your mountpoints? T="%s", D="%s"', [$sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
705 $this->addMessageToFlashMessageQueue('FileUtility.CouldNotAccessAllNecessaryResources', [$sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
706 } catch (InsufficientFolderAccessPermissionsException $e) {
707 $this->writeLog(2, 1, 121, 'You don\'t have full access to the destination directory "%s"!', [$targetFolderObject->getIdentifier()]);
708 $this->addMessageToFlashMessageQueue('FileUtility.YouDontHaveFullAccessToTheDestinationDirectory', [$targetFolderObject->getIdentifier()]);
709 } catch (\TYPO3\CMS\Core\Resource\Exception\InvalidTargetFolderException $e) {
710 $this->writeLog(2, 1, 122, 'Cannot copy folder "%s" into target folder "%s", because the target folder is already within the folder to be copied!', [$sourceFolderObject->getName(), $targetFolderObject->getName()]);
711 $this->addMessageToFlashMessageQueue('FileUtility.CannotCopyFolderIntoTargetFolderBecauseTheTargetFolderIsAlreadyWithinTheFolderToBeCopied', [$sourceFolderObject->getName(), $targetFolderObject->getName()]);
712 } catch (ExistingTargetFolderException $e) {
713 $this->writeLog(2, 1, 123, 'Target "%s" already exists!', [$targetFolderObject->getIdentifier()]);
714 $this->addMessageToFlashMessageQueue('FileUtility.TargetAlreadyExists', [$targetFolderObject->getIdentifier()]);
715 } catch (\BadMethodCallException $e) {
716 $this->writeLog(3, 1, 129, 'The function to copy a folder between storages is not yet implemented', []);
717 $this->addMessageToFlashMessageQueue('FileUtility.TheFunctionToCopyAFolderBetweenStoragesIsNotYetImplemented');
718 } catch (\RuntimeException $e) {
719 $this->writeLog(2, 2, 119, 'Directory "%s" WAS NOT copied to "%s"! Write-permission problem?', [$sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
720 $this->addMessageToFlashMessageQueue('FileUtility.DirectoryWasNotCopiedTo', [$sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
721 }
722 if ($resultObject) {
723 $this->writeLog(2, 0, 2, 'Directory "%s" copied to "%s"', [$sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
724 $this->addMessageToFlashMessageQueue('FileUtility.DirectoryCopiedTo', [$sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()], FlashMessage::OK);
725 }
726 }
727 return $resultObject;
728 }
729
730 /**
731 * Moving files and folders (action=3)
732 *
733 * $cmds['data'] (string): The file/folder to move
734 * + example "4:mypath/tomyfolder/myfile.jpg")
735 * + for backwards compatibility: the identifier was the path+filename
736 * $cmds['target'] (string): The path where to move to.
737 * + example "2:targetpath/targetfolder/"
738 * $cmds['altName'] (string): Use an alternative name if the target already exists
739 *
740 * @param array $cmds Command details as described above
741 * @return \TYPO3\CMS\Core\Resource\File
742 */
743 protected function func_move($cmds)
744 {
745 $sourceFileObject = $this->getFileObject($cmds['data']);
746 $targetFolderObject = $this->getFileObject($cmds['target']);
747 // Basic check
748 if (!$targetFolderObject instanceof Folder) {
749 $this->writeLog(3, 2, 100, 'Destination "%s" was not a directory', [$cmds['target']]);
750 $this->addMessageToFlashMessageQueue('FileUtility.DestinationWasNotADirectory', [$cmds['target']]);
751 return false;
752 }
753 $alternativeName = (string)$cmds['altName'];
754 $resultObject = null;
755 // Moving the file
756 if ($sourceFileObject instanceof File) {
757 try {
758 if ($alternativeName !== '') {
759 // Don't allow overwriting existing files, but find a new name
760 $resultObject = $sourceFileObject->moveTo($targetFolderObject, $alternativeName, DuplicationBehavior::RENAME);
761 } else {
762 // Don't allow overwriting existing files
763 $resultObject = $sourceFileObject->moveTo($targetFolderObject, null, DuplicationBehavior::CANCEL);
764 }
765 $this->writeLog(3, 0, 1, 'File "%s" moved to "%s"', [$sourceFileObject->getIdentifier(), $resultObject->getIdentifier()]);
766 $this->addMessageToFlashMessageQueue('FileUtility.FileMovedTo', [$sourceFileObject->getIdentifier(), $resultObject->getIdentifier()], FlashMessage::OK);
767 } catch (InsufficientUserPermissionsException $e) {
768 $this->writeLog(3, 1, 114, 'You are not allowed to move files', []);
769 $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToMoveFiles');
770 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientFileAccessPermissionsException $e) {
771 $this->writeLog(3, 1, 110, 'Could not access all necessary resources. Source file or destination maybe was not within your mountpoints? T="%s", D="%s"', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
772 $this->addMessageToFlashMessageQueue('FileUtility.CouldNotAccessAllNecessaryResources', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
773 } catch (IllegalFileExtensionException $e) {
774 $this->writeLog(3, 1, 111, 'Extension of file name "%s" is not allowed in "%s"!', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
775 $this->addMessageToFlashMessageQueue('FileUtility.ExtensionOfFileNameIsNotAllowedIn', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
776 } catch (ExistingTargetFileNameException $e) {
777 $this->writeLog(3, 1, 112, 'File "%s" already exists in folder "%s"!', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
778 $this->addMessageToFlashMessageQueue('FileUtility.FileAlreadyExistsInFolder', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
779 } catch (\BadMethodCallException $e) {
780 $this->writeLog(3, 1, 126, 'The function to move a file between storages is not yet implemented', []);
781 $this->addMessageToFlashMessageQueue('FileUtility.TheFunctionToMoveAFileBetweenStoragesIsNotYetImplemented');
782 } catch (\RuntimeException $e) {
783 $this->writeLog(3, 2, 109, 'File "%s" WAS NOT copied to "%s"! Write-permission problem?', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
784 $this->addMessageToFlashMessageQueue('FileUtility.FileWasNotCopiedTo', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
785 }
786 } else {
787 // Else means this is a Folder
788 $sourceFolderObject = $sourceFileObject;
789 try {
790 if ($alternativeName !== '') {
791 // Don't allow overwriting existing files, but find a new name
792 $resultObject = $sourceFolderObject->moveTo($targetFolderObject, $alternativeName, DuplicationBehavior::RENAME);
793 } else {
794 // Don't allow overwriting existing files
795 $resultObject = $sourceFolderObject->moveTo($targetFolderObject, null, DuplicationBehavior::RENAME);
796 }
797 $this->writeLog(3, 0, 2, 'Directory "%s" moved to "%s"', [$sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
798 $this->addMessageToFlashMessageQueue('FileUtility.DirectoryMovedTo', [$sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()], FlashMessage::OK);
799 } catch (InsufficientUserPermissionsException $e) {
800 $this->writeLog(3, 1, 125, 'You are not allowed to move directories', []);
801 $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToMoveDirectories');
802 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientFileAccessPermissionsException $e) {
803 $this->writeLog(3, 1, 110, 'Could not access all necessary resources. Source file or destination maybe was not within your mountpoints? T="%s", D="%s"', [$sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
804 $this->addMessageToFlashMessageQueue('FileUtility.CouldNotAccessAllNecessaryResources', [$sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
805 } catch (InsufficientFolderAccessPermissionsException $e) {
806 $this->writeLog(3, 1, 121, 'You don\'t have full access to the destination directory "%s"!', [$targetFolderObject->getIdentifier()]);
807 $this->addMessageToFlashMessageQueue('FileUtility.YouDontHaveFullAccessToTheDestinationDirectory', [$targetFolderObject->getIdentifier()]);
808 } catch (\TYPO3\CMS\Core\Resource\Exception\InvalidTargetFolderException $e) {
809 $this->writeLog(3, 1, 122, 'Cannot move folder "%s" into target folder "%s", because the target folder is already within the folder to be moved!', [$sourceFolderObject->getName(), $targetFolderObject->getName()]);
810 $this->addMessageToFlashMessageQueue('FileUtility.CannotMoveFolderIntoTargetFolderBecauseTheTargetFolderIsAlreadyWithinTheFolderToBeMoved', [$sourceFolderObject->getName(), $targetFolderObject->getName()]);
811 } catch (ExistingTargetFolderException $e) {
812 $this->writeLog(3, 1, 123, 'Target "%s" already exists!', [$targetFolderObject->getIdentifier()]);
813 $this->addMessageToFlashMessageQueue('FileUtility.TargetAlreadyExists', [$targetFolderObject->getIdentifier()]);
814 } catch (\BadMethodCallException $e) {
815 $this->writeLog(3, 1, 127, 'The function to move a folder between storages is not yet implemented', []);
816 $this->addMessageToFlashMessageQueue('FileUtility.TheFunctionToMoveAFolderBetweenStoragesIsNotYetImplemented', []);
817 } catch (\RuntimeException $e) {
818 $this->writeLog(3, 2, 119, 'Directory "%s" WAS NOT moved to "%s"! Write-permission problem?', [$sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
819 $this->addMessageToFlashMessageQueue('FileUtility.DirectoryWasNotMovedTo', [$sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
820 }
821 }
822 return $resultObject;
823 }
824
825 /**
826 * Renaming files or foldes (action=5)
827 *
828 * $cmds['data'] (string): The file/folder to copy
829 * + example "4:mypath/tomyfolder/myfile.jpg")
830 * + for backwards compatibility: the identifier was the path+filename
831 * $cmds['target'] (string): New name of the file/folder
832 *
833 * @param array $cmds Command details as described above
834 * @return \TYPO3\CMS\Core\Resource\File Returns the new file upon success
835 */
836 public function func_rename($cmds)
837 {
838 $sourceFileObject = $this->getFileObject($cmds['data']);
839 $sourceFile = $sourceFileObject->getName();
840 $targetFile = $cmds['target'];
841 $resultObject = null;
842 if ($sourceFileObject instanceof File) {
843 try {
844 // Try to rename the File
845 $resultObject = $sourceFileObject->rename($targetFile);
846 $this->writeLog(5, 0, 1, 'File renamed from "%s" to "%s"', [$sourceFile, $targetFile]);
847 if ($sourceFile === $targetFile) {
848 $this->addMessageToFlashMessageQueue('FileUtility.FileRenamedSameName', [$sourceFile], FlashMessage::INFO);
849 } else {
850 $this->addMessageToFlashMessageQueue('FileUtility.FileRenamedFromTo', [$sourceFile, $targetFile], FlashMessage::OK);
851 }
852 } catch (InsufficientUserPermissionsException $e) {
853 $this->writeLog(5, 1, 102, 'You are not allowed to rename files!', []);
854 $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToRenameFiles');
855 } catch (IllegalFileExtensionException $e) {
856 $this->writeLog(5, 1, 101, 'Extension of file name "%s" or "%s" was not allowed!', [$sourceFileObject->getName(), $targetFile]);
857 $this->addMessageToFlashMessageQueue('FileUtility.ExtensionOfFileNameOrWasNotAllowed', [$sourceFileObject->getName(), $targetFile]);
858 } catch (ExistingTargetFileNameException $e) {
859 $this->writeLog(5, 1, 120, 'Destination "%s" existed already!', [$targetFile]);
860 $this->addMessageToFlashMessageQueue('FileUtility.DestinationExistedAlready', [$targetFile]);
861 } catch (NotInMountPointException $e) {
862 $this->writeLog(5, 1, 121, 'Destination path "%s" was not within your mountpoints!', [$targetFile]);
863 $this->addMessageToFlashMessageQueue('FileUtility.DestinationPathWasNotWithinYourMountpoints', [$targetFile]);
864 } catch (\RuntimeException $e) {
865 $this->writeLog(5, 1, 100, 'File "%s" was not renamed! Write-permission problem in "%s"?', [$sourceFileObject->getName(), $targetFile]);
866 $this->addMessageToFlashMessageQueue('FileUtility.FileWasNotRenamed', [$sourceFileObject->getName(), $targetFile]);
867 }
868 } else {
869 // Else means this is a Folder
870 try {
871 // Try to rename the Folder
872 $resultObject = $sourceFileObject->rename($targetFile);
873 $this->writeLog(5, 0, 2, 'Directory renamed from "%s" to "%s"', [$sourceFile, $targetFile]);
874 if ($sourceFile === $targetFile) {
875 $this->addMessageToFlashMessageQueue('FileUtility.DirectoryRenamedSameName', [$sourceFile], FlashMessage::INFO);
876 } else {
877 $this->addMessageToFlashMessageQueue('FileUtility.DirectoryRenamedFromTo', [$sourceFile, $targetFile], FlashMessage::OK);
878 }
879 } catch (InsufficientUserPermissionsException $e) {
880 $this->writeLog(5, 1, 111, 'You are not allowed to rename directories!', []);
881 $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToRenameDirectories');
882 } catch (ExistingTargetFileNameException $e) {
883 $this->writeLog(5, 1, 120, 'Destination "%s" existed already!', [$targetFile]);
884 $this->addMessageToFlashMessageQueue('FileUtility.DestinationExistedAlready', [$targetFile]);
885 } catch (NotInMountPointException $e) {
886 $this->writeLog(5, 1, 121, 'Destination path "%s" was not within your mountpoints!', [$targetFile]);
887 $this->addMessageToFlashMessageQueue('FileUtility.DestinationPathWasNotWithinYourMountpoints', [$targetFile]);
888 } catch (\RuntimeException $e) {
889 $this->writeLog(5, 1, 110, 'Directory "%s" was not renamed! Write-permission problem in "%s"?', [$sourceFileObject->getName(), $targetFile]);
890 $this->addMessageToFlashMessageQueue('FileUtility.DirectoryWasNotRenamed', [$sourceFileObject->getName(), $targetFile]);
891 }
892 }
893 return $resultObject;
894 }
895
896 /**
897 * This creates a new folder. (action=6)
898 *
899 * $cmds['data'] (string): The new folder name
900 * $cmds['target'] (string): The path where to copy to.
901 * + example "2:targetpath/targetfolder/"
902 *
903 * @param array $cmds Command details as described above
904 * @return \TYPO3\CMS\Core\Resource\Folder Returns the new foldername upon success
905 */
906 public function func_newfolder($cmds)
907 {
908 $targetFolderObject = $this->getFileObject($cmds['target']);
909 if (!$targetFolderObject instanceof Folder) {
910 $this->writeLog(6, 2, 104, 'Destination "%s" was not a directory', [$cmds['target']]);
911 $this->addMessageToFlashMessageQueue('FileUtility.DestinationWasNotADirectory', [$cmds['target']]);
912 return false;
913 }
914 $resultObject = null;
915 $folderName = $cmds['data'];
916 try {
917 $resultObject = $targetFolderObject->createFolder($folderName);
918 $this->writeLog(6, 0, 1, 'Directory "%s" created in "%s"', [$folderName, $targetFolderObject->getIdentifier()]);
919 $this->addMessageToFlashMessageQueue('FileUtility.DirectoryCreatedIn', [$folderName, $targetFolderObject->getIdentifier()], FlashMessage::OK);
920 } catch (\TYPO3\CMS\Core\Resource\Exception\InvalidFileNameException $e) {
921 $this->writeLog(6, 1, 104, 'Invalid folder name "%s"!', [$folderName]);
922 $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToCreateDirectories', [$folderName]);
923 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientFolderWritePermissionsException $e) {
924 $this->writeLog(6, 1, 103, 'You are not allowed to create directories!', []);
925 $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToCreateDirectories');
926 } catch (\TYPO3\CMS\Core\Resource\Exception\NotInMountPointException $e) {
927 $this->writeLog(6, 1, 102, 'Destination path "%s" was not within your mountpoints!', [$targetFolderObject->getIdentifier()]);
928 $this->addMessageToFlashMessageQueue('FileUtility.DestinationPathWasNotWithinYourMountpoints', [$targetFolderObject->getIdentifier()]);
929 } catch (\TYPO3\CMS\Core\Resource\Exception\ExistingTargetFolderException $e) {
930 $this->writeLog(6, 1, 101, 'File or directory "%s" existed already!', [$folderName]);
931 $this->addMessageToFlashMessageQueue('FileUtility.FileOrDirectoryExistedAlready', [$folderName]);
932 } catch (\RuntimeException $e) {
933 $this->writeLog(6, 1, 100, 'Directory "%s" not created. Write-permission problem in "%s"?', [$folderName, $targetFolderObject->getIdentifier()]);
934 $this->addMessageToFlashMessageQueue('FileUtility.DirectoryNotCreated', [$folderName, $targetFolderObject->getIdentifier()]);
935 }
936 return $resultObject;
937 }
938
939 /**
940 * This creates a new file. (action=8)
941 * $cmds['data'] (string): The new file name
942 * $cmds['target'] (string): The path where to create it.
943 * + example "2:targetpath/targetfolder/"
944 *
945 * @param array $cmds Command details as described above
946 * @return string Returns the new filename upon success
947 */
948 public function func_newfile($cmds)
949 {
950 $targetFolderObject = $this->getFileObject($cmds['target']);
951 if (!$targetFolderObject instanceof Folder) {
952 $this->writeLog(8, 2, 104, 'Destination "%s" was not a directory', [$cmds['target']]);
953 $this->addMessageToFlashMessageQueue('FileUtility.DestinationWasNotADirectory', [$cmds['target']]);
954 return false;
955 }
956 $resultObject = null;
957 $fileName = $cmds['data'];
958 try {
959 $resultObject = $targetFolderObject->createFile($fileName);
960 $this->writeLog(8, 0, 1, 'File created: "%s"', [$fileName]);
961 $this->addMessageToFlashMessageQueue('FileUtility.FileCreated', [$fileName], FlashMessage::OK);
962 } catch (IllegalFileExtensionException $e) {
963 $this->writeLog(8, 1, 106, 'Extension of file "%s" was not allowed!', [$fileName]);
964 $this->addMessageToFlashMessageQueue('FileUtility.ExtensionOfFileWasNotAllowed', [$fileName]);
965 } catch (InsufficientFolderWritePermissionsException $e) {
966 $this->writeLog(8, 1, 103, 'You are not allowed to create files!', []);
967 $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToCreateFiles');
968 } catch (NotInMountPointException $e) {
969 $this->writeLog(8, 1, 102, 'Destination path "%s" was not within your mountpoints!', [$targetFolderObject->getIdentifier()]);
970 $this->addMessageToFlashMessageQueue('FileUtility.DestinationPathWasNotWithinYourMountpoints', [$targetFolderObject->getIdentifier()]);
971 } catch (ExistingTargetFileNameException $e) {
972 $this->writeLog(8, 1, 101, 'File existed already in "%s"!', [$targetFolderObject->getIdentifier()]);
973 $this->addMessageToFlashMessageQueue('FileUtility.FileExistedAlreadyIn', [$targetFolderObject->getIdentifier()]);
974 } catch (InvalidFileNameException $e) {
975 $this->writeLog(8, 1, 106, 'File name "%s" was not allowed!', [$fileName]);
976 $this->addMessageToFlashMessageQueue('FileUtility.FileNameWasNotAllowed', [$fileName]);
977 } catch (\RuntimeException $e) {
978 $this->writeLog(8, 1, 100, 'File "%s" was not created! Write-permission problem in "%s"?', [$fileName, $targetFolderObject->getIdentifier()]);
979 $this->addMessageToFlashMessageQueue('FileUtility.FileWasNotCreated', [$fileName, $targetFolderObject->getIdentifier()]);
980 }
981 return $resultObject;
982 }
983
984 /**
985 * Editing textfiles or folders (action=9)
986 *
987 * @param array $cmds $cmds['data'] is the new content. $cmds['target'] is the target (file or dir)
988 * @return bool Returns TRUE on success
989 */
990 public function func_edit($cmds)
991 {
992 // Example identifier for $cmds['target'] => "4:mypath/tomyfolder/myfile.jpg"
993 // for backwards compatibility: the combined file identifier was the path+filename
994 $fileIdentifier = $cmds['target'];
995 $fileObject = $this->getFileObject($fileIdentifier);
996 // Example identifier for $cmds['target'] => "2:targetpath/targetfolder/"
997 $content = $cmds['data'];
998 if (!$fileObject instanceof File) {
999 $this->writeLog(9, 2, 123, 'Target "%s" was not a file!', [$fileIdentifier]);
1000 $this->addMessageToFlashMessageQueue('FileUtility.TargetWasNotAFile', [$fileIdentifier]);
1001 return false;
1002 }
1003 $extList = $GLOBALS['TYPO3_CONF_VARS']['SYS']['textfile_ext'];
1004 if (!GeneralUtility::inList($extList, $fileObject->getExtension())) {
1005 $this->writeLog(9, 1, 102, 'File extension "%s" is not a textfile format! (%s)', [$fileObject->getExtension(), $extList]);
1006 $this->addMessageToFlashMessageQueue('FileUtility.FileExtensionIsNotATextfileFormat', [$fileObject->getExtension(), $extList]);
1007 return false;
1008 }
1009 try {
1010 $fileObject->setContents($content);
1011 clearstatcache();
1012 $this->writeLog(9, 0, 1, 'File saved to "%s", bytes: %s, MD5: %s ', [$fileObject->getIdentifier(), $fileObject->getSize(), md5($content)]);
1013 $this->addMessageToFlashMessageQueue('FileUtility.FileSavedToBytesMd5', [$fileObject->getIdentifier(), $fileObject->getSize(), md5($content)], FlashMessage::OK);
1014 return true;
1015 } catch (InsufficientUserPermissionsException $e) {
1016 $this->writeLog(9, 1, 104, 'You are not allowed to edit files!', []);
1017 $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToEditFiles');
1018 return false;
1019 } catch (InsufficientFileWritePermissionsException $e) {
1020 $this->writeLog(9, 1, 100, 'File "%s" was not saved! Write-permission problem?', [$fileObject->getIdentifier()]);
1021 $this->addMessageToFlashMessageQueue('FileUtility.FileWasNotSaved', [$fileObject->getIdentifier()]);
1022 return false;
1023 } catch (IllegalFileExtensionException $e) {
1024 $this->writeLog(9, 1, 100, 'File "%s" was not saved! File extension rejected!', [$fileObject->getIdentifier()]);
1025 $this->addMessageToFlashMessageQueue('FileUtility.FileWasNotSaved', [$fileObject->getIdentifier()]);
1026 return false;
1027 }
1028 }
1029
1030 /**
1031 * Upload of files (action=1)
1032 * when having multiple uploads (HTML5-style), the array $_FILES looks like this:
1033 * Array(
1034 * [upload_1] => Array(
1035 * [name] => Array(
1036 * [0] => GData - Content-Elements and Media-Gallery.pdf
1037 * [1] => CMS Expo 2011.txt
1038 * )
1039 * [type] => Array(
1040 * [0] => application/pdf
1041 * [1] => text/plain
1042 * )
1043 * [tmp_name] => Array(
1044 * [0] => /Applications/MAMP/tmp/php/phpNrOB43
1045 * [1] => /Applications/MAMP/tmp/php/phpD2HQAK
1046 * )
1047 * [size] => Array(
1048 * [0] => 373079
1049 * [1] => 1291
1050 * )
1051 * )
1052 * )
1053 * in HTML you'd need sth like this: <input type="file" name="upload_1[]" multiple="true" />
1054 *
1055 * @param array $cmds $cmds['data'] is the ID-number (points to the global var that holds the filename-ref
1056 * ($_FILES['upload_' . $id]['name']) . $cmds['target'] is the target directory, $cmds['charset']
1057 * is the the character set of the file name (utf-8 is needed for JS-interaction)
1058 * @return File[] | FALSE Returns an array of new file objects upon success. False otherwise
1059 */
1060 public function func_upload($cmds)
1061 {
1062 $uploadPosition = $cmds['data'];
1063 $uploadedFileData = $_FILES['upload_' . $uploadPosition];
1064 if (empty($uploadedFileData['name']) || is_array($uploadedFileData['name']) && empty($uploadedFileData['name'][0])) {
1065 $this->writeLog(1, 2, 108, 'No file was uploaded!', []);
1066 $this->addMessageToFlashMessageQueue('FileUtility.NoFileWasUploaded');
1067 return false;
1068 }
1069 // Example identifier for $cmds['target'] => "2:targetpath/targetfolder/"
1070 $targetFolderObject = $this->getFileObject($cmds['target']);
1071 // Uploading with non HTML-5-style, thus, make an array out of it, so we can loop over it
1072 if (!is_array($uploadedFileData['name'])) {
1073 $uploadedFileData = [
1074 'name' => [$uploadedFileData['name']],
1075 'type' => [$uploadedFileData['type']],
1076 'tmp_name' => [$uploadedFileData['tmp_name']],
1077 'size' => [$uploadedFileData['size']]
1078 ];
1079 }
1080 $resultObjects = [];
1081 $numberOfUploadedFilesForPosition = count($uploadedFileData['name']);
1082 // Loop through all uploaded files
1083 for ($i = 0; $i < $numberOfUploadedFilesForPosition; $i++) {
1084 $fileInfo = [
1085 'name' => $uploadedFileData['name'][$i],
1086 'type' => $uploadedFileData['type'][$i],
1087 'tmp_name' => $uploadedFileData['tmp_name'][$i],
1088 'size' => $uploadedFileData['size'][$i]
1089 ];
1090 try {
1091 /** @var $fileObject File */
1092 $fileObject = $targetFolderObject->addUploadedFile($fileInfo, (string)$this->existingFilesConflictMode);
1093 $fileObject = ResourceFactory::getInstance()->getFileObjectByStorageAndIdentifier($targetFolderObject->getStorage()->getUid(), $fileObject->getIdentifier());
1094 if ($this->existingFilesConflictMode->equals(DuplicationBehavior::REPLACE)) {
1095 $this->getIndexer($fileObject->getStorage())->updateIndexEntry($fileObject);
1096 }
1097 $resultObjects[] = $fileObject;
1098 $this->internalUploadMap[$uploadPosition] = $fileObject->getCombinedIdentifier();
1099 $this->writeLog(1, 0, 1, 'Uploading file "%s" to "%s"', [$fileInfo['name'], $targetFolderObject->getIdentifier()]);
1100 $this->addMessageToFlashMessageQueue('FileUtility.UploadingFileTo', [$fileInfo['name'], $targetFolderObject->getIdentifier()], FlashMessage::OK);
1101 } catch (InsufficientFileWritePermissionsException $e) {
1102 $this->writeLog(1, 1, 107, 'You are not allowed to override "%s"!', [$fileInfo['name']]);
1103 $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToOverride', [$fileInfo['name']]);
1104 } catch (UploadException $e) {
1105 $this->writeLog(1, 2, 106, 'The upload has failed, no uploaded file found!', []);
1106 $this->addMessageToFlashMessageQueue('FileUtility.TheUploadHasFailedNoUploadedFileFound');
1107 } catch (InsufficientUserPermissionsException $e) {
1108 $this->writeLog(1, 1, 105, 'You are not allowed to upload files!', []);
1109 $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToUploadFiles');
1110 } catch (UploadSizeException $e) {
1111 $this->writeLog(1, 1, 104, 'The uploaded file "%s" exceeds the size-limit', [$fileInfo['name']]);
1112 $this->addMessageToFlashMessageQueue('FileUtility.TheUploadedFileExceedsTheSize-limit', [$fileInfo['name']]);
1113 } catch (InsufficientFolderWritePermissionsException $e) {
1114 $this->writeLog(1, 1, 103, 'Destination path "%s" was not within your mountpoints!', [$targetFolderObject->getIdentifier()]);
1115 $this->addMessageToFlashMessageQueue('FileUtility.DestinationPathWasNotWithinYourMountpoints', [$targetFolderObject->getIdentifier()]);
1116 } catch (IllegalFileExtensionException $e) {
1117 $this->writeLog(1, 1, 102, 'Extension of file name "%s" is not allowed in "%s"!', [$fileInfo['name'], $targetFolderObject->getIdentifier()]);
1118 $this->addMessageToFlashMessageQueue('FileUtility.ExtensionOfFileNameIsNotAllowedIn', [$fileInfo['name'], $targetFolderObject->getIdentifier()]);
1119 } catch (ExistingTargetFileNameException $e) {
1120 $this->writeLog(1, 1, 101, 'No unique filename available in "%s"!', [$targetFolderObject->getIdentifier()]);
1121 $this->addMessageToFlashMessageQueue('FileUtility.NoUniqueFilenameAvailableIn', [$targetFolderObject->getIdentifier()]);
1122 } catch (\RuntimeException $e) {
1123 $this->writeLog(1, 1, 100, 'Uploaded file could not be moved! Write-permission problem in "%s"?', [$targetFolderObject->getIdentifier()]);
1124 $this->addMessageToFlashMessageQueue('FileUtility.UploadedFileCouldNotBeMoved', [$targetFolderObject->getIdentifier()]);
1125 }
1126 }
1127
1128 return $resultObjects;
1129 }
1130
1131 /**
1132 * Replaces a file on the filesystem and changes the identifier of the persisted file object in sys_file if
1133 * keepFilename is not checked. If keepFilename is checked, only the file content will be replaced.
1134 *
1135 * @param array $cmdArr
1136 * @return array|bool
1137 * @throws Exception\InsufficientFileAccessPermissionsException
1138 * @throws Exception\InvalidFileException
1139 * @throws \RuntimeException
1140 */
1141 protected function replaceFile(array $cmdArr)
1142 {
1143 $uploadPosition = $cmdArr['data'];
1144 $fileInfo = $_FILES['replace_' . $uploadPosition];
1145 if (empty($fileInfo['name'])) {
1146 $this->writeLog(1, 2, 108, 'No file was uploaded for replacing!', []);
1147 $this->addMessageToFlashMessageQueue('FileUtility.NoFileWasUploadedForReplacing');
1148 return false;
1149 }
1150
1151 $keepFileName = ($cmdArr['keepFilename'] == 1) ? true : false;
1152 $resultObjects = [];
1153
1154 try {
1155 $fileObjectToReplace = $this->getFileObject($cmdArr['uid']);
1156 $folder = $fileObjectToReplace->getParentFolder();
1157 $resourceStorage = $fileObjectToReplace->getStorage();
1158
1159 $fileObject = $resourceStorage->addUploadedFile($fileInfo, $folder, $fileObjectToReplace->getName(), DuplicationBehavior::REPLACE);
1160
1161 // Check if there is a file that is going to be uploaded that has a different name as the replacing one
1162 // but exists in that folder as well.
1163 // rename to another name, but check if the name is already given
1164 if ($keepFileName === false) {
1165 // if a file with the same name already exists, we need to change it to _01 etc.
1166 // if the file does not exist, we can do a simple rename
1167 $resourceStorage->moveFile($fileObject, $folder, $fileInfo['name'], DuplicationBehavior::RENAME);
1168 }
1169
1170 $resultObjects[] = $fileObject;
1171 $this->internalUploadMap[$uploadPosition] = $fileObject->getCombinedIdentifier();
1172
1173 $this->writeLog(1, 0, 1, 'Replacing file "%s" to "%s"', [$fileInfo['name'], $fileObjectToReplace->getIdentifier()]);
1174 $this->addMessageToFlashMessageQueue('FileUtility.ReplacingFileTo', [$fileInfo['name'], $fileObjectToReplace->getIdentifier()], FlashMessage::OK);
1175 } catch (InsufficientFileWritePermissionsException $e) {
1176 $this->writeLog(1, 1, 107, 'You are not allowed to override "%s"!', [$fileInfo['name']]);
1177 $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToOverride', [$fileInfo['name']]);
1178 } catch (UploadException $e) {
1179 $this->writeLog(1, 2, 106, 'The upload has failed, no uploaded file found!', []);
1180 $this->addMessageToFlashMessageQueue('FileUtility.TheUploadHasFailedNoUploadedFileFound');
1181 } catch (InsufficientUserPermissionsException $e) {
1182 $this->writeLog(1, 1, 105, 'You are not allowed to upload files!', []);
1183 $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToUploadFiles');
1184 } catch (UploadSizeException $e) {
1185 $this->writeLog(1, 1, 104, 'The uploaded file "%s" exceeds the size-limit', [$fileInfo['name']]);
1186 $this->addMessageToFlashMessageQueue('FileUtility.TheUploadedFileExceedsTheSize-limit', [$fileInfo['name']]);
1187 } catch (InsufficientFolderWritePermissionsException $e) {
1188 $this->writeLog(1, 1, 103, 'Destination path "%s" was not within your mountpoints!', [$fileObjectToReplace->getIdentifier()]);
1189 $this->addMessageToFlashMessageQueue('FileUtility.DestinationPathWasNotWithinYourMountpoints', [$fileObjectToReplace->getIdentifier()]);
1190 } catch (IllegalFileExtensionException $e) {
1191 $this->writeLog(1, 1, 102, 'Extension of file name "%s" is not allowed in "%s"!', [$fileInfo['name'], $fileObjectToReplace->getIdentifier()]);
1192 $this->addMessageToFlashMessageQueue('FileUtility.ExtensionOfFileNameIsNotAllowedIn', [$fileInfo['name'], $fileObjectToReplace->getIdentifier()]);
1193 } catch (ExistingTargetFileNameException $e) {
1194 $this->writeLog(1, 1, 101, 'No unique filename available in "%s"!', [$fileObjectToReplace->getIdentifier()]);
1195 $this->addMessageToFlashMessageQueue('FileUtility.NoUniqueFilenameAvailableIn', [$fileObjectToReplace->getIdentifier()]);
1196 } catch (\RuntimeException $e) {
1197 throw $e;
1198 }
1199 return $resultObjects;
1200 }
1201
1202 /**
1203 * Add flash message to message queue
1204 *
1205 * @param FlashMessage $flashMessage
1206 * @return void
1207 */
1208 protected function addFlashMessage(FlashMessage $flashMessage)
1209 {
1210 /** @var $flashMessageService FlashMessageService */
1211 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
1212
1213 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
1214 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
1215 $defaultFlashMessageQueue->enqueue($flashMessage);
1216 }
1217
1218 /**
1219 * Gets Indexer
1220 *
1221 * @param \TYPO3\CMS\Core\Resource\ResourceStorage $storage
1222 * @return \TYPO3\CMS\Core\Resource\Index\Indexer
1223 */
1224 protected function getIndexer(ResourceStorage $storage)
1225 {
1226 return GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\Index\Indexer::class, $storage);
1227 }
1228
1229 /**
1230 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
1231 */
1232 protected function getBackendUser()
1233 {
1234 return $GLOBALS['BE_USER'];
1235 }
1236
1237 /**
1238 * Returns LanguageService
1239 *
1240 * @return LanguageService
1241 */
1242 protected function getLanguageService()
1243 {
1244 return $GLOBALS['LANG'];
1245 }
1246 }