Completed
Push — sanitizedFileExists ( b03dc0 )
by Christian
05:49
created

AjaxController::fileExistsAction()   C

Complexity

Conditions 12
Paths 60

Size

Total Lines 48
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 36
nc 60
nop 1
dl 0
loc 48
rs 6.9666
c 0
b 0
f 0

How to fix   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
declare(strict_types = 1);
3
4
/*
5
 * This file is part of the package typo3/cms-digital-asset-management.
6
 *
7
 * For the full copyright and license information, please read the
8
 * LICENSE file that was distributed with this source code.
9
 */
10
11
namespace TYPO3\CMS\DigitalAssetManagement\Controller;
12
13
use Psr\Http\Message\ResponseInterface;
14
use Psr\Http\Message\ServerRequestInterface;
15
use TYPO3\CMS\Backend\Routing\UriBuilder;
16
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
17
use TYPO3\CMS\Core\Core\Environment;
18
use TYPO3\CMS\Core\Http\JsonResponse;
19
use TYPO3\CMS\Core\Http\Response;
20
use TYPO3\CMS\Core\Http\Stream;
21
use TYPO3\CMS\Core\Resource\Driver\LocalDriver;
22
use TYPO3\CMS\Core\Resource\DuplicationBehavior;
23
use TYPO3\CMS\Core\Resource\Exception as ResourceException;
24
use TYPO3\CMS\Core\Resource\Exception\InvalidTargetFolderException;
25
use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
26
use TYPO3\CMS\Core\Resource\FileInterface;
27
use TYPO3\CMS\Core\Resource\Folder;
28
use TYPO3\CMS\Core\Resource\ResourceFactory;
29
use TYPO3\CMS\Core\Utility\GeneralUtility;
30
use TYPO3\CMS\Core\Utility\StringUtility;
31
use TYPO3\CMS\DigitalAssetManagement\Entity\FileMount;
32
use TYPO3\CMS\DigitalAssetManagement\Entity\FileOperationResult;
33
use TYPO3\CMS\DigitalAssetManagement\Entity\FolderItemFile;
34
use TYPO3\CMS\DigitalAssetManagement\Entity\FolderItemFolder;
35
use TYPO3\CMS\DigitalAssetManagement\Entity\FolderItemImage;
36
use TYPO3\CMS\DigitalAssetManagement\Entity\Storage;
37
use TYPO3\CMS\DigitalAssetManagement\Entity\TreeItemFolder;
38
use TYPO3\CMS\DigitalAssetManagement\Exception\ControllerException;
39
use TYPO3\CMS\DigitalAssetManagement\Http\FileExistsResponse;
40
use TYPO3\CMS\DigitalAssetManagement\Http\FileOperationResponse;
41
use TYPO3\CMS\DigitalAssetManagement\Http\FolderItemsResponse;
42
use TYPO3\CMS\DigitalAssetManagement\Http\JsonExceptionResponse;
43
use TYPO3\CMS\DigitalAssetManagement\Http\StoragesAndMountsResponse;
44
45
/**
46
 * Main API endpoint. These are ajax actions called by JS side.
47
 *
48
 * Look up the end points in Configuration/Backend/Routes.php: A typical
49
 * path is something like /ajax/dam/getStoragesAndMounts which maps to a method
50
 * with the same name plus word "Action": getStoragesAndMountsAction().
51
 *
52
 * All actions return a JsonResponse, if all is good, the return code is
53
 * 200. A different code, usually in 4xx range will be returned if the
54
 * client sent a bogus request, often with some exception details.
55
 */
