Passed
Push — master ( 6501ef...61030c )
by
unknown
12:36
created

ExtendedFileUtility::replaceFile()   B

Complexity

Conditions 11
Paths 107

Size

Total Lines 60
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 44
c 0
b 0
f 0
dl 0
loc 60
rs 7.2583
cc 11
nc 107
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1085
                /** @scrutinizer ignore-call */ 
1086
                $fileObject = $targetFolderObject->addUploadedFile($fileInfo, (string)$this->existingFilesConflictMode);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1086
                if ($this->existingFilesConflictMode->equals(DuplicationBehavior::REPLACE)) {
1087
                    $this->getIndexer($fileObject->getStorage())->updateIndexEntry($fileObject);
1088
                }
1089
                $resultObjects[] = $fileObject;
1090
                $this->internalUploadMap[$uploadPosition] = $fileObject->getCombinedIdentifier();
1091
                if ($fileObject->getName() !== $fileInfo['name']) {
1092
                    $this->addMessageToFlashMessageQueue('FileUtility.FileNameSanitized', [$fileInfo['name'], $fileObject->getName()], FlashMessage::WARNING);
1093
                }
1094
                $this->writeLog(SystemLogFileAction::UPLOAD, SystemLogErrorClassification::MESSAGE, 1, 'Uploading file "%s" to "%s"', [$fileInfo['name'], $targetFolderObject->getIdentifier()]);
1095
                $this->addMessageToFlashMessageQueue('FileUtility.UploadingFileTo', [$fileInfo['name'], $targetFolderObject->getIdentifier()], FlashMessage::OK);
1096
            } catch (InsufficientFileWritePermissionsException $e) {
1097
                $this->writeLog(SystemLogFileAction::UPLOAD, SystemLogErrorClassification::USER_ERROR, 107, 'You are not allowed to override "%s"!', [$fileInfo['name']]);
1098
                $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToOverride', [$fileInfo['name']]);
1099
            } catch (UploadException $e) {
1100
                $this->writeLog(SystemLogFileAction::UPLOAD, SystemLogErrorClassification::SYSTEM_ERROR, 106, 'The upload has failed, no uploaded file found!', []);
1101
                $this->addMessageToFlashMessageQueue('FileUtility.TheUploadHasFailedNoUploadedFileFound');
1102
            } catch (InsufficientUserPermissionsException $e) {
1103
                $this->writeLog(SystemLogFileAction::UPLOAD, SystemLogErrorClassification::USER_ERROR, 105, 'You are not allowed to upload files!', []);
1104
                $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToUploadFiles');
1105
            } catch (UploadSizeException $e) {
1106
                $this->writeLog(SystemLogFileAction::UPLOAD, SystemLogErrorClassification::USER_ERROR, 104, 'The uploaded file "%s" exceeds the size-limit', [$fileInfo['name']]);
1107
                $this->addMessageToFlashMessageQueue('FileUtility.TheUploadedFileExceedsTheSize-limit', [$fileInfo['name']]);
1108
            } catch (InsufficientFolderWritePermissionsException $e) {
1109
                $this->writeLog(SystemLogFileAction::UPLOAD, SystemLogErrorClassification::USER_ERROR, 103, 'Destination path "%s" was not within your mountpoints!', [$targetFolderObject->getIdentifier()]);
1110
                $this->addMessageToFlashMessageQueue('FileUtility.DestinationPathWasNotWithinYourMountpoints', [$targetFolderObject->getIdentifier()]);
1111
            } catch (IllegalFileExtensionException $e) {
1112
                $this->writeLog(SystemLogFileAction::UPLOAD, SystemLogErrorClassification::USER_ERROR, 102, 'Extension of file name "%s" is not allowed in "%s"!', [$fileInfo['name'], $targetFolderObject->getIdentifier()]);
1113
                $this->addMessageToFlashMessageQueue('FileUtility.ExtensionOfFileNameIsNotAllowedIn', [$fileInfo['name'], $targetFolderObject->getIdentifier()]);
1114
            } catch (ExistingTargetFileNameException $e) {
1115
                $this->writeLog(SystemLogFileAction::UPLOAD, SystemLogErrorClassification::USER_ERROR, 101, 'No unique filename available in "%s"!', [$targetFolderObject->getIdentifier()]);
1116
                $this->addMessageToFlashMessageQueue('FileUtility.NoUniqueFilenameAvailableIn', [$targetFolderObject->getIdentifier()]);
1117
            } catch (\RuntimeException $e) {
1118
                $this->writeLog(SystemLogFileAction::UPLOAD, SystemLogErrorClassification::USER_ERROR, 100, 'Uploaded file could not be moved! Write-permission problem in "%s"?', [$targetFolderObject->getIdentifier()]);
1119
                $this->addMessageToFlashMessageQueue('FileUtility.UploadedFileCouldNotBeMoved', [$targetFolderObject->getIdentifier()]);
1120
            }
