Passed
Push — api-basic-search ( 778dbd )
by Jürgen
05:33 queued 11s
created

AjaxController::searchAction()   B

Complexity

Conditions 9
Paths 35

Size

Total Lines 36
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

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