56
class AjaxController
57
{
58
    /**
59
     * @return JsonResponse
60
     */
61
    public function getNewStorageUrlAction(): JsonResponse
62
    {
63
        $backendUser = $this->getBackendUser();
64
        if (!$backendUser->isAdmin()) {
65
            return new JsonExceptionResponse(new ControllerException('User is not admin', 1554380677));
66
        }
67
        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
68
        $urlParameters = [
69
            'edit' => [
70
                'sys_file_storage' => [
71
                    0 => 'new',
72
                ],
73
            ],
74
            'returnUrl' => (string)$uriBuilder->buildUriFromRoute('file_DigitalAssetManagement'),
75
        ];
76
        return new JsonResponse([(string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters)]);
77
    }
78
79
    /**
80
     * @param ServerRequestInterface $request
81
     * @return Response
82
     */
83
    public function prepareDownloadAction(ServerRequestInterface $request): ResponseInterface
84
    {
85
        $identifiers = $request->getQueryParams()['identifiers'] ?? '';
86
        try {
87
            if (empty($identifiers) || !is_array($identifiers)) {
88
                throw new ControllerException('List of files or folders needed', 1554375542);
89
            }
90
            // (Mis)-use the LocalDriver method to sanitize the filename prefix
91
            $archivePrefix = rtrim(
92
                GeneralUtility::makeInstance(LocalDriver::class)
93
                    ->sanitizeFileName($request->getQueryParams()['filenamePrefix'] ?? 'download'),
94
                '-'
95
            );
96
            $archiveFilename = Environment::getVarPath()
97
                . '/transient/'
98
                . $archivePrefix
99
                . '-'
100
                . GeneralUtility::hmac(StringUtility::getUniqueId(), 'damDownload')
101
                . '.zip';
102
            $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
103
            $zip = new \ZipArchive();
104
            $zip->open($archiveFilename, \ZipArchive::CREATE);
105
            foreach ($identifiers as $identifier) {
106
                $resource = $resourceFactory->getObjectFromCombinedIdentifier($identifier);
107
                if ($resource instanceof FileInterface) {
108
                    /** @var FileInterface $resource */
109
                    $zip->addFile($resource->getForLocalProcessing(false), $resource->getName());
110
                } else {
111
                    /** @var Folder $resource */
112
                    $this->addFolderToArchiveRecursive($resource, $zip);
113
                }
114
            }
115
            $zip->close();
116
            GeneralUtility::fixPermissions($archiveFilename);
117
        } catch (ResourceException $e) {
118
            return new JsonExceptionResponse($e);
119
        } catch (ControllerException $e) {
120
            return new JsonExceptionResponse($e);
121
        }
122
        return (new Response())
123
            ->withHeader('Filename', basename($archiveFilename))
124
            ->withHeader('Content-Type', 'application/zip')
125
            ->withHeader('Content-Length', (string) filesize($archiveFilename))
126
            ->withBody(new Stream($archiveFilename));
127
    }
128
129
    /**
130
     * @param Folder $folder
131
     * @param \ZipArchive $zip
132
     * @param array $parentFolders
133
     */
134
    protected function addFolderToArchiveRecursive(Folder $folder, \ZipArchive $zip, array $parentFolders = []): void
135
    {
136
        $folderName = $folder->getName();
137
        $parentFolders[] = $folderName;
138
        $implodedParentFolders = implode('/', $parentFolders);
139
        $zip->addEmptyDir($implodedParentFolders);
140
        $files = $folder->getFiles();
141
        foreach ($files as $file) {
142
            $zip->addFile($file->getForLocalProcessing(false), $implodedParentFolders . '/' . $file->getName());
143
        }
144
        $subFolders = $folder->getSubfolders();
145
        foreach ($subFolders as $subFolder) {
146
            $this->addFolderToArchiveRecursive($subFolder, $zip, $parentFolders);
147
        }
148
    }
149
150
    /**
151
     * @return JsonResponse
152
     */
153
    public function damGetLogoutUrlAction(): JsonResponse
154
    {
155
        if (empty($this->getBackendUser()->user['uid'])) {
156
            return new JsonExceptionResponse(new ControllerException('User is not logged in', 1554380677));
157
        }
158
        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
159
        return new JsonResponse([ (string)$uriBuilder->buildUriFromRoute('logout') ]);
160
    }
161
162
    /**
163
     * Set module state of BE user. Send a json array as ['data'] POST
164
     *
165
     * @param ServerRequestInterface $request
166
     * @return JsonResponse
167
     */
168
    public function setStateAction(ServerRequestInterface $request): JsonResponse
169
    {
170
        $backendUser = $this->getBackendUser();
171
        $backendUser->uc['digital_asset_management'] = $request->getParsedBody()['data'] ?? [];
172
        $backendUser->writeUC();
173
        return new JsonResponse();
174
    }
175
176
    /**
177
     * @return JsonResponse
178
     */
179
    public function getStateAction(): JsonResponse
180
    {
181
        return new JsonResponse([ 'data' => $this->getBackendUser()->uc['digital_asset_management'] ?? []]);
182
    }
183
184
    /**
185
     * @param ServerRequestInterface $request
186
     *
187
     * @return JsonResponse
188
     */
189
    public function createFolderAction(ServerRequestInterface $request): JsonResponse
190
    {
191
        $identifier = $request->getQueryParams()['identifier'] ?? '';
192
        if (empty($identifier)) {
193
            return new JsonExceptionResponse(new ControllerException('Identifier needed', 1554204780));
194
        }
195
        try {
196
            $folder = $this->createFolderRecursive($identifier);
197
            return new JsonResponse([new FolderItemFolder($folder)]);
198
        } catch (ResourceException $e) {
199
            return new JsonExceptionResponse($e);
200
        }
201
    }
202
203
    /**
204
     * @param ServerRequestInterface $request
205
     *
206
     * @return JsonResponse
207
     */
208
    public function fileUploadAction(ServerRequestInterface $request): JsonResponse
209
    {
210
        $identifier = $request->getQueryParams()['identifier'] ?? '';
211
        $conflictMode = $request->getQueryParams()['conflictMode'] ?? '';
212
        $tempFilename = '';
213
        try {
214
            if (empty($identifier)) {
215
                throw new ControllerException('Identifier needed', 1554132801);
216
            }
217
            if (empty($conflictMode) || !in_array($conflictMode, ['replace', 'cancel', 'rename'], true)) {
218
                throw new ControllerException('conflictMode must be one of "replace", "cancel", "rename"');
219
            }
220
            $folderIdentifier = dirname($identifier) . '/';
221
            $fileIdentifier = basename($identifier);
222
            $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
223
            try {
224
                $folder = $resourceFactory->retrieveFileOrFolderObject($folderIdentifier);
225
            } catch (ResourceDoesNotExistException $e) {
226
                $folder = $this->createFolderRecursive($folderIdentifier);
227
            }
228
            $tempFilename = tempnam(sys_get_temp_dir(), 'upload_');
229
            file_put_contents($tempFilename, $request->getBody());
230
            $file = $folder->addFile($tempFilename, $fileIdentifier, (string)DuplicationBehavior::cast($conflictMode));
231
            $fileExtension = strtolower($file->getExtension());
232
            if (GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileExtension)
233
                || GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['mediafile_ext'], $fileExtension)
234
            ) {
235
                return new JsonResponse([new FolderItemImage($file)]);
236
            }
237
            return new JsonResponse([new FolderItemFile($file)]);
238
        } catch (ResourceException $e) {
239
            if (!empty($tempFilename) && file_exists($tempFilename)) {
240
                unlink($tempFilename);
241
            }
242
            return new JsonExceptionResponse($e);
243
        } catch (ControllerException $e) {
244
            return new JsonExceptionResponse($e);
245
        }