1121
        }
1122
1123
        return $resultObjects;
1124
    }
1125
1126
    /**
1127
     * Replaces a file on the filesystem and changes the identifier of the persisted file object in sys_file if
1128
     * keepFilename is not checked. If keepFilename is checked, only the file content will be replaced.
1129
     *
1130
     * @param array $cmdArr
1131
     * @return array|bool
1132
     * @throws Exception\InsufficientFileAccessPermissionsException
1133
     * @throws Exception\InvalidFileException
1134
     * @throws \RuntimeException
1135
     */
1136
    protected function replaceFile(array $cmdArr)
1137
    {
1138
        $fileObjectToReplace = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $fileObjectToReplace is dead and can be removed.
Loading history...
1139
        $uploadPosition = $cmdArr['data'];
1140
        $fileInfo = $_FILES['replace_' . $uploadPosition];
1141
        if (empty($fileInfo['name'])) {
1142
            $this->writeLog(SystemLogFileAction::UPLOAD, SystemLogErrorClassification::SYSTEM_ERROR, 108, 'No file was uploaded for replacing!', []);
1143
            $this->addMessageToFlashMessageQueue('FileUtility.NoFileWasUploadedForReplacing');
1144
            return false;
1145
        }
1146
1147
        $keepFileName = (bool)($cmdArr['keepFilename'] ?? false);
1148
        $resultObjects = [];
1149
1150
        try {
1151
            $fileObjectToReplace = $this->getFileObject($cmdArr['uid']);
1152
            $folder = $fileObjectToReplace->getParentFolder();
1153
            $resourceStorage = $fileObjectToReplace->getStorage();
1154
1155
            $fileObject = $resourceStorage->addUploadedFile($fileInfo, $folder, $fileObjectToReplace->getName(), DuplicationBehavior::REPLACE);
1156
1157
            // Check if there is a file that is going to be uploaded that has a different name as the replacing one
1158
            // but exists in that folder as well.
1159
            // rename to another name, but check if the name is already given
1160
            if ($keepFileName === false) {
1161
                // if a file with the same name already exists, we need to change it to _01 etc.
1162
                // if the file does not exist, we can do a simple rename
1163
                $resourceStorage->moveFile($fileObject, $folder, $fileInfo['name'], DuplicationBehavior::RENAME);
1164
            }
1165
1166
            $resultObjects[] = $fileObject;
1167
            $this->internalUploadMap[$uploadPosition] = $fileObject->getCombinedIdentifier();
1168
1169
            $this->writeLog(SystemLogFileAction::UPLOAD, SystemLogErrorClassification::MESSAGE, 1, 'Replacing file "%s" to "%s"', [$fileInfo['name'], $fileObjectToReplace->getIdentifier()]);
1170
            $this->addMessageToFlashMessageQueue('FileUtility.ReplacingFileTo', [$fileInfo['name'], $fileObjectToReplace->getIdentifier()], FlashMessage::OK);
1171
        } catch (InsufficientFileWritePermissionsException $e) {
1172
            $this->writeLog(SystemLogFileAction::UPLOAD, SystemLogErrorClassification::USER_ERROR, 107, 'You are not allowed to override "%s"!', [$fileInfo['name']]);
1173
            $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToOverride', [$fileInfo['name']]);
1174
        } catch (UploadException $e) {
1175
            $this->writeLog(SystemLogFileAction::UPLOAD, SystemLogErrorClassification::SYSTEM_ERROR, 106, 'The upload has failed, no uploaded file found!', []);
1176
            $this->addMessageToFlashMessageQueue('FileUtility.TheUploadHasFailedNoUploadedFileFound');
1177
        } catch (InsufficientUserPermissionsException $e) {
1178
            $this->writeLog(SystemLogFileAction::UPLOAD, SystemLogErrorClassification::USER_ERROR, 105, 'You are not allowed to upload files!', []);
1179
            $this->addMessageToFlashMessageQueue('FileUtility.YouAreNotAllowedToUploadFiles');
1180
        } catch (UploadSizeException $e) {
1181
            $this->writeLog(SystemLogFileAction::UPLOAD, SystemLogErrorClassification::USER_ERROR, 104, 'The uploaded file "%s" exceeds the size-limit', [$fileInfo['name']]);
1182
            $this->addMessageToFlashMessageQueue('FileUtility.TheUploadedFileExceedsTheSize-limit', [$fileInfo['name']]);
1183
        } catch (InsufficientFolderWritePermissionsException $e) {
1184
            $this->writeLog(SystemLogFileAction::UPLOAD, SystemLogErrorClassification::USER_ERROR, 103, 'Destination path "%s" was not within your mountpoints!', [$fileObjectToReplace->getIdentifier()]);
1185
            $this->addMessageToFlashMessageQueue('FileUtility.DestinationPathWasNotWithinYourMountpoints', [$fileObjectToReplace->getIdentifier()]);
1186
        } catch (IllegalFileExtensionException $e) {
1187
            $this->writeLog(SystemLogFileAction::UPLOAD, SystemLogErrorClassification::USER_ERROR, 102, 'Extension of file name "%s" is not allowed in "%s"!', [$fileInfo['name'], $fileObjectToReplace->getIdentifier()]);
1188
            $this->addMessageToFlashMessageQueue('FileUtility.ExtensionOfFileNameIsNotAllowedIn', [$fileInfo['name'], $fileObjectToReplace->getIdentifier()]);
1189
        } catch (ExistingTargetFileNameException $e) {
1190
            $this->writeLog(SystemLogFileAction::UPLOAD, SystemLogErrorClassification::USER_ERROR, 101, 'No unique filename available in "%s"!', [$fileObjectToReplace->getIdentifier()]);
1191
            $this->addMessageToFlashMessageQueue('FileUtility.NoUniqueFilenameAvailableIn', [$fileObjectToReplace->getIdentifier()]);
1192
        } catch (\RuntimeException $e) {
1193
            throw $e;
1194
        }
1195
        return $resultObjects;
1196
    }
