ExtendedFileUtility.php 75.3 KB
Newer Older
1
<?php
2

3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
7
8
 * It is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, either version 2
 * of the License, or any later version.
9
 *
10
11
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
14
 * The TYPO3 project - inspiring people to share!
 */
15

16
17
namespace TYPO3\CMS\Core\Utility\File;

18
use Psr\EventDispatcher\EventDispatcherInterface;
19
use Psr\Http\Message\ServerRequestInterface;
20
use TYPO3\CMS\Backend\Utility\BackendUtility;
21
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
22
use TYPO3\CMS\Core\Database\Connection;
23
use TYPO3\CMS\Core\Database\ConnectionPool;
24
use TYPO3\CMS\Core\Http\ApplicationType;
25
use TYPO3\CMS\Core\Localization\LanguageService;
26
use TYPO3\CMS\Core\Messaging\FlashMessage;
27
use TYPO3\CMS\Core\Messaging\FlashMessageService;
28
use TYPO3\CMS\Core\Resource\DuplicationBehavior;
29
use TYPO3\CMS\Core\Resource\Event\AfterFileCommandProcessedEvent;
30
use TYPO3\CMS\Core\Resource\Exception;
31
32
use TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException;
use TYPO3\CMS\Core\Resource\Exception\ExistingTargetFolderException;
33
use TYPO3\CMS\Core\Resource\Exception\FileOperationErrorException;
34
use TYPO3\CMS\Core\Resource\Exception\IllegalFileExtensionException;
35
use TYPO3\CMS\Core\Resource\Exception\InsufficientFileAccessPermissionsException;
36
37
38
39
use TYPO3\CMS\Core\Resource\Exception\InsufficientFileWritePermissionsException;
use TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException;
use TYPO3\CMS\Core\Resource\Exception\InsufficientFolderWritePermissionsException;
use TYPO3\CMS\Core\Resource\Exception\InsufficientUserPermissionsException;
40
use TYPO3\CMS\Core\Resource\Exception\InvalidFileException;
41
use TYPO3\CMS\Core\Resource\Exception\InvalidFileNameException;
42
use TYPO3\CMS\Core\Resource\Exception\InvalidTargetFolderException;
43
use TYPO3\CMS\Core\Resource\Exception\NotInMountPointException;
44
use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
45
46
use TYPO3\CMS\Core\Resource\Exception\UploadException;
use TYPO3\CMS\Core\Resource\Exception\UploadSizeException;
47
use TYPO3\CMS\Core\Resource\File;
48
use TYPO3\CMS\Core\Resource\Folder;
49
use TYPO3\CMS\Core\Resource\Index\Indexer;
50
51
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Resource\ResourceStorage;
52
53
54
use TYPO3\CMS\Core\SysLog\Action\File as SystemLogFileAction;
use TYPO3\CMS\Core\SysLog\Error as SystemLogErrorClassification;
use TYPO3\CMS\Core\SysLog\Type as SystemLogType;
55
use TYPO3\CMS\Core\Type\Exception\InvalidEnumerationValueException;
56
use TYPO3\CMS\Core\Utility\Exception\NotImplementedMethodException;
57
58
use TYPO3\CMS\Core\Utility\GeneralUtility;

59
/**
60
61
 * Contains functions for performing file operations like copying, pasting, uploading, moving,
 * deleting etc. through the TCE
62
 *
63
 * See document "TYPO3 Core API" for syntax
64
65
66
67
 *
 * This class contains functions primarily used by tce_file.php (TYPO3 Core Engine for file manipulation)
 * Functions include copying, moving, deleting, uploading and so on...
 *
Benni Mack's avatar
Benni Mack committed
68
 * All fileoperations must be within the filemount paths of the user.
69
 *
Benni Mack's avatar
Benni Mack committed
70
71
 * @internal Since TYPO3 v10, this class should not be used anymore outside of TYPO3 Core, and is considered internal,
 * as the FAL API should be used instead.
72
 */