246
    }
247
248
    /**
249
     * Create folders from a not yet sanitized combined identifier
250
     *
251
     * @param string $folderIdentifier
252
     * @return Folder
253
     */
254
    protected function createFolderRecursive(string $folderIdentifier): Folder
255
    {
256
        $splitCombinedIdentifier = explode(':', $folderIdentifier);
257
        if (count($splitCombinedIdentifier) !== 2) {
258
            throw new ControllerException('Invalid identifier', 1554725198);
259
        }
260
        $storage = $splitCombinedIdentifier[0];
261
        if (!is_numeric($storage)) {
262
            throw new ControllerException('Broken storage identifier', 1554725216);
263
        }
264
        $storage = (int)$storage;
265
        $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
266
        $storage = $resourceFactory->getStorageObject($storage);
267
        $folderObject = $storage->getFolder('/');
268
        $folders = array_filter(explode('/', ltrim($splitCombinedIdentifier[1], '/')));
269
        foreach ($folders as $folder) {
270
            $sanitizedFolderName = $storage->sanitizeFileName($folder, $folderObject);
271
            try {
272
                $folderObject = $folderObject->getSubfolder($sanitizedFolderName);
273
            } catch (\InvalidArgumentException $e) {
274
                $folderObject->createFolder($sanitizedFolderName);
275
                $folderObject = $folderObject->getSubfolder($sanitizedFolderName);
276
            }
277
        }
278
        return $folderObject;
279
    }