1197
1198
    /**
1199
     * Add flash message to message queue
1200
     *
1201
     * @param FlashMessage $flashMessage
1202
     */
1203
    protected function addFlashMessage(FlashMessage $flashMessage)
1204
    {
1205
        /** @var FlashMessageService $flashMessageService */
1206
        $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
1207
1208
        /** @var \TYPO3\CMS\Core\Messaging\FlashMessageQueue $defaultFlashMessageQueue */
1209
        $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
1210
        $defaultFlashMessageQueue->enqueue($flashMessage);
1211
    }
1212
1213
    /**
1214
     * Gets Indexer
1215
     *
1216
     * @param \TYPO3\CMS\Core\Resource\ResourceStorage $storage
1217
     * @return \TYPO3\CMS\Core\Resource\Index\Indexer
1218
     */
1219
    protected function getIndexer(ResourceStorage $storage)
1220
    {
1221
        return GeneralUtility::makeInstance(Indexer::class, $storage);
1222
    }
1223
1224
    /**
1225
     * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
1226
     */
1227
    protected function getBackendUser()
1228
    {
1229
        return $GLOBALS['BE_USER'];
1230
    }
1231
1232
    /**
1233
     * Returns LanguageService
1234
     *
1235
     * @return LanguageService
1236
     */
1237
    protected function getLanguageService()
1238
    {
1239
        return $GLOBALS['LANG'];
1240
    }
1241
}
1242