73
74
75
76
77
78
class ExtendedFileUtility extends BasicFileUtility
{
    /**
     * Defines behaviour when uploading files with names that already exist; possible values are
     * the values of the \TYPO3\CMS\Core\Resource\DuplicationBehavior enumeration
     *
79
     * @var DuplicationBehavior
80
81
     */
    protected $existingFilesConflictMode;
82

83
84
85
86
87
88
89
    /**
     * This array is self-explaining (look in the class below).
     * It grants access to the functions. This could be set from outside in order to enabled functions to users.
     * See also the function setActionPermissions() which takes input directly from the user-record
     *
     * @var array
     */
90
    public $actionPerms = [
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
        // File permissions
        'addFile' => false,
        'readFile' => false,
        'writeFile' => false,
        'copyFile' => false,
        'moveFile' => false,
        'renameFile' => false,
        'deleteFile' => false,
        // Folder permissions
        'addFolder' => false,
        'readFolder' => false,
        'writeFolder' => false,
        'copyFolder' => false,
        'moveFolder' => false,
        'renameFolder' => false,
        'deleteFolder' => false,
107
        'recursivedeleteFolder' => false,
108
    ];
109

110
111
112
113
114
    /**
     * Will contain map between upload ID and the final filename
     *
     * @var array
     */
115
    public $internalUploadMap = [];
116

117
118
119
120
121
122
123
    /**
     * Container for FlashMessages so they can be localized
     *
     * @var array
     */
    protected $flashMessages = [];

124
125
126
127
    /**
     * @var array
     */
    protected $fileCmdMap;
128

129
130
131
132
133
134
    /**
     * The File Factory
     *
     * @var \TYPO3\CMS\Core\Resource\ResourceFactory
     */
    protected $fileFactory;
135

136
137
138
139
140
141
142
143
144
    /**
     * Get existingFilesConflictMode
     *
     * @return string
     */
    public function getExistingFilesConflictMode()
    {
        return (string)$this->existingFilesConflictMode;
    }
145

146
147
148
    /**
     * Set existingFilesConflictMode
     *
149
     * @param DuplicationBehavior|string $existingFilesConflictMode Instance or constant of \TYPO3\CMS\Core\Resource\DuplicationBehavior
150
151
152
153
154
155
156
157
158
159
160
161
     * @throws Exception
     */
    public function setExistingFilesConflictMode($existingFilesConflictMode)
    {
        try {
            $this->existingFilesConflictMode = DuplicationBehavior::cast($existingFilesConflictMode);
        } catch (InvalidEnumerationValueException $e) {
            throw new Exception(
                sprintf(
                    'Invalid argument, received: "%s", expected a value from enumeration \TYPO3\CMS\Core\Resource\DuplicationBehavior (%s)',
                    $existingFilesConflictMode,
                    implode(', ', DuplicationBehavior::getConstants())
162
163
                ),
                1476046229
164
165
166
            );
        }
    }
167

168
169
170
171
172
173
174
175
    /**
     * Initialization of the class
     *
     * @param array $fileCmds Array with the commands to execute. See "TYPO3 Core API" document
     */
    public function start($fileCmds)
    {
        // Initialize Object Factory
176
        $this->fileFactory = GeneralUtility::makeInstance(ResourceFactory::class);
177
178
179
        // Initializing file processing commands:
        $this->fileCmdMap = $fileCmds;
    }
180

181
182
183
184
185
186
    /**
     * Sets the file action permissions.
     * If no argument is given, permissions of the currently logged in backend user are taken into account.
     *
     * @param array $permissions File Permissions.
     */
187
    public function setActionPermissions(array $permissions = [])
188
189
    {
        if (empty($permissions)) {
190
            $permissions = $this->getBackendUser()->getFilePermissions();
191
192
193
        }
        $this->actionPerms = $permissions;
    }
194

195
196
197
198
199
200
201
202
    /**
     * Processing the command array in $this->fileCmdMap
     *
     * @return mixed FALSE, if the file functions were not initialized
     * @throws \UnexpectedValueException
     */
    public function processData()
    {
203
        $result = [];
204
205
        if (is_array($this->fileCmdMap)) {
            // Check if there were uploads expected, but no one made
206
            if ($this->fileCmdMap['upload'] ?? false) {
207
208
                $uploads = $this->fileCmdMap['upload'];
                foreach ($uploads as $upload) {
209
                    if (empty($_FILES['upload_' . $upload['data']]['name'])
210
211
                        || (
                            is_array($_FILES['upload_' . $upload['data']]['name'])
212
                            && empty($_FILES['upload_' . $upload['data']]['name'][0])
213
214
215
216
217
218
                        )
                    ) {
                        unset($this->fileCmdMap['upload'][$upload['data']]);
                    }
                }
                if (empty($this->fileCmdMap['upload'])) {
219
                    $this->writeLog(SystemLogFileAction::UPLOAD, SystemLogErrorClassification::USER_ERROR, 'No file was uploaded');
220
                    $this->addMessageToFlashMessageQueue('FileUtility.NoFileWasUploaded');
221
222
                }
            }
223

224
            // Check if there were new folder names expected, but non given
225
            if ($this->fileCmdMap['newfolder'] ?? false) {
226
227
228
229
230
231
                foreach ($this->fileCmdMap['newfolder'] as $key => $cmdArr) {
                    if (empty($cmdArr['data'])) {
                        unset($this->fileCmdMap['newfolder'][$key]);
                    }
                }
                if (empty($this->fileCmdMap['newfolder'])) {
232
                    $this->writeLog(SystemLogFileAction::NEW_FOLDER, SystemLogErrorClassification::USER_ERROR, 'No name for new folder given');
233
                    $this->addMessageToFlashMessageQueue('FileUtility.NoNameForNewFolderGiven');
234
235
                }
            }
236

237
238
239
240
            // Traverse each set of actions
            foreach ($this->fileCmdMap as $action => $actionData) {
                // Traverse all action data. More than one file might be affected at the same time.
                if (is_array($actionData)) {
241
                    $result[$action] = [];
242
243
244
                    // We reset the array keys of $actionData to keep track of the corresponding
                    // result, while not changing the previous behaviour of $result[$action][].
                    foreach (array_values($actionData) as $key => $cmdArr) {
245
246
247
248
249
                        // Clear file stats
                        clearstatcache();
                        // Branch out based on command:
                        switch ($action) {
                            case 'delete':
250
                                $result[$action][$key] = $this->func_delete($cmdArr);
251
252
                                break;
                            case 'copy':
253
                                $result[$action][$key] = $this->func_copy($cmdArr);
254
255
                                break;
                            case 'move':
256
                                $result[$action][$key] = $this->func_move($cmdArr);
257
258
                                break;
                            case 'rename':
259
                                $result[$action][$key] = $this->func_rename($cmdArr);
260
261
                                break;
                            case 'newfolder':
262
                                $result[$action][$key] = $this->func_newfolder($cmdArr);
263
264
                                break;
                            case 'newfile':
265
                                $result[$action][$key] = $this->func_newfile($cmdArr);
266
267
                                break;
                            case 'editfile':
268
                                $result[$action][$key] = $this->func_edit($cmdArr);
269
270
                                break;
                            case 'upload':
271
                                $result[$action][$key] = $this->func_upload($cmdArr);
272
273
                                break;
                            case 'replace':
274
                                $result[$action][$key] = $this->replaceFile($cmdArr);
275
276
                                break;
                        }
277

278
                        GeneralUtility::makeInstance(EventDispatcherInterface::class)->dispatch(
279
280
                            new AfterFileCommandProcessedEvent([$action => $cmdArr], $result[$action][$key], (string)$this->existingFilesConflictMode)
                        );
281
282
283
284
285
286
                    }
                }
            }
        }
        return $result;
    }
287

288
289
    /**
     * @param int $action The action number. See the functions in the class for a hint. Eg. edit is '9', upload is '1' ...
290
291
292
     * @param int $severity The severity: 0 = message, 1 = error, 2 = System Error, 3 = security notice (admin)
     * @param string $message This is the default, raw error message in english
     * @param array $context Additional information when the log is shown
293
     */
294
    protected function writeLog(int $action, int $severity, string $message, array $context = []): void
295
    {
296
297
        if (!is_object($this->getBackendUser())) {
            return;
298
        }
299
        $this->getBackendUser()->writelog(SystemLogType::FILE, $action, $severity, 0, $message, $context);
300
    }
301

302
303
304
305
306
    /**
     * Adds a localized FlashMessage to the message queue
     *
     * @param string $localizationKey
     * @param array $replaceMarkers
307
     * @param int $severity
308
309
     * @throws \InvalidArgumentException
     */
310
    protected function addMessageToFlashMessageQueue($localizationKey, array $replaceMarkers = [], $severity = FlashMessage::ERROR)
311
    {
312
313
314
315
316
317
318
319
320
321
322
323
324
        if (($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface
            && ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isBackend()
        ) {
            $label = $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/fileMessages.xlf:' . $localizationKey);
            $message = vsprintf($label, $replaceMarkers);
            $flashMessage = GeneralUtility::makeInstance(
                FlashMessage::class,
                $message,
                '',
                $severity,
                true
            );
            $this->addFlashMessage($flashMessage);
325
        }
326
327
    }

328
329
330
331
332
333
334
335
336
337
338
339
340
341
    /*************************************
     *
     * File operation functions
     *
     **************************************/
    /**
     * Deleting files and folders (action=4)
     *
     * @param array $cmds $cmds['data'] is the file/folder to delete
     * @return bool Returns TRUE upon success
     */
    public function func_delete(array $cmds)
    {
        $result = false;
342
        // Example identifier for $cmds['data'] => "4:mypath/tomyfolder/myfile.jpg"
343
344
345
346
347
348
        // for backwards compatibility: the combined file identifier was the path+filename
        try {
            $fileObject = $this->getFileObject($cmds['data']);
        } catch (ResourceDoesNotExistException $e) {
            $flashMessage = GeneralUtility::makeInstance(
                FlashMessage::class,
349
                sprintf(
350
                    $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:message.description.fileNotFound'),
351
352
                    $cmds['data']
                ),
353
                $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:message.header.fileNotFound'),
354
355
356
357
                FlashMessage::ERROR,
                true
            );
            $this->addFlashMessage($flashMessage);
358

359
360
361
362
363
364
            return false;
        }
        // checks to delete the file
        if ($fileObject instanceof File) {
            // check if the file still has references
            // Exclude sys_file_metadata records as these are no use references
365
366
367
368
369
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
            $refIndexRecords = $queryBuilder
                ->select('tablename', 'recuid', 'ref_uid')
                ->from('sys_refindex')
                ->where(
370
371
372
373
374
375
376
377
378
379
380
381
                    $queryBuilder->expr()->eq(
                        'ref_table',
                        $queryBuilder->createNamedParameter('sys_file', \PDO::PARAM_STR)
                    ),
                    $queryBuilder->expr()->eq(
                        'ref_uid',
                        $queryBuilder->createNamedParameter($fileObject->getUid(), \PDO::PARAM_INT)
                    ),
                    $queryBuilder->expr()->neq(
                        'tablename',
                        $queryBuilder->createNamedParameter('sys_file_metadata', \PDO::PARAM_STR)
                    )
382
                )
383
                ->executeQuery()
384
                ->fetchAllAssociative();
385
386
            $deleteFile = true;
            if (!empty($refIndexRecords)) {
387
388
                $shortcutContent = [];
                $brokenReferences = [];
389

390
391
392
393
                foreach ($refIndexRecords as $fileReferenceRow) {
                    if ($fileReferenceRow['tablename'] === 'sys_file_reference') {
                        $row = $this->transformFileReferenceToRecordReference($fileReferenceRow);
                        $shortcutRecord = BackendUtility::getRecord($row['tablename'], $row['recuid']);
394

395
                        if ($shortcutRecord) {
396
                            $shortcutContent[] = '[record:' . $row['tablename'] . ':' . $row['recuid'] . ']';
397
398
399
                        } else {
                            $brokenReferences[] = $fileReferenceRow['ref_uid'];
                        }
400
401
                    } else {
                        $shortcutContent[] = '[record:' . $fileReferenceRow['tablename'] . ':' . $fileReferenceRow['recuid'] . ']';
402
403
404
405
406
407
                    }
                }
                if (!empty($brokenReferences)) {
                    // render a message that the file has broken references
                    $flashMessage = GeneralUtility::makeInstance(
                        FlashMessage::class,
408
409
                        sprintf($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:message.description.fileHasBrokenReferences'), count($brokenReferences)),
                        $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:message.header.fileHasBrokenReferences'),
410
411
412
413
414
415
416
417
418
                        FlashMessage::INFO,
                        true
                    );
                    $this->addFlashMessage($flashMessage);
                }
                if (!empty($shortcutContent)) {
                    // render a message that the file could not be deleted
                    $flashMessage = GeneralUtility::makeInstance(
                        FlashMessage::class,
419
420
                        sprintf($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:message.description.fileNotDeletedHasReferences'), $fileObject->getName()) . ' ' . implode(', ', $shortcutContent),
                        $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:message.header.fileNotDeletedHasReferences'),
421
422
423
424
425
426
427
                        FlashMessage::WARNING,
                        true
                    );
                    $this->addFlashMessage($flashMessage);
                    $deleteFile = false;
                }
            }
428

429
430
431
            if ($deleteFile) {
                try {
                    $result = $fileObject->delete();
432

433
434
435
                    // show the user that the file was deleted
                    $flashMessage = GeneralUtility::makeInstance(
                        FlashMessage::class,
436
437
                        sprintf($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:message.description.fileDeleted'), $fileObject->getName()),
                        $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:message.header.fileDeleted'),
438
439
440
441
442
                        FlashMessage::OK,
                        true
                    );
                    $this->addFlashMessage($flashMessage);
                    // Log success
443
                    $this->writeLog(SystemLogFileAction::DELETE, SystemLogErrorClassification::MESSAGE, 'File "{identifier}" deleted', ['identifier' => $fileObject->getIdentifier()]);
444
                } catch (InsufficientFileAccessPermissionsException $e) {
445
                    $this->writeLog(SystemLogFileAction::DELETE, SystemLogErrorClassification::USER_ERROR, 'You are not allowed to access the file "{identifier}"', ['identifier' => $fileObject->getIdentifier()]);
446
                    $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToAccessTheFile', [$fileObject->getIdentifier()]);
447
                } catch (NotInMountPointException $e) {
448
                    $this->writeLog(SystemLogFileAction::DELETE, SystemLogErrorClassification::USER_ERROR, 'Target "{identifier}" was not within your mountpoints"', ['identifier' => $fileObject->getIdentifier()]);
449
                    $this->addMessageToFlashMessageQueue('FileUtility.TargetWasNotWithinYourMountpoints', [$fileObject->getIdentifier()]);
450
                } catch (\RuntimeException $e) {
451
                    $this->writeLog(SystemLogFileAction::DELETE, SystemLogErrorClassification::USER_ERROR, 'Could not delete file "{identifier}". Write-permission problem?', ['identifier' => $fileObject->getIdentifier()]);
452
                    $this->addMessageToFlashMessageQueue('FileUtility.CouldNotDeleteFile', [$fileObject->getIdentifier()]);
453
454
455
456
457
458
459
460
461
462
463
464
                }
            }
        } else {
            /** @var Folder $fileObject */
            if (!$this->folderHasFilesInUse($fileObject)) {
                try {
                    $result = $fileObject->delete(true);
                    if ($result) {
                        // notify the user that the folder was deleted
                        /** @var FlashMessage $flashMessage */
                        $flashMessage = GeneralUtility::makeInstance(
                            FlashMessage::class,
465
466
                            sprintf($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:message.description.folderDeleted'), $fileObject->getName()),
                            $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:message.header.folderDeleted'),
467
468
469
470
471
                            FlashMessage::OK,
                            true
                        );
                        $this->addFlashMessage($flashMessage);
                        // Log success
472
                        $this->writeLog(SystemLogFileAction::DELETE, SystemLogErrorClassification::MESSAGE, 'Directory "{identifier}" deleted', ['identifier' => $fileObject->getIdentifier()]);
473
                    }
474
                } catch (InsufficientUserPermissionsException $e) {
475
                    $this->writeLog(SystemLogFileAction::DELETE, SystemLogErrorClassification::USER_ERROR, 'Could not delete directory. Is directory "{identifier}" empty? (You are not allowed to delete directories recursively)', ['identifier' => $fileObject->getIdentifier()]);
476
                    $this->addMessageToFlashMessageQueue('FileUtility.CouldNotDeleteDirectory', [$fileObject->getIdentifier()]);
477
                } catch (InsufficientFolderAccessPermissionsException $e) {
478
                    $this->writeLog(SystemLogFileAction::DELETE, SystemLogErrorClassification::USER_ERROR, 'You are not allowed to access the directory "{identifier}"', ['identifier' => $fileObject->getIdentifier()]);
479
                    $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToAccessTheDirectory', [$fileObject->getIdentifier()]);
480
                } catch (NotInMountPointException $e) {
481
                    $this->writeLog(SystemLogFileAction::DELETE, SystemLogErrorClassification::USER_ERROR, 'Target "{identifier}" was not within your mountpoints', ['identifier' => $fileObject->getIdentifier()]);
482
                    $this->addMessageToFlashMessageQueue('FileUtility.TargetWasNotWithinYourMountpoints', [$fileObject->getIdentifier()]);
483
                } catch (FileOperationErrorException $e) {
484
                    $this->writeLog(SystemLogFileAction::DELETE, SystemLogErrorClassification::USER_ERROR, 'Could not delete directory "{identifier}". Write-permission problem?', ['identifier' => $fileObject->getIdentifier()]);
485
                    $this->addMessageToFlashMessageQueue('FileUtility.CouldNotDeleteDirectory', [$fileObject->getIdentifier()]);
486
487
488
                }
            }
        }
489

490
491
        return $result;
    }
492

493
494
495
496
497
498
499
500
501
502
503
504
505
506
    /**
     * Checks files in given folder recursively for for existing references.
     *
     * Creates a flash message if there are references.
     *
     * @param Folder $folder
     * @return bool TRUE if folder has files in use, FALSE otherwise
     */
    public function folderHasFilesInUse(Folder $folder)
    {
        $files = $folder->getFiles(0, 0, Folder::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, true);
        if (empty($files)) {
            return false;
        }
507

508
        /** @var int[] $fileUids */
509
        $fileUids = [];
510
511
512
        foreach ($files as $file) {
            $fileUids[] = $file->getUid();
        }
513
514
515

        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
        $numberOfReferences = $queryBuilder
516
            ->count('hash')
517
518
            ->from('sys_refindex')
            ->where(
519
520
521
522
523
524
525
526
527
528
529
530
                $queryBuilder->expr()->eq(
                    'ref_table',
                    $queryBuilder->createNamedParameter('sys_file', \PDO::PARAM_STR)
                ),
                $queryBuilder->expr()->in(
                    'ref_uid',
                    $queryBuilder->createNamedParameter($fileUids, Connection::PARAM_INT_ARRAY)
                ),
                $queryBuilder->expr()->neq(
                    'tablename',
                    $queryBuilder->createNamedParameter('sys_file_metadata', \PDO::PARAM_STR)
                )
531
            )->executeQuery()->fetchOne();
532

533
534
535
536
537
        $hasReferences = $numberOfReferences > 0;
        if ($hasReferences) {
            /** @var FlashMessage $flashMessage */
            $flashMessage = GeneralUtility::makeInstance(
                FlashMessage::class,
538
539
                $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:message.description.folderNotDeletedHasFilesWithReferences'),
                $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:message.header.folderNotDeletedHasFilesWithReferences'),
540
541
542
543
544
                FlashMessage::WARNING,
                true
            );
            $this->addFlashMessage($flashMessage);
        }
545

546
547
        return $hasReferences;
    }
548

549
550
551
552
553
554
555
556
557
    /**
     * Maps results from the fal file reference table on the
     * structure of  the normal reference index table.
     *
     * @param array $referenceRecord
     * @return array
     */
    protected function transformFileReferenceToRecordReference(array $referenceRecord)
    {
558
559
560
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
        $queryBuilder->getRestrictions()->removeAll();
        $fileReference = $queryBuilder
561
            ->select('uid_foreign', 'tablenames', 'fieldname', 'sorting_foreign')
562
563
            ->from('sys_file_reference')
            ->where(
564
565
566
567
                $queryBuilder->expr()->eq(
                    'uid',
                    $queryBuilder->createNamedParameter($referenceRecord['recuid'], \PDO::PARAM_INT)
                )
568
            )
569
            ->executeQuery()
570
            ->fetchAssociative();
571

572
        return [
573
574
575
576
577
            'recuid' => $fileReference['uid_foreign'],
            'tablename' => $fileReference['tablenames'],
            'field' => $fileReference['fieldname'],
            'flexpointer' => '',
            'softref_key' => '',
578
            'sorting' => $fileReference['sorting_foreign'],
579
        ];
580
    }
581

582
583
584
585
    /**
     * Gets a File or a Folder object from an identifier [storage]:[fileId]
     *
     * @param string $identifier
586
587
588
     * @return File|Folder
     * @throws Exception\InsufficientFileAccessPermissionsException
     * @throws Exception\InvalidFileException
589
590
591
592
     */
    protected function getFileObject($identifier)
    {
        $object = $this->fileFactory->retrieveFileOrFolderObject($identifier);
593
594
        if ($object === null) {
            throw new InvalidFileException('The item ' . $identifier . ' was not a file or directory', 1320122453);
595
596
        }
        if ($object->getStorage()->getUid() === 0) {
597
            throw new InsufficientFileAccessPermissionsException('You are not allowed to access files outside your storages', 1375889830);
598
599
600
        }
        return $object;
    }
601

602
603
604
605
606
607
608
609
610
611
612
    /**
     * Copying files and folders (action=2)
     *
     * $cmds['data'] (string): The file/folder to copy
     * + example "4:mypath/tomyfolder/myfile.jpg")
     * + for backwards compatibility: the identifier was the path+filename
     * $cmds['target'] (string): The path where to copy to.
     * + example "2:targetpath/targetfolder/"
     * $cmds['altName'] (string): Use an alternative name if the target already exists
     *
     * @param array $cmds Command details as described above
613
     * @return \TYPO3\CMS\Core\Resource\File|false
614
615
616
617
     */
    protected function func_copy($cmds)
    {
        $sourceFileObject = $this->getFileObject($cmds['data']);
618
        /** @var \TYPO3\CMS\Core\Resource\Folder $targetFolderObject */
619
620
621
        $targetFolderObject = $this->getFileObject($cmds['target']);
        // Basic check
        if (!$targetFolderObject instanceof Folder) {
622
            $this->writeLog(SystemLogFileAction::COPY, SystemLogErrorClassification::SYSTEM_ERROR, 'Destination "{identifier}" was not a directory', ['identifier' => $cmds['target']]);
623
            $this->addMessageToFlashMessageQueue('FileUtility.DestinationWasNotADirectory', [$cmds['target']]);
624
625
626
            return false;
        }
        // If this is TRUE, we append _XX to the file name if
627
        $appendSuffixOnConflict = (string)($cmds['altName'] ?? '');
628
629
630
631
632
633
        $resultObject = null;
        $conflictMode = $appendSuffixOnConflict !== '' ? DuplicationBehavior::RENAME : DuplicationBehavior::CANCEL;
        // Copying the file
        if ($sourceFileObject instanceof File) {
            try {
                $resultObject = $sourceFileObject->copyTo($targetFolderObject, null, $conflictMode);
634
            } catch (InsufficientUserPermissionsException $e) {
635
                $this->writeLog(SystemLogFileAction::COPY, SystemLogErrorClassification::USER_ERROR, 'You are not allowed to copy files');
636
                $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToCopyFiles');
637
            } catch (InsufficientFileAccessPermissionsException $e) {
638
                $this->writeLog(SystemLogFileAction::COPY, SystemLogErrorClassification::USER_ERROR, 'Could not access all necessary resources. Source file "{identifier}" or destination "{destination}" was not within your mountpoints maybe', ['identifier' => $sourceFileObject->getIdentifier(), 'destination' => $targetFolderObject->getIdentifier()]);
639
                $this->addMessageToFlashMessageQueue('FileUtility.CouldNotAccessAllNecessaryResources', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
640
            } catch (IllegalFileExtensionException $e) {
641
                $this->writeLog(SystemLogFileAction::COPY, SystemLogErrorClassification::USER_ERROR, 'Extension of file name "{identifier}" is not allowed in "{destination}"', ['identifier' => $sourceFileObject->getIdentifier(), 'destination' => $targetFolderObject->getIdentifier()]);
642
                $this->addMessageToFlashMessageQueue('FileUtility.ExtensionOfFileNameIsNotAllowedIn', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
643
            } catch (ExistingTargetFileNameException $e) {
644
                $this->writeLog(SystemLogFileAction::COPY, SystemLogErrorClassification::USER_ERROR, 'File "{identifier}" already exists in folder "{destination}"', ['identifier' => $sourceFileObject->getIdentifier(), 'destination' => $targetFolderObject->getIdentifier()]);
645
                $this->addMessageToFlashMessageQueue('FileUtility.FileAlreadyExistsInFolder', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
646
            } catch (NotImplementedMethodException $e) {
647
                $this->writeLog(SystemLogFileAction::MOVE, SystemLogErrorClassification::USER_ERROR, 'The function to copy a file between storages is not yet implemented');
648
                $this->addMessageToFlashMessageQueue('FileUtility.TheFunctionToCopyAFileBetweenStoragesIsNotYetImplemented');
649
            } catch (\RuntimeException $e) {
650
                $this->writeLog(SystemLogFileAction::COPY, SystemLogErrorClassification::SYSTEM_ERROR, 'File "{identifier}" was not copied to "{destination}" - Write-permission problem?', ['identifier' => $sourceFileObject->getIdentifier(), 'destination' => $targetFolderObject->getIdentifier()]);
651
                $this->addMessageToFlashMessageQueue('FileUtility.FileWasNotCopiedTo', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
652
653
            }
            if ($resultObject) {
654
                $this->writeLog(SystemLogFileAction::COPY, SystemLogErrorClassification::MESSAGE, 'File "{identifier}" copied to "{destination}"', ['identifier' => $sourceFileObject->getIdentifier(), 'destination' => $resultObject->getIdentifier()]);
655
                $this->addMessageToFlashMessageQueue('FileUtility.FileCopiedTo', [$sourceFileObject->getIdentifier(), $resultObject->getIdentifier()], FlashMessage::OK);
656
657
658
659
660
661
            }
        } else {
            // Else means this is a Folder
            $sourceFolderObject = $sourceFileObject;
            try {
                $resultObject = $sourceFolderObject->copyTo($targetFolderObject, null, $conflictMode);
662
            } catch (InsufficientUserPermissionsException $e) {
663
                $this->writeLog(SystemLogFileAction::COPY, SystemLogErrorClassification::USER_ERROR, 'You are not allowed to copy directories');
664
                $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToCopyDirectories');
665
            } catch (InsufficientFileAccessPermissionsException $e) {
666
                $this->writeLog(SystemLogFileAction::COPY, SystemLogErrorClassification::USER_ERROR, 'Could not access all necessary resources. Maybe source file "{identifier}" or destination "{destination}" was not within your mountpoints', ['identifier' => $sourceFolderObject->getIdentifier(), 'destination' => $targetFolderObject->getIdentifier()]);
667
                $this->addMessageToFlashMessageQueue('FileUtility.CouldNotAccessAllNecessaryResources', [$sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
668
            } catch (InsufficientFolderAccessPermissionsException $e) {
669
                $this->writeLog(SystemLogFileAction::COPY, SystemLogErrorClassification::USER_ERROR, 'You don\'t have full access to the destination directory "{destination}"', ['destination' => $targetFolderObject->getIdentifier()]);
670
                $this->addMessageToFlashMessageQueue('FileUtility.YouDontHaveFullAccessToTheDestinationDirectory', [$targetFolderObject->getIdentifier()]);
671
            } catch (InvalidTargetFolderException $e) {
672
                $this->writeLog(SystemLogFileAction::COPY, SystemLogErrorClassification::USER_ERROR, 'Cannot copy folder "{name}" into target folder "{destination}", because there is already a folder or file with that name in the target folder', ['name' => $sourceFolderObject->getName(), 'destination' => $targetFolderObject->getIdentifier()]);
673
                $this->addMessageToFlashMessageQueue('FileUtility.CannotCopyFolderIntoTargetFolderBecauseTheTargetFolderIsAlreadyWithinTheFolderToBeCopied', [$sourceFolderObject->getName(), $targetFolderObject->getIdentifier()]);
674
            } catch (ExistingTargetFolderException $e) {
675
                $this->writeLog(SystemLogFileAction::COPY, SystemLogErrorClassification::USER_ERROR, 'Target "{destination}" already exists', ['destination' => $targetFolderObject->getIdentifier()]);
676
                $this->addMessageToFlashMessageQueue('FileUtility.TargetAlreadyExists', [$targetFolderObject->getIdentifier()]);
677
            } catch (NotImplementedMethodException $e) {
678
                $this->writeLog(SystemLogFileAction::MOVE, SystemLogErrorClassification::USER_ERROR, 'The function to copy a folder between storages is not yet implemented');
679
                $this->addMessageToFlashMessageQueue('FileUtility.TheFunctionToCopyAFolderBetweenStoragesIsNotYetImplemented');
680
            } catch (\RuntimeException $e) {
681
                $this->writeLog(SystemLogFileAction::COPY, SystemLogErrorClassification::SYSTEM_ERROR, 'Directory "{identifier}" was not copied to "{destination}". Write-permission problem?', ['identifier' => $sourceFolderObject->getIdentifier(), 'destination' => $targetFolderObject->getIdentifier()]);
682
                $this->addMessageToFlashMessageQueue('FileUtility.DirectoryWasNotCopiedTo', [$sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
683
684
            }
            if ($resultObject) {
685
                $this->writeLog(SystemLogFileAction::COPY, SystemLogErrorClassification::MESSAGE, 'Directory "{identifier}" copied to "{destination}"', ['identifier' => $sourceFolderObject->getIdentifier(), 'destination' => $targetFolderObject->getIdentifier()]);
686
                $this->addMessageToFlashMessageQueue('FileUtility.DirectoryCopiedTo', [$sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()], FlashMessage::OK);
687
688
689
690
            }
        }
        return $resultObject;
    }
691

692
693
694
695
696
697
698
699
700
701
702
    /**
     * Moving files and folders (action=3)
     *
     * $cmds['data'] (string): The file/folder to move
     * + example "4:mypath/tomyfolder/myfile.jpg")
     * + for backwards compatibility: the identifier was the path+filename
     * $cmds['target'] (string): The path where to move to.
     * + example "2:targetpath/targetfolder/"
     * $cmds['altName'] (string): Use an alternative name if the target already exists
     *
     * @param array $cmds Command details as described above
703
     * @return \TYPO3\CMS\Core\Resource\File|false
704
705
706
707
708
709
710
     */
    protected function func_move($cmds)
    {
        $sourceFileObject = $this->getFileObject($cmds['data']);
        $targetFolderObject = $this->getFileObject($cmds['target']);
        // Basic check
        if (!$targetFolderObject instanceof Folder) {
711
            $this->writeLog(SystemLogFileAction::MOVE, SystemLogErrorClassification::SYSTEM_ERROR, 'Destination "{destination}" was not a directory', ['destination' => $cmds['target']]);
712
            $this->addMessageToFlashMessageQueue('FileUtility.DestinationWasNotADirectory', [$cmds['target']]);
713
714
            return false;
        }
715
        $alternativeName = (string)($cmds['altName'] ?? '');
716
717
718
719
720
721
722
723
724
725
726
        $resultObject = null;
        // Moving the file
        if ($sourceFileObject instanceof File) {
            try {
                if ($alternativeName !== '') {
                    // Don't allow overwriting existing files, but find a new name
                    $resultObject = $sourceFileObject->moveTo($targetFolderObject, $alternativeName, DuplicationBehavior::RENAME);
                } else {
                    // Don't allow overwriting existing files
                    $resultObject = $sourceFileObject->moveTo($targetFolderObject, null, DuplicationBehavior::CANCEL);
                }
727
                $this->writeLog(SystemLogFileAction::MOVE, SystemLogErrorClassification::MESSAGE, 'File "{identifier}" moved to "{destination}"', ['identifier' => $sourceFileObject->getIdentifier(), 'destination' => $resultObject->getIdentifier()]);
728
                $this->addMessageToFlashMessageQueue('FileUtility.FileMovedTo', [$sourceFileObject->getIdentifier(), $resultObject->getIdentifier()], FlashMessage::OK);
729
            } catch (InsufficientUserPermissionsException $e) {
730
                $this->writeLog(SystemLogFileAction::MOVE, SystemLogErrorClassification::USER_ERROR, 'You are not allowed to move files');
731
                $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToMoveFiles');
732
            } catch (InsufficientFileAccessPermissionsException $e) {
733
                $this->writeLog(SystemLogFileAction::MOVE, SystemLogErrorClassification::USER_ERROR, 'Could not access all necessary resources. Maybe source file "{identifier}" or destination "{destination}" was not within your mountpoints', ['identifier' => $sourceFileObject->getIdentifier(), 'destination' => $targetFolderObject->getIdentifier()]);
734
                $this->addMessageToFlashMessageQueue('FileUtility.CouldNotAccessAllNecessaryResources', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
735
            } catch (IllegalFileExtensionException $e) {
736
                $this->writeLog(SystemLogFileAction::MOVE, SystemLogErrorClassification::USER_ERROR, 'Extension of file name "{identifier}" is not allowed in "{destination}"', ['identifier' => $sourceFileObject->getIdentifier(), 'destination' => $targetFolderObject->getIdentifier()]);
737
                $this->addMessageToFlashMessageQueue('FileUtility.ExtensionOfFileNameIsNotAllowedIn', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
738
            } catch (ExistingTargetFileNameException $e) {
739
                $this->writeLog(SystemLogFileAction::MOVE, SystemLogErrorClassification::USER_ERROR, 'File "{identifier}" already exists in folder "{destination}"', ['identifier' => $sourceFileObject->getIdentifier(), 'destination' => $targetFolderObject->getIdentifier()]);
740
                $this->addMessageToFlashMessageQueue('FileUtility.FileAlreadyExistsInFolder', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
741
            } catch (NotImplementedMethodException $e) {
742
                $this->writeLog(SystemLogFileAction::MOVE, SystemLogErrorClassification::USER_ERROR, 'The function to move a file between storages is not yet implemented');
743
                $this->addMessageToFlashMessageQueue('FileUtility.TheFunctionToMoveAFileBetweenStoragesIsNotYetImplemented');
744
            } catch (\RuntimeException $e) {
745
                $this->writeLog(SystemLogFileAction::MOVE, SystemLogErrorClassification::SYSTEM_ERROR, 'File "{identifier}" was not copied to "{destination}". Write-permission problem?', ['identifier' => $sourceFileObject->getIdentifier(), 'destination' => $targetFolderObject->getIdentifier()]);
746
                $this->addMessageToFlashMessageQueue('FileUtility.FileWasNotCopiedTo', [$sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
747
748
749
750
751
752
753
754
755
756
757
758
            }
        } else {
            // Else means this is a Folder
            $sourceFolderObject = $sourceFileObject;
            try {
                if ($alternativeName !== '') {
                    // Don't allow overwriting existing files, but find a new name
                    $resultObject = $sourceFolderObject->moveTo($targetFolderObject, $alternativeName, DuplicationBehavior::RENAME);
                } else {
                    // Don't allow overwriting existing files
                    $resultObject = $sourceFolderObject->moveTo($targetFolderObject, null, DuplicationBehavior::RENAME);
                }
759
                $this->writeLog(SystemLogFileAction::MOVE, SystemLogErrorClassification::MESSAGE, 'Directory "{identifier}" moved to "{destination}"', ['identifier' => $sourceFolderObject->getIdentifier(), 'destination' => $targetFolderObject->getIdentifier()]);
760
                $this->addMessageToFlashMessageQueue('FileUtility.DirectoryMovedTo', [$sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()], FlashMessage::OK);
761
            } catch (InsufficientUserPermissionsException $e) {
762
                $this->writeLog(SystemLogFileAction::MOVE, SystemLogErrorClassification::USER_ERROR, 'You are not allowed to move directories');
763
                $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToMoveDirectories');
764
            } catch (InsufficientFileAccessPermissionsException $e) {
765
                $this->writeLog(SystemLogFileAction::MOVE, SystemLogErrorClassification::USER_ERROR, 'Could not access all necessary resources. Maybe source folder "{identifier}" or destination "{destination}" was not within your mountpoints', ['identifier' => $sourceFolderObject->getIdentifier(), 'destination' => $targetFolderObject->getIdentifier()]);
766
                $this->addMessageToFlashMessageQueue('FileUtility.CouldNotAccessAllNecessaryResources', [$sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
767
            } catch (InsufficientFolderAccessPermissionsException $e) {
768
                $this->writeLog(SystemLogFileAction::MOVE, SystemLogErrorClassification::USER_ERROR, 'You don\'t have full access to the destination directory "{destination}"', ['destination' => $targetFolderObject->getIdentifier()]);
769
                $this->addMessageToFlashMessageQueue('FileUtility.YouDontHaveFullAccessToTheDestinationDirectory', [$targetFolderObject->getIdentifier()]);
770
            } catch (InvalidTargetFolderException $e) {
771
                $this->writeLog(SystemLogFileAction::MOVE, SystemLogErrorClassification::USER_ERROR, 'Cannot move folder "{identifier}" into target folder "{destination}", because the target folder is already within the folder to be moved', ['identifier' => $sourceFolderObject->getName(), 'destination' => $targetFolderObject->getName()]);
772
                $this->addMessageToFlashMessageQueue('FileUtility.CannotMoveFolderIntoTargetFolderBecauseTheTargetFolderIsAlreadyWithinTheFolderToBeMoved', [$sourceFolderObject->getName(), $targetFolderObject->getName()]);
773
            } catch (ExistingTargetFolderException $e) {
774
                $this->writeLog(SystemLogFileAction::MOVE, SystemLogErrorClassification::USER_ERROR, 'Target "{destination}" already exists', ['destination' => $targetFolderObject->getIdentifier()]);
775
                $this->addMessageToFlashMessageQueue('FileUtility.TargetAlreadyExists', [$targetFolderObject->getIdentifier()]);
776
            } catch (NotImplementedMethodException $e) {
777
778
                $this->writeLog(SystemLogFileAction::MOVE, SystemLogErrorClassification::USER_ERROR, 'The function to move a folder between storages is not yet implemented');
                $this->addMessageToFlashMessageQueue('FileUtility.TheFunctionToMoveAFolderBetweenStoragesIsNotYetImplemented');
779
            } catch (\RuntimeException $e) {
780
                $this->writeLog(SystemLogFileAction::MOVE, SystemLogErrorClassification::SYSTEM_ERROR, 'Directory "{identifier}" was not moved to "{destination}". Write-permission problem?', ['identifier' => $sourceFolderObject->getIdentifier(), 'destination' => $targetFolderObject->getIdentifier()]);
781
                $this->addMessageToFlashMessageQueue('FileUtility.DirectoryWasNotMovedTo', [$sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()]);
782
783
784
785
            }
        }
        return $resultObject;
    }
786

787
    /**
788
     * Renaming files or folders (action=5)
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
     *
     * $cmds['data'] (string): The file/folder to copy
     * + example "4:mypath/tomyfolder/myfile.jpg")
     * + for backwards compatibility: the identifier was the path+filename
     * $cmds['target'] (string): New name of the file/folder
     *
     * @param array $cmds Command details as described above
     * @return \TYPO3\CMS\Core\Resource\File Returns the new file upon success
     */
    public function func_rename($cmds)
    {
        $sourceFileObject = $this->getFileObject($cmds['data']);
        $sourceFile = $sourceFileObject->getName();
        $targetFile = $cmds['target'];
        $resultObject = null;
        if ($sourceFileObject instanceof File) {
            try {
                // Try to rename the File
807
                $resultObject = $sourceFileObject->rename($targetFile, $this->existingFilesConflictMode);
808
                if ($resultObject->getName() !== $targetFile) {
809
                    $this->writeLog(SystemLogFileAction::RENAME, SystemLogErrorClassification::USER_ERROR, 'File renamed from "{identifier}" to "{destination}". Filename had to be sanitized', ['identifier' => $sourceFile, 'destination' => $targetFile]);
810
811
                    $this->addMessageToFlashMessageQueue('FileUtility.FileNameSanitized', [$targetFile, $resultObject->getName()], FlashMessage::WARNING);
                } else {
812
                    $this->writeLog(SystemLogFileAction::RENAME, SystemLogErrorClassification::MESSAGE, 'File renamed from "{identifier}" to "{destination}"', ['identifier' => $sourceFile, 'destination' => $targetFile]);
813
814
                }
                if ($sourceFile === $resultObject->getName()) {
815
816
                    $this->addMessageToFlashMessageQueue('FileUtility.FileRenamedSameName', [$sourceFile], FlashMessage::INFO);
                } else {
817
                    $this->addMessageToFlashMessageQueue('FileUtility.FileRenamedFromTo', [$sourceFile, $resultObject->getName()], FlashMessage::OK);
818
                }
819
            } catch (InsufficientUserPermissionsException $e) {
820
                $this->writeLog(SystemLogFileAction::RENAME, SystemLogErrorClassification::USER_ERROR, 'You are not allowed to rename files');
821
822
                $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToRenameFiles');
            } catch (IllegalFileExtensionException $e) {
823
                $this->writeLog(SystemLogFileAction::RENAME, SystemLogErrorClassification::USER_ERROR, 'Extension of file name "{identifier}" or "{destination}" was not allowed', ['identifier' => $sourceFileObject->getName(), 'destination' => $targetFile]);
824
                $this->addMessageToFlashMessageQueue('FileUtility.ExtensionOfFileNameOrWasNotAllowed', [$sourceFileObject->getName(), $targetFile]);
825
            } catch (ExistingTargetFileNameException $e) {
826
                $this->writeLog(SystemLogFileAction::RENAME, SystemLogErrorClassification::USER_ERROR, 'Destination "{destination}" existed already', ['destination' => $targetFile]);
827
                $this->addMessageToFlashMessageQueue('FileUtility.DestinationExistedAlready', [$targetFile]);
828
            } catch (NotInMountPointException $e) {
829
                $this->writeLog(SystemLogFileAction::RENAME, SystemLogErrorClassification::USER_ERROR, 'Destination path "{destination}" was not within your mountpoints', ['destination' => $targetFile]);
830
                $this->addMessageToFlashMessageQueue('FileUtility.DestinationPathWasNotWithinYourMountpoints', [$targetFile]);
831
            } catch (\RuntimeException $e) {
832
                $this->writeLog(SystemLogFileAction::RENAME, SystemLogErrorClassification::USER_ERROR, 'File "{identifier}" was not renamed. Write-permission problem in "{destination}"?', ['identifier' => $sourceFileObject->getName(), 'destination' => $targetFile]);
833
                $this->addMessageToFlashMessageQueue('FileUtility.FileWasNotRenamed', [$sourceFileObject->getName(), $targetFile]);
834
835
836
837
838
839
            }
        } else {
            // Else means this is a Folder
            try {
                // Try to rename the Folder
                $resultObject = $sourceFileObject->rename($targetFile);
840
                $newFolderName = $resultObject->getName();
841
                $this->writeLog(SystemLogFileAction::RENAME, SystemLogErrorClassification::MESSAGE, 'Directory renamed from "{identifier}" to "{destination}"', ['identifier' => $sourceFile, 'destination' => $targetFile]);
842
                if ($sourceFile === $newFolderName) {
843
844
                    $this->addMessageToFlashMessageQueue('FileUtility.DirectoryRenamedSameName', [$sourceFile], FlashMessage::INFO);
                } else {
845
846
847
848
849
                    if ($newFolderName === $targetFile) {
                        $this->addMessageToFlashMessageQueue('FileUtility.DirectoryRenamedFromTo', [$sourceFile, $newFolderName], FlashMessage::OK);
                    } else {
                        $this->addMessageToFlashMessageQueue('FileUtility.DirectoryRenamedFromToCharReplaced', [$sourceFile, $newFolderName], FlashMessage::WARNING);
                    }
850
                }
851
            } catch (InsufficientUserPermissionsException $e) {
852
                $this->writeLog(SystemLogFileAction::RENAME, SystemLogErrorClassification::USER_ERROR, 'You are not allowed to rename directories');
853
854
                $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToRenameDirectories');
            } catch (ExistingTargetFileNameException $e) {
855
                $this->writeLog(SystemLogFileAction::RENAME, SystemLogErrorClassification::USER_ERROR, 'Destination "{destination}" existed already', ['destination' => $targetFile]);
856
                $this->addMessageToFlashMessageQueue('FileUtility.DestinationExistedAlready', [$targetFile]);
857
            } catch (NotInMountPointException $e) {
858
                $this->writeLog(SystemLogFileAction::RENAME, SystemLogErrorClassification::USER_ERROR, 'Destination path "{destination}" was not within your mountpoints', ['destination' => $targetFile]);
859
                $this->addMessageToFlashMessageQueue('FileUtility.DestinationPathWasNotWithinYourMountpoints', [$targetFile]);
860
            } catch (\RuntimeException $e) {
861
                $this->writeLog(SystemLogFileAction::RENAME, SystemLogErrorClassification::USER_ERROR, 'Directory "{identifier}" was not renamed. Write-permission problem in "{destination}"?', ['identifier' => $sourceFileObject->getName(), 'destination' => $targetFile]);
862
                $this->addMessageToFlashMessageQueue('FileUtility.DirectoryWasNotRenamed', [$sourceFileObject->getName(), $targetFile]);
863
864
865
866
            }
        }
        return $resultObject;
    }
867

868
869
870
871
872
873
874
875
    /**
     * This creates a new folder. (action=6)
     *
     * $cmds['data'] (string): The new folder name
     * $cmds['target'] (string): The path where to copy to.
     * + example "2:targetpath/targetfolder/"
     *
     * @param array $cmds Command details as described above
876
     * @return \TYPO3\CMS\Core\Resource\Folder|false Returns the new foldername upon success