280
281
    /**
282
     * File exists for a client side created pseudo combined identifier that
283
     * is sanitized on server side.
284
     *
285
     * @param ServerRequestInterface $request
286
     * @return JsonResponse
287
     */
288
    public function fileExistsAction(ServerRequestInterface $request): JsonResponse
289
    {
290
        try {
291
            $identifier = $request->getQueryParams()['identifier'];
292
            if (empty($identifier)) {
293
                throw new ControllerException('Identifier needed', 1554125449);
294
            }
295
            $splitCombinedIdentifier = explode(':', $identifier);
296
            if (count($splitCombinedIdentifier) !== 2) {
297
                throw new ControllerException('Invalid identifier', 1554717790);
298
            }
299
            $storage = $splitCombinedIdentifier[0];
300
            if (!is_numeric($storage)) {
301
                throw new ControllerException('Broken storage identifier', 1554717432);
302
            }
303
            $storage = (int)$storage;
304
            $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
305
            $storage = $resourceFactory->getStorageObject($storage);
306
            $folderObject = $storage->getFolder('/');
307
            $folders = explode('/', ltrim($splitCombinedIdentifier[1], '/'));
308
            $fileName = array_pop($folders);
309
            if (empty($fileName)) {
310
                throw new ControllerException('Empty filename not allowed', 1554719701);
311
            }
312
            foreach ($folders as $folder) {
313
                // Throws on '..' and '.', so no path traversal possible
314
                $folderObject = $folderObject->getSubfolder($storage->sanitizeFileName($folder, $folderObject));
315
            }
316
            $fileName = $folderObject->getStorage()->sanitizeFileName($fileName, $folderObject);
317
        } catch (\InvalidArgumentException $e) {
318
            return new FileExistsResponse(FileExistsResponse::PARENT_FOLDER_DOES_NOT_EXIST);
319
        } catch (ResourceException $e) {
320
            return new JsonExceptionResponse($e);
321
        } catch (ControllerException $e) {
322
            return new JsonExceptionResponse($e);
323
        }
324
        if ($folderObject->hasFile($fileName)) {
325
            $file = $storage->getFileInFolder($fileName, $folderObject);
326
            // If file is an image or media, create image object, else file object
327
            $fileExtension = strtolower($file->getExtension());
328
            if (GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileExtension)
329
                || GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['mediafile_ext'], $fileExtension)
330
            ) {
331
                return new JsonResponse([new FolderItemImage($file)]);
332
            }
333
            return new JsonResponse([new FolderItemFile($file)]);
334
        } else {
335
            return new FileExistsResponse(FileExistsResponse::FILE_DOES_NOT_EXIST);
336
        }
337
    }
338
339
    /**
340
     * Return item list (folders, files, images) of a server side created valid
341
     * storage:path FAL folder identifier. GET request with identifier argument.
342
     *
343
     * @param ServerRequestInterface $request
344
     *
345
     * @return JsonResponse
346
     */
347
    public function getFolderItemsAction(ServerRequestInterface $request): JsonResponse
348
    {
349
        try {
350
            $identifier = $request->getQueryParams()['identifier'] ?? '';
351
            if (empty($identifier)) {
352
                throw new ControllerException('Identifier needed', 1553699828);
353
            }
354
            $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
355
            $folderObject = $resourceFactory->getObjectFromCombinedIdentifier($identifier);
356
            if (!$folderObject instanceof Folder) {
357
                throw new ControllerException('Identifier is not a folder', 1553701684);
358
            }
359
            $subFolders = $folderObject->getSubfolders();
360
            $folders = [];
361
            foreach ($subFolders as $subFolder) {
362
                $folders[] = new FolderItemFolder($subFolder);
363
            }
364
            $allFiles = $folderObject->getFiles();
365
            $files = [];
366
            $images = [];
367
            foreach ($allFiles as $file) {
368
                // If file is an image or media, create image object, else file object
369
                $fileExtension = strtolower($file->getExtension());
370
                if (GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileExtension)
371
                    || GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['mediafile_ext'], $fileExtension)
372
                ) {
373
                    $images[] = new FolderItemImage($file);
374
                } else {
375
                    $files[] = new FolderItemFile($file);
376
                }
377
            }
378
            return new FolderItemsResponse($folders, $files, $images);
379
        } catch (ResourceException $e) {
380
            return new JsonExceptionResponse($e);
381
        } catch (ControllerException $e) {
382
            return new JsonExceptionResponse($e);
383
        }
384
    }
385
386
    /**
387
     * Returns list of storages (admins), or file mounts (non-admin). Admins
388
     * do NOT receive a list of file mounts, just the storages.
389
     *
390
     * Storages are returned in no particular order, file mounts are ordered
391
     * by 'sorting' DB field.
392
     *
393
     * Return structure is an array of Storage or FileMount objects.
394
     *
395
     * @return JsonResponse
396
     */
397
    public function getStoragesAndMountsAction(): JsonResponse
398
    {
399
        $backendUser = $this->getBackendUser();
400
        $storages = $backendUser->getFileStorages();
401
        $entities = [];
402
        if ($backendUser->isAdmin()) {
403
            foreach ($storages as $storage) {
404
                $entities[] = new Storage($storage);
405
            }
406
        } else {
407
            foreach ($storages as $storage) {
408
                $fileMounts = $storage->getFileMounts();
409
                foreach ($fileMounts as $fileMount) {
410
                    $entities[] = new FileMount($storage, $fileMount);
411
                }
412
            }
413
        }
414
        return new StoragesAndMountsResponse($entities);
415
    }
416
417
    /**
418
     * Returns list of folders only. No files, no images
419
     * Result is sorted by name
420
     *
421
     * Return structure is an array of TreeItemFolder objects.
422
     *
423
     * @param ServerRequestInterface $request
424
     *
425
     * @return JsonResponse
426
     */
427
    public function getTreeFoldersAction(ServerRequestInterface $request): JsonResponse
428
    {
429
        try {
430
            $identifier = $request->getQueryParams()['identifier'] ?? '';
431
            if (empty($identifier)) {
432
                throw new ControllerException('Identifier needed', 1553699828);
433
            }
434
            $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
435
            $folderObject = $resourceFactory->getObjectFromCombinedIdentifier($identifier);
436
            if (!$folderObject instanceof Folder) {
437
                throw new ControllerException('Identifier is not a folder', 1553701684);
438
            }
439
            $subFolders = $folderObject->getSubfolders();
440
            $folders = [];
441
            foreach ($subFolders as $subFolder) {
442
                $folders[] = new TreeItemFolder($subFolder);
443
            }
444
            return new JsonResponse($folders);
445
        } catch (ResourceException $e) {
446
            return new JsonExceptionResponse($e);
447
        } catch (ControllerException $e) {
448
            return new JsonExceptionResponse($e);
449
        }
450
    }
451
452
    /**
453
     * Copy files or folders
454
     * Query parameters
455
     *  'identifiers' array of identifier to copy
456
     *  'targetFolderIdentifier' string the target identifier. Must be a folder.
457
     *  'conflictMode' string one of: "replace", "cancel", "rename", as defined in \TYPO3\CMS\Core\Resource\DuplicationBehavior
458
     *
459
     * @param ServerRequestInterface $request
460
     *
461
     * @return JsonResponse
462
     */
463
    public function copyResourcesAction(ServerRequestInterface $request): JsonResponse
464
    {
465
        try {
466
            $identifiers = $request->getQueryParams()['identifiers'];
467
            $conflictMode = $request->getQueryParams()['conflictMode'] ?? '';
468
            $targetFolderIdentifier = $request->getQueryParams()['targetFolderIdentifier'];
469
            if (empty($identifiers)) {
470
                throw new ControllerException('Identifiers needed', 1553699828);
471
            }
472
            if (empty($conflictMode) || !in_array($conflictMode, ['replace', 'cancel', 'rename'], true)) {
473
                throw new ControllerException('conflictMode must be one of "replace", "cancel", "rename"');
474
            }
475
            if (empty($targetFolderIdentifier)) {
476
                throw new ControllerException(
477
                    'Target folder identifier needed',
478
                    1554122023
479
                );
480
            }
481
            $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
482
            $targetFolderObject = $resourceFactory->getObjectFromCombinedIdentifier($targetFolderIdentifier);
483
            if (!$targetFolderObject instanceof Folder) {
484
                throw new ControllerException('Target identifier is not a folder', 1553701684);
485
            }
486
        } catch (ResourceException $e) {
487
            return new JsonExceptionResponse($e);
488
        } catch (ControllerException $e) {
489
            return new JsonExceptionResponse($e);
490
        }
491
        $resources = [];
492
        foreach ($identifiers as $identifier) {
493
            $state = FileOperationResult::FAILED;
494
            $message = '';
495
            $resultEntity = null;
496
            try {
497
                $sourceObject = $resourceFactory->getObjectFromCombinedIdentifier($identifier);
498
                if ($resultObject = $sourceObject->copyTo($targetFolderObject, null, (string)DuplicationBehavior::cast($conflictMode))) {
499
                    if ($resultObject instanceof Folder) {
500
                        $resultEntity = new FolderItemFolder($resultObject);
501
                    } else {
502
                        $resultEntity = new FolderItemFile($resultObject);
503
                    }
504
                    $state = FileOperationResult::COPIED;
505
                }
506
            } catch (InvalidTargetFolderException $e) {
507
                $message = $e->getMessage();
508
            } catch (ResourceException $e) {
509
                $message = $e->getMessage();
510
            }
511
            $resources[] = new FileOperationResult($identifier, $state, $message, $resultEntity);
512
        }
513
        return new FileOperationResponse($resources);
514
    }
515
516
    /**
517
     * Move files or folders
518
     * Query parameters
519
     *  'identifiers' array of identifier to move
520
     *  'targetFolderIdentifier' string the target identifier. Must be a folder.
521
     *  'conflictMode' string one of: "replace", "cancel", "rename", as defined in \TYPO3\CMS\Core\Resource\DuplicationBehavior
522
     *
523
     * @param ServerRequestInterface $request
524
     *
525
     * @return JsonResponse
526
     */
527
    public function moveResourcesAction(ServerRequestInterface $request): JsonResponse
528
    {
529
        try {
530
            $identifiers = $request->getQueryParams()['identifiers'];
531
            $conflictMode = $request->getQueryParams()['conflictMode'] ?? '';
532
            $targetFolderIdentifier = $request->getQueryParams()['targetFolderIdentifier'];
533
            if (empty($identifiers)) {
534
                throw new ControllerException('Identifiers needed', 1553699828);
535
            }
536
            if (empty($conflictMode) || !in_array($conflictMode, ['replace', 'cancel', 'rename'], true)) {
537
                throw new ControllerException('conflictMode must be one of "replace", "cancel", "rename"');
538
            }
539
            if (empty($targetFolderIdentifier)) {
540
                throw new ControllerException('Target folder identifier needed', 1554122023);
541
            }
542
            $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
543
            $targetFolderObject = $resourceFactory->getObjectFromCombinedIdentifier($targetFolderIdentifier);
544
            if (!$targetFolderObject instanceof Folder) {
545
                throw new ControllerException('Target identifier is not a folder', 1553701684);
546
            }
547
        } catch (ResourceException $e) {
548
            return new JsonExceptionResponse($e);
549
        } catch (ControllerException $e) {
550
            return new JsonExceptionResponse($e);
551
        }
552
        $resources = [];
553
        foreach ($identifiers as $identifier) {
554
            $state = FileOperationResult::FAILED;
555
            $message = '';
556
            $resultEntity = null;
557
            try {
558
                $sourceObject = $resourceFactory->getObjectFromCombinedIdentifier($identifier);
559
                if ($resultObject = $sourceObject->moveTo($targetFolderObject, null, (string)DuplicationBehavior::cast($conflictMode))) {
560
                    if ($resultObject instanceof Folder) {
561
                        $resultEntity = new FolderItemFolder($resultObject);
562
                    } else {
563
                        $resultEntity = new FolderItemFile($resultObject);
564
                    }
565
                    $state = FileOperationResult::MOVED;
566
                }
567
            } catch (InvalidTargetFolderException $e) {
568
                $message = $e->getMessage();
569
            } catch (ResourceException $e) {
570
                $message = $e->getMessage();
571
            }
572
            $resources[] = new FileOperationResult($identifier, $state, $message, $resultEntity);
573
        }
574
        return new FileOperationResponse($resources);
575
    }
576
577
    /**
578
     * rename file or folder
579
     * Query parameters
580
     *  'identifier' string identifier to rename
581
     *  'targetName' string The new name of file or folder.
582
     *  'conflictMode' string one of: "replace", "cancel", "rename"
583
     *
584
     * @param ServerRequestInterface $request
585
     *
586
     * @return JsonResponse
587
     */
588
    public function renameResourcesAction(ServerRequestInterface $request): JsonResponse
589
    {
590
        try {
591
            $identifier = $request->getQueryParams()['identifier'];
592
            $targetName = $request->getQueryParams()['targetName'];
593
            $conflictMode = $request->getQueryParams()['conflictMode'] ?? '';
594
            if (empty($identifier)) {
595
                throw new ControllerException('Identifier needed', 1553699828);
596
            }
597
            if (empty($conflictMode) || !in_array($conflictMode, ['replace', 'cancel', 'rename'], true)) {
598
                throw new ControllerException('conflictMode must be one of "replace", "cancel", "rename"');
599
            }
600
            if (empty($targetName)) {
601
                throw new ControllerException('Target name needed', 1554193259);
602
            }
603
            $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
604
            $fileOrFolder = $resourceFactory->retrieveFileOrFolderObject($identifier);
605
        } catch (ResourceException $e) {
606
            return new JsonExceptionResponse($e);
607
        } catch (ControllerException $e) {
608
            return new JsonExceptionResponse($e);
609
        }
610
        $resources = [];
611
        $state = FileOperationResult::FAILED;
612
        $resultEntity = null;
613
        try {
614
            if ($fileOrFolder === null) {
615
                throw new ResourceException\ResourceDoesNotExistException('Resource does not exist');
616
            } else {
617
                $resultObject = $fileOrFolder->rename($targetName, (string)DuplicationBehavior::cast($conflictMode));
618
                if ($resultObject instanceof Folder) {
619
                    $resultEntity = new FolderItemFolder($resultObject);
620
                    $message = 'Folder was successfully renamed';
621
                } else {
622
                    $resultEntity = new FolderItemFile($resultObject);
623
                    $message = 'File was successfully renamed';
624
                }
625
                $state = FileOperationResult::RENAMED;
626
            }
627
        } catch (ResourceException $e) {
628
            $message = $e->getMessage();
629
        }
630
        $resources[] = new FileOperationResult($identifier, $state, $message, $resultEntity);
631
        return new JsonResponse($resources);
632
    }
633
634
    /**
635
     * delete file or folder
636
     * Query parameters
637
     *  'identifiers' array of strings identifier of file or folder to delete
638
     *
639
     * @param ServerRequestInterface $request
640
     *
641
     * @return JsonResponse
642
     */
643
    public function deleteResourcesAction(ServerRequestInterface $request): JsonResponse
644
    {
645
        try {
646
            $identifiers = $request->getQueryParams()['identifiers'];
647
            if (empty($identifiers)) {
648
                throw new ControllerException('Identifiers needed', 1553699828);
649
            }
650
        } catch (ControllerException $e) {
651
            return new JsonExceptionResponse($e);
652
        }
653
        $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
654
        $resources = [];
655
        foreach ($identifiers as $identifier) {
656
            try {
657
                $sourceObject = $resourceFactory->getObjectFromCombinedIdentifier($identifier);
658
                if ($sourceObject->delete(true)) {
659
                    $state = FileOperationResult::DELETED;
660
                    $message = 'Resource deleted';
661
                } else {
662
                    $state = FileOperationResult::FAILED;
663
                    $message = 'Resource could not be deleted';
664
                }
665
            } catch (ResourceException $e) {
666
                $state = FileOperationResult::FAILED;
667
                $message = $e->getMessage();
668
            }
669
            $resources[] = new FileOperationResult($identifier, $state, $message, null);
670
        }
671
        return new FileOperationResponse($resources);
672
    }
673
674
    /**
675
     * @return BackendUserAuthentication
676
     */
677
    protected function getBackendUser(): BackendUserAuthentication
678
    {
679
        return $GLOBALS['BE_USER'];
680
    }
681
}
682