FileListController::handleRequest()   F
last analyzed

Complexity

Conditions 19
Paths 1016

Size

Total Lines 107
Code Lines 70

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 70
dl 0
loc 107
rs 0.3499
c 0
b 0
f 0
cc 19
nc 1016
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
declare(strict_types=1);
4
5
/*
6
 * This file is part of the TYPO3 CMS project.
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17
18
namespace TYPO3\CMS\Filelist\Controller;
19
20
use Psr\Http\Message\ResponseFactoryInterface;
21
use Psr\Http\Message\ResponseInterface;
22
use Psr\Http\Message\ServerRequestInterface;
23
use Psr\Log\LoggerAwareInterface;
24
use Psr\Log\LoggerAwareTrait;
25
use TYPO3\CMS\Backend\Clipboard\Clipboard;
26
use TYPO3\CMS\Backend\Routing\UriBuilder;
27
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
28
use TYPO3\CMS\Backend\Template\ModuleTemplate;
29
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
30
use TYPO3\CMS\Backend\Utility\BackendUtility;
31
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
32
use TYPO3\CMS\Core\Imaging\Icon;
33
use TYPO3\CMS\Core\Imaging\IconFactory;
34
use TYPO3\CMS\Core\Localization\LanguageService;
35
use TYPO3\CMS\Core\Messaging\FlashMessage;
36
use TYPO3\CMS\Core\Messaging\FlashMessageService;
37
use TYPO3\CMS\Core\Page\PageRenderer;
38
use TYPO3\CMS\Core\Resource\DuplicationBehavior;
39
use TYPO3\CMS\Core\Resource\Exception;
40
use TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException;
41
use TYPO3\CMS\Core\Resource\Folder;
42
use TYPO3\CMS\Core\Resource\ResourceFactory;
43
use TYPO3\CMS\Core\Resource\ResourceStorage;
44
use TYPO3\CMS\Core\Resource\Search\FileSearchDemand;
45
use TYPO3\CMS\Core\Resource\StorageRepository;
46
use TYPO3\CMS\Core\Resource\Utility\ListUtility;
47
use TYPO3\CMS\Core\Utility\File\ExtendedFileUtility;
48
use TYPO3\CMS\Core\Utility\GeneralUtility;
49
use TYPO3\CMS\Core\Utility\MathUtility;
50
use TYPO3\CMS\Filelist\FileList;
51
use TYPO3\CMS\Fluid\View\StandaloneView;
52
use TYPO3Fluid\Fluid\View\ViewInterface;
53
54
/**
55
 * Script Class for creating the list of files in the File > Filelist module
56
 * @internal this is a concrete TYPO3 controller implementation and solely used for EXT:filelist and not part of TYPO3's Core API.
57
 */
58
class FileListController implements LoggerAwareInterface
59
{
60
    use LoggerAwareTrait;
61
62
    protected array $MOD_MENU = [];
63
    protected array $MOD_SETTINGS = [];
64
    protected string $id = '';
65
    protected string $cmd = '';
66
    protected string $searchTerm = '';
67
    protected int $pointer = 0;
68
    protected ?Folder $folderObject = null;
69
    protected ?DuplicationBehavior $overwriteExistingFiles = null;
70
71
    protected UriBuilder $uriBuilder;
72
    protected PageRenderer $pageRenderer;
73
    protected IconFactory $iconFactory;
74
    protected ResourceFactory $resourceFactory;
75
    protected ModuleTemplateFactory $moduleTemplateFactory;
76
    protected ResponseFactoryInterface $responseFactory;
77
78
    protected ?ModuleTemplate $moduleTemplate = null;
79
    protected ?ViewInterface $view = null;
80
    protected ?FileList $filelist = null;
81
82
    public function __construct(
83
        UriBuilder $uriBuilder,
84
        PageRenderer $pageRenderer,
85
        IconFactory $iconFactory,
86
        ResourceFactory $resourceFactory,
87
        ModuleTemplateFactory $moduleTemplateFactory,
88
        ResponseFactoryInterface $responseFactory
89
    ) {
90
        $this->uriBuilder = $uriBuilder;
91
        $this->pageRenderer = $pageRenderer;
92
        $this->iconFactory = $iconFactory;
93
        $this->resourceFactory = $resourceFactory;
94
        $this->moduleTemplateFactory = $moduleTemplateFactory;
95
        $this->responseFactory = $responseFactory;
96
    }
97
98
    public function handleRequest(ServerRequestInterface $request): ResponseInterface
99
    {
100
        $lang = $this->getLanguageService();
101
        $backendUser = $this->getBackendUser();
102
103
        $this->moduleTemplate = $this->moduleTemplateFactory->create($request);
104
        $this->moduleTemplate->setTitle($lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:mlang_tabs_tab'));
105
106
        $queryParams = $request->getQueryParams();
107
        $parsedBody = $request->getParsedBody();
108
109
        $this->id = (string)($parsedBody['id'] ?? $queryParams['id'] ?? '');
110
        $this->cmd = (string)($parsedBody['cmd'] ?? $queryParams['cmd'] ?? '');
111
        $this->searchTerm = (string)($parsedBody['searchTerm'] ?? $queryParams['searchTerm'] ?? '');
112
        $this->pointer = (int)($request->getParsedBody()['pointer'] ?? $request->getQueryParams()['pointer'] ?? 0);
113
        $this->overwriteExistingFiles = DuplicationBehavior::cast(
114
            $parsedBody['overwriteExistingFiles'] ?? $queryParams['overwriteExistingFiles'] ?? null
115
        );
116
117
        try {
118
            if ($this->id !== '') {
119
                $backendUser->evaluateUserSpecificFileFilterSettings();
120
                $storage = GeneralUtility::makeInstance(StorageRepository::class)->findByCombinedIdentifier($this->id);
121
                if ($storage !== null) {
122
                    $identifier = substr($this->id, strpos($this->id, ':') + 1);
123
                    if (!$storage->hasFolder($identifier)) {
124
                        $identifier = $storage->getFolderIdentifierFromFileIdentifier($identifier);
125
                    }
126
                    $this->folderObject = $storage->getFolder($identifier);
127
                    // Disallow access to fallback storage 0
128
                    if ($storage->getUid() === 0) {
129
                        throw new InsufficientFolderAccessPermissionsException(
130
                            'You are not allowed to access files outside your storages',
131
                            1434539815
132
                        );
133
                    }
134
                    // Disallow the rendering of the processing folder (e.g. could be called manually)
135
                    if ($this->folderObject && $storage->isProcessingFolder($this->folderObject)) {
136
                        $this->folderObject = $storage->getRootLevelFolder();
137
                    }
138
                }
139
            } else {
140
                // Take the first object of the first storage
141
                $fileStorages = $backendUser->getFileStorages();
142
                $fileStorage = reset($fileStorages);
143
                if ($fileStorage) {
144
                    $this->folderObject = $fileStorage->getRootLevelFolder();
145
                } else {
146
                    throw new \RuntimeException('Could not find any folder to be displayed.', 1349276894);
147
                }
148
            }
149
150
            if ($this->folderObject && !$this->folderObject->getStorage()->isWithinFileMountBoundaries($this->folderObject)) {
151
                throw new \RuntimeException('Folder not accessible.', 1430409089);
152
            }
153
        } catch (InsufficientFolderAccessPermissionsException $permissionException) {
154
            $this->folderObject = null;
155
            $this->addFlashMessage(
156
                sprintf($lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:missingFolderPermissionsMessage'), $this->id),
157
                $lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:missingFolderPermissionsTitle'),
158
                FlashMessage::ERROR
159
            );
160
        } catch (Exception $fileException) {
161
            $this->folderObject = null;
162
            // Take the first object of the first storage
163
            $fileStorages = $backendUser->getFileStorages();
164
            $fileStorage = reset($fileStorages);
165
            if ($fileStorage instanceof ResourceStorage) {
166
                $this->folderObject = $fileStorage->getRootLevelFolder();
167
                if (!$fileStorage->isWithinFileMountBoundaries($this->folderObject)) {
168
                    $this->folderObject = null;
169
                }
170
            }
171
            if (!$this->folderObject) {
172
                $this->addFlashMessage(
173
                    sprintf($lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:folderNotFoundMessage'), $this->id),
174
                    $lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:folderNotFoundTitle'),
175
                    FlashMessage::ERROR
176
                );
177
            }
178
        } catch (\RuntimeException $e) {
179
            $this->folderObject = null;
180
            $this->addFlashMessage(
181
                $e->getMessage() . ' (' . $e->getCode() . ')',
182
                $lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:folderNotFoundTitle'),
183
                FlashMessage::ERROR
184
            );
185
        }
186
187
        if ($this->folderObject
188
            && !$this->folderObject->getStorage()->checkFolderActionPermission('read', $this->folderObject)
189
        ) {
190
            $this->folderObject = null;
191
        }
192
193
        $this->initializeView();
194
        $this->initializeModule($request);
195
196
        // In case the folderObject is NULL, the request is either invalid or the user
197
        // does not have necessary permissions. Just render and return the "empty" view.
198
        if ($this->folderObject === null) {
199
            return $this->htmlResponse(
200
                $this->moduleTemplate->setContent($this->view->render())->renderContent()
0 ignored issues
show
Bug introduced by
The method render() does not exist on null. ( Ignorable by Annotation )

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

200
                $this->moduleTemplate->setContent($this->view->/** @scrutinizer ignore-call */ render())->renderContent()

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...
201
            );
202
        }
203
204
        return $this->processRequest($request);
205
    }
206
207
    protected function processRequest(ServerRequestInterface $request): ResponseInterface
208
    {
209
        $lang = $this->getLanguageService();
210
211
        // Initialize FileList, including the clipboard actions
212
        $this->initializeFileList($request);
213
214
        // Generate the file listing markup
215
        $this->generateFileList();
216
217
        // Generate the clipboard, if enabled
218
        if ($this->MOD_SETTINGS['clipBoard'] ?? false) {
219
            $this->view->assign('clipBoardHtml', $this->filelist->clipObj->printClipboard());
220
        }
221
222
        // Register drag-uploader
223
        $this->registerDrapUploader();
224
225
        // Register the display thumbnails / show clipboard checkboxes
226
        $this->registerFileListCheckboxes();
227
228
        // Register additional doc header buttons
229
        $this->registerAdditionalDocHeaderButtons($request);
230
231
        // Add additional view variables
232
        $this->view->assignMultiple([
233
            'headline' => $this->getModuleHeadline(),
234
            'folderIdentifier' => $this->folderObject->getCombinedIdentifier(),
0 ignored issues
show
Bug introduced by
The method getCombinedIdentifier() does not exist on null. ( Ignorable by Annotation )

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

234
            'folderIdentifier' => $this->folderObject->/** @scrutinizer ignore-call */ getCombinedIdentifier(),

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...
235
            'searchTerm' => $this->searchTerm,
236
        ]);
237
238
        // Overwrite the default module title, adding the specific module headline (the folder name)
239
        $this->moduleTemplate->setTitle(
0 ignored issues
show
Bug introduced by
The method setTitle() does not exist on null. ( Ignorable by Annotation )

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

239
        $this->moduleTemplate->/** @scrutinizer ignore-call */ 
240
                               setTitle(

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...
240
            $lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:mlang_tabs_tab'),
241
            $this->getModuleHeadline()
242
        );
243
244
        // Additional doc header information: current path and folder info
245
        $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation([
246
            '_additional_info' => $this->filelist->getFolderInfo(),
0 ignored issues
show
Bug introduced by
The method getFolderInfo() does not exist on null. ( Ignorable by Annotation )

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

246
            '_additional_info' => $this->filelist->/** @scrutinizer ignore-call */ getFolderInfo(),

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...
247
        ]);
248
        $this->moduleTemplate->getDocHeaderComponent()->setMetaInformationForResource($this->folderObject);
0 ignored issues
show
Bug introduced by
It seems like $this->folderObject can also be of type null; however, parameter $resource of TYPO3\CMS\Backend\Templa...nformationForResource() does only seem to accept TYPO3\CMS\Core\Resource\ResourceInterface, maybe add an additional type check? ( Ignorable by Annotation )

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

248
        $this->moduleTemplate->getDocHeaderComponent()->setMetaInformationForResource(/** @scrutinizer ignore-type */ $this->folderObject);
Loading history...
249
250
        // Render view and return the response
251
        return $this->htmlResponse(
252
            $this->moduleTemplate->setContent($this->view->render())->renderContent()
253
        );
254
    }
255
256
    protected function initializeView(): void
257
    {
258
        $this->view = GeneralUtility::makeInstance(StandaloneView::class);
259
        $this->view->setTemplateRootPaths(['EXT:filelist/Resources/Private/Templates/FileList']);
0 ignored issues
show
Bug introduced by
The method setTemplateRootPaths() does not exist on TYPO3Fluid\Fluid\View\ViewInterface. It seems like you code against a sub-type of TYPO3Fluid\Fluid\View\ViewInterface such as TYPO3\CMS\Fluid\View\AbstractTemplateView. ( Ignorable by Annotation )

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

259
        $this->view->/** @scrutinizer ignore-call */ 
260
                     setTemplateRootPaths(['EXT:filelist/Resources/Private/Templates/FileList']);
Loading history...
260
        $this->view->setPartialRootPaths(['EXT:filelist/Resources/Private/Partials']);
0 ignored issues
show
Bug introduced by
The method setPartialRootPaths() does not exist on TYPO3Fluid\Fluid\View\ViewInterface. It seems like you code against a sub-type of TYPO3Fluid\Fluid\View\ViewInterface such as TYPO3\CMS\Fluid\View\AbstractTemplateView. ( Ignorable by Annotation )

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

260
        $this->view->/** @scrutinizer ignore-call */ 
261
                     setPartialRootPaths(['EXT:filelist/Resources/Private/Partials']);
Loading history...
261
        $this->view->setLayoutRootPaths(['EXT:filelist/Resources/Private/Layouts']);
0 ignored issues
show
Bug introduced by
The method setLayoutRootPaths() does not exist on TYPO3Fluid\Fluid\View\ViewInterface. It seems like you code against a sub-type of TYPO3Fluid\Fluid\View\ViewInterface such as TYPO3\CMS\Fluid\View\AbstractTemplateView. ( Ignorable by Annotation )

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

261
        $this->view->/** @scrutinizer ignore-call */ 
262
                     setLayoutRootPaths(['EXT:filelist/Resources/Private/Layouts']);
Loading history...
262
        $this->view->setTemplatePathAndFilename(
0 ignored issues
show
Bug introduced by
The method setTemplatePathAndFilename() does not exist on TYPO3Fluid\Fluid\View\ViewInterface. It seems like you code against a sub-type of TYPO3Fluid\Fluid\View\ViewInterface such as TYPO3\CMS\Fluid\View\AbstractTemplateView. ( Ignorable by Annotation )

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

262
        $this->view->/** @scrutinizer ignore-call */ 
263
                     setTemplatePathAndFilename(
Loading history...
263
            GeneralUtility::getFileAbsFileName('EXT:filelist/Resources/Private/Templates/File/List.html')
264
        );
265
        $this->view->assign('currentIdentifier', $this->id);
266
267
        // @todo: These modules should be merged into one module
268
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Filelist/FileListLocalisation');
269
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Filelist/FileList');
270
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Filelist/FileDelete');
271
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
272
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ClipboardComponent');
273
        $this->pageRenderer->addInlineLanguageLabelFile(
274
            'EXT:backend/Resources/Private/Language/locallang_alt_doc.xlf',
275
            'buttons'
276
        );
277
    }
278
279
    protected function initializeModule(ServerRequestInterface $request): void
280
    {
281
        // Configure the "menu" - which is used internally to save the values of sorting, displayThumbs etc.
282
        // Values NOT in this array will not be saved in the settings-array for the module.
283
        $this->MOD_MENU = ['sort' => '', 'reverse' => '', 'displayThumbs' => '', 'clipBoard' => ''];
284
        $this->MOD_SETTINGS = BackendUtility::getModuleData(
285
            $this->MOD_MENU,
286
            $request->getParsedBody()['SET'] ?? $request->getQueryParams()['SET'] ?? null,
287
            'file_list'
288
        );
289
290
        $userTsConfig = $this->getBackendUser()->getTSConfig();
291
292
        // Set predefined value for DisplayThumbnails:
293
        if (($userTsConfig['options.']['file_list.']['enableDisplayThumbnails'] ?? '') === 'activated') {
294
            $this->MOD_SETTINGS['displayThumbs'] = true;
295
        } elseif (($userTsConfig['options.']['file_list.']['enableDisplayThumbnails'] ?? '') === 'deactivated') {
296
            $this->MOD_SETTINGS['displayThumbs'] = false;
297
        }
298
        // Set predefined value for Clipboard:
299
        if (($userTsConfig['options.']['file_list.']['enableClipBoard'] ?? '') === 'activated') {
300
            $this->MOD_SETTINGS['clipBoard'] = true;
301
        } elseif (($userTsConfig['options.']['file_list.']['enableClipBoard'] ?? '') === 'deactivated') {
302
            $this->MOD_SETTINGS['clipBoard'] = false;
303
        }
304
        if (!isset($this->MOD_SETTINGS['sort'])) {
305
            // Set default sorting
306
            $this->MOD_SETTINGS['sort'] = 'file';
307
            $this->MOD_SETTINGS['reverse'] = 0;
308
        }
309
310
        // Finally add the help button doc header button to the module
311
        $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
312
        $cshButton = $buttonBar->makeHelpButton()
313
            ->setModuleName('xMOD_csh_corebe')
314
            ->setFieldName('filelist_module');
315
        $buttonBar->addButton($cshButton);
316
    }
317
318
    protected function initializeFileList(ServerRequestInterface $request): void
319
    {
320
        // Create the file list
321
        $this->filelist = GeneralUtility::makeInstance(FileList::class);
322
        $this->filelist->thumbs = ($GLOBALS['TYPO3_CONF_VARS']['GFX']['thumbnails'] ?? false) && ($this->MOD_SETTINGS['displayThumbs'] ?? false);
323
324
        // Create clipboard object and initialize it
325
        $CB = array_replace_recursive($request->getQueryParams()['CB'] ?? [], $request->getParsedBody()['CB'] ?? []);
326
        if ($this->cmd === 'setCB') {
327
            $CB['el'] = $this->filelist->clipObj->cleanUpCBC(array_merge(
328
                (array)($request->getParsedBody()['CBH'] ?? []),
329
                (array)($request->getParsedBody()['CBC'] ?? [])
330
            ), '_FILE');
331
        }
332
        if (!($this->MOD_SETTINGS['clipBoard'] ?? false)) {
333
            $CB['setP'] = 'normal';
334
        }
335
        $this->filelist->clipObj->setCmd($CB);
336
        $this->filelist->clipObj->cleanCurrent();
337
        $this->filelist->clipObj->endClipboard();
338
339
        // If the "cmd" was to delete files from the list, do that:
340
        if ($this->cmd === 'delete') {
341
            $items = $this->filelist->clipObj->cleanUpCBC(
342
                (array)($request->getParsedBody()['CBC'] ?? []),
343
                '_FILE',
344
                1
345
            );
346
            if (!empty($items)) {
347
                // Make command array:
348
                $FILE = [];
349
                foreach ($items as $clipboardIdentifier => $combinedIdentifier) {
350
                    $FILE['delete'][] = ['data' => $combinedIdentifier];
351
                    $this->filelist->clipObj->removeElement($clipboardIdentifier);
352
                }
353
                // Init file processing object for deleting and pass the cmd array.
354
                /** @var ExtendedFileUtility $fileProcessor */
355
                $fileProcessor = GeneralUtility::makeInstance(ExtendedFileUtility::class);
356
                $fileProcessor->setActionPermissions();
357
                $fileProcessor->setExistingFilesConflictMode($this->overwriteExistingFiles);
358
                $fileProcessor->start($FILE);
359
                $fileProcessor->processData();
360
                // Clean & Save clipboard state
361
                $this->filelist->clipObj->cleanCurrent();
362
                $this->filelist->clipObj->endClipboard();
363
            }
364
        }
365
366
        // Start up the file list by including processed settings.
367
        $this->filelist->start(
368
            $this->folderObject,
0 ignored issues
show
Bug introduced by
It seems like $this->folderObject can also be of type null; however, parameter $folderObject of TYPO3\CMS\Filelist\FileList::start() does only seem to accept TYPO3\CMS\Core\Resource\Folder, maybe add an additional type check? ( Ignorable by Annotation )

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

368
            /** @scrutinizer ignore-type */ $this->folderObject,
Loading history...
369
            MathUtility::forceIntegerInRange($this->pointer, 0, 100000),
370
            (string)($this->MOD_SETTINGS['sort'] ?? ''),
371
            (bool)($this->MOD_SETTINGS['reverse'] ?? false),
372
            (bool)($this->MOD_SETTINGS['clipBoard'] ?? false)
373
        );
374
    }
375
376
    protected function generateFileList(): void
377
    {
378
        $lang = $this->getLanguageService();
379
380
        // If a searchTerm is provided, create the searchDemand object
381
        $searchDemand = $this->searchTerm !== ''
382
            ? FileSearchDemand::createForSearchTerm($this->searchTerm)->withRecursive()
383
            : null;
384
385
        // Generate the list, if accessible
386
        if ($this->folderObject->getStorage()->isBrowsable()) {
387
            $this->view->assign('listHtml', $this->filelist->getTable($searchDemand));
388
            if ($this->filelist->totalItems === 0 && $searchDemand !== null) {
389
                // In case this is a search and no results were found, add a flash message
390
                // @todo This info should in the future also be displayed for folders without any file.
391
                //       Currently only an empty table is shown. This however has to be done in FileList directly.
392
                $this->addFlashMessage(
393
                    $lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang.xlf:flashmessage.no_results')
394
                );
395
            }
396
        } else {
397
            $this->addFlashMessage(
398
                $lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:storageNotBrowsableMessage'),
399
                $lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:storageNotBrowsableTitle')
400
            );
401
        }
402
    }
403
404
    protected function registerDrapUploader(): void
405
    {
406
        // Include DragUploader only if we have write access
407
        if ($this->folderObject->checkActionPermission('write')
408
            && $this->folderObject->getStorage()->checkUserActionPermission('add', 'File')
409
        ) {
410
            $lang = $this->getLanguageService();
411
            $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/DragUploader');
412
            $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/locallang_core.xlf', 'file_upload');
413
            $this->pageRenderer->addInlineLanguageLabelArray([
414
                'permissions.read' => $lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:read'),
415
                'permissions.write' => $lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:write'),
416
            ]);
417
            $this->view->assign('drapUploader', [
418
                'fileDenyPattern' => $GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'] ?? null,
419
                'maxFileSize' => GeneralUtility::getMaxUploadFileSize() * 1024,
420
                'defaultDuplicationBehaviourAction', $this->getDefaultDuplicationBehaviourAction(),
421
            ]);
422
        }
423
    }
424
425
    protected function registerFileListCheckboxes(): void
426
    {
427
        $lang = $this->getLanguageService();
428
        $userTsConfig = $this->getBackendUser()->getTSConfig();
429
        $addParams = '';
430
431
        if ($this->searchTerm) {
432
            $addParams .= '&searchTerm=' . htmlspecialchars($this->searchTerm);
433
        }
434
        if ($this->pointer) {
435
            $addParams .= '&pointer=' . $this->pointer;
436
        }
437
438
        $this->view->assign('displayThumbs', [
439
            'enabled' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['thumbnails'] && $userTsConfig['options.']['file_list.']['enableDisplayThumbnails'] === 'selectable',
440
            'label' => htmlspecialchars($lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:displayThumbs')),
441
            'html' => BackendUtility::getFuncCheck(
442
                $this->id,
443
                'SET[displayThumbs]',
444
                $this->MOD_SETTINGS['displayThumbs'] ?? '',
445
                '',
446
                $addParams,
447
                'id="checkDisplayThumbs"'
448
            )
449
        ]);
450
        $this->view->assign('enableClipBoard', [
451
            'enabled' => $userTsConfig['options.']['file_list.']['enableClipBoard'] === 'selectable',
452
            'label' => htmlspecialchars($lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:clipBoard')),
453
            'html' => BackendUtility::getFuncCheck(
454
                $this->id,
455
                'SET[clipBoard]',
456
                $this->MOD_SETTINGS['clipBoard'] ?? '',
457
                '',
458
                $addParams,
459
                'id="checkClipBoard"'
460
            ),
461
        ]);
462
    }
463
464
    /**
465
     * Create the panel of buttons for submitting the form or otherwise perform operations.
466
     */
467
    protected function registerAdditionalDocHeaderButtons(ServerRequestInterface $request): void
468
    {
469
        $lang = $this->getLanguageService();
470
        $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
471
472
        // Refresh
473
        $refreshButton = $buttonBar->makeLinkButton()
474
            ->setHref($request->getAttribute('normalizedParams')->getRequestUri())
475
            ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.reload'))
476
            ->setIcon($this->iconFactory->getIcon('actions-refresh', Icon::SIZE_SMALL));
477
        $buttonBar->addButton($refreshButton, ButtonBar::BUTTON_POSITION_RIGHT);
478
479
        // Level up
480
        try {
481
            $currentStorage = $this->folderObject->getStorage();
482
            $parentFolder = $this->folderObject->getParentFolder();
483
            if ($currentStorage->isWithinFileMountBoundaries($parentFolder)
484
                && $parentFolder->getIdentifier() !== $this->folderObject->getIdentifier()
485
            ) {
486
                $levelUpButton = $buttonBar->makeLinkButton()
487
                    ->setDataAttributes([
488
                        'tree-update-request' => htmlspecialchars('folder' . GeneralUtility::md5int($parentFolder->getCombinedIdentifier())),
489
                    ])
490
                    ->setHref(
491
                        (string)$this->uriBuilder->buildUriFromRoute(
492
                            'file_FilelistList',
493
                            ['id' => $parentFolder->getCombinedIdentifier()]
494
                        )
495
                    )
496
                    ->setShowLabelText(true)
497
                    ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.upOneLevel'))
498
                    ->setIcon($this->iconFactory->getIcon('actions-view-go-up', Icon::SIZE_SMALL));
499
                $buttonBar->addButton($levelUpButton, ButtonBar::BUTTON_POSITION_LEFT, 1);
500
            }
501
        } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
502
        }
503
504
        // Shortcut
505
        $shortCutButton = $buttonBar->makeShortcutButton()
506
            ->setRouteIdentifier('file_FilelistList')
507
            ->setDisplayName(sprintf(
508
                '%s: %s',
509
                $lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:mlang_tabs_tab'),
510
                $this->folderObject->getName() ?: $this->folderObject->getIdentifier()
511
            ))
512
            ->setArguments(array_filter([
513
                'id' => $this->id,
514
                'searchTerm' => $this->searchTerm
515
            ]));
516
        $buttonBar->addButton($shortCutButton, ButtonBar::BUTTON_POSITION_RIGHT);
517
518
        // Upload button (only if upload to this directory is allowed)
519
        if ($this->folderObject
520
            && $this->folderObject->checkActionPermission('write')
521
            && $this->folderObject->getStorage()->checkUserActionPermission('add', 'File')
522
        ) {
523
            $uploadButton = $buttonBar->makeLinkButton()
524
                ->setHref((string)$this->uriBuilder->buildUriFromRoute(
525
                    'file_upload',
526
                    [
527
                        'target' => $this->folderObject->getCombinedIdentifier(),
528
                        'returnUrl' => $this->filelist->listURL(),
529
                    ]
530
                ))
531
                ->setClasses('t3js-drag-uploader-trigger')
532
                ->setShowLabelText(true)
533
                ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.upload'))
534
                ->setIcon($this->iconFactory->getIcon('actions-edit-upload', Icon::SIZE_SMALL));
535
            $buttonBar->addButton($uploadButton, ButtonBar::BUTTON_POSITION_LEFT, 2);
536
        }
537
538
        // New folder button
539
        if ($this->folderObject && $this->folderObject->checkActionPermission('write')
540
            && ($this->folderObject->getStorage()->checkUserActionPermission(
541
                'add',
542
                'File'
543
            ) || $this->folderObject->checkActionPermission('add'))
544
        ) {
545
            $newButton = $buttonBar->makeLinkButton()
546
                ->setHref((string)$this->uriBuilder->buildUriFromRoute(
547
                    'file_newfolder',
548
                    [
549
                        'target' => $this->folderObject->getCombinedIdentifier(),
550
                        'returnUrl' => $this->filelist->listURL(),
551
                    ]
552
                ))
553
                ->setShowLabelText(true)
554
                ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.new'))
555
                ->setIcon($this->iconFactory->getIcon('actions-add', Icon::SIZE_SMALL));
556
            $buttonBar->addButton($newButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
557
        }
558
559
        // Add paste button if clipboard is initialized
560
        if ($this->filelist->clipObj instanceof Clipboard && $this->folderObject->checkActionPermission('write')) {
561
            $elFromTable = $this->filelist->clipObj->elFromTable('_FILE');
562
            if (!empty($elFromTable)) {
563
                $addPasteButton = true;
564
                $elToConfirm = [];
565
                foreach ($elFromTable as $key => $element) {
566
                    $clipBoardElement = $this->resourceFactory->retrieveFileOrFolderObject($element);
567
                    if ($clipBoardElement instanceof Folder && $clipBoardElement->getStorage()->isWithinFolder(
568
                        $clipBoardElement,
569
                        $this->folderObject
0 ignored issues
show
Bug introduced by
It seems like $this->folderObject can also be of type null; however, parameter $resource of TYPO3\CMS\Core\Resource\...orage::isWithinFolder() does only seem to accept TYPO3\CMS\Core\Resource\ResourceInterface, maybe add an additional type check? ( Ignorable by Annotation )

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

569
                        /** @scrutinizer ignore-type */ $this->folderObject
Loading history...
570
                    )
571
                    ) {
572
                        $addPasteButton = false;
573
                    }
574
                    $elToConfirm[$key] = $clipBoardElement->getName();
575
                }
576
                if ($addPasteButton) {
577
                    $confirmText = $this->filelist->clipObj
578
                        ->confirmMsgText('_FILE', $this->folderObject->getReadablePath(), 'into', $elToConfirm);
579
                    $pastButtonTitle = $lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:clip_paste');
580
                    $pasteButton = $buttonBar->makeLinkButton()
581
                        ->setHref($this->filelist->clipObj
582
                            ->pasteUrl('_FILE', $this->folderObject->getCombinedIdentifier()))
583
                        ->setClasses('t3js-modal-trigger')
584
                        ->setDataAttributes([
585
                            'severity' => 'warning',
586
                            'bs-content' => $confirmText,
587
                            'title' => $pastButtonTitle
588
                        ])
589
                        ->setShowLabelText(true)
590
                        ->setTitle($pastButtonTitle)
591
                        ->setIcon($this->iconFactory->getIcon('actions-document-paste-into', Icon::SIZE_SMALL));
592
                    $buttonBar->addButton($pasteButton, ButtonBar::BUTTON_POSITION_LEFT, 4);
593
                }
594
            }
595
        }
596
    }
597
598
    /**
599
     * Get main headline based on active folder or storage for backend module
600
     * Folder names are resolved to their special names like done in the tree view.
601
     *
602
     * @return string
603
     */
604
    protected function getModuleHeadline(): string
605
    {
606
        $name = $this->folderObject->getName();
607
        if ($name === '') {
608
            // Show storage name on storage root
609
            if ($this->folderObject->getIdentifier() === '/') {
610
                $name = $this->folderObject->getStorage()->getName();
611
            }
612
        } else {
613
            $name = key(ListUtility::resolveSpecialFolderNames(
614
                [$name => $this->folderObject]
615
            ));
616
        }
617
        return (string)$name;
618
    }
619
620
    /**
621
     * Return the default duplication behaviour action, set in TSconfig
622
     *
623
     * @return string
624
     */
625
    protected function getDefaultDuplicationBehaviourAction(): string
626
    {
627
        $defaultAction = $this->getBackendUser()->getTSConfig()
628
            ['options.']['file_list.']['uploader.']['defaultAction'] ?? '';
629
630
        if ($defaultAction === '') {
631
            return DuplicationBehavior::CANCEL;
632
        }
633
634
        if (!in_array($defaultAction, [
635
            DuplicationBehavior::REPLACE,
636
            DuplicationBehavior::RENAME,
637
            DuplicationBehavior::CANCEL
638
        ], true)) {
639
            $this->logger->warning('TSConfig: options.file_list.uploader.defaultAction contains an invalid value ("{value}"), fallback to default value: "{default}"', [
0 ignored issues
show
Bug introduced by
The method warning() does not exist on null. ( Ignorable by Annotation )

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

639
            $this->logger->/** @scrutinizer ignore-call */ 
640
                           warning('TSConfig: options.file_list.uploader.defaultAction contains an invalid value ("{value}"), fallback to default value: "{default}"', [

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...
640
                'value' => $defaultAction,
641
                'default' => DuplicationBehavior::CANCEL
642
            ]);
643
            $defaultAction = DuplicationBehavior::CANCEL;
644
        }
645
        return $defaultAction;
646
    }
647
648
    /**
649
     * Generate a response by either the given $html or by rendering the module content.
650
     *
651
     * @param string $html
652
     * @return ResponseInterface
653
     */
654
    protected function htmlResponse(string $html): ResponseInterface
655
    {
656
        $response = $this->responseFactory
657
            ->createResponse()
658
            ->withHeader('Content-Type', 'text/html; charset=utf-8');
659
660
        $response->getBody()->write($html);
661
        return $response;
662
    }
663
664
    /**
665
     * Adds a flash message to the default flash message queue
666
     *
667
     * @param string $message
668
     * @param string $title
669
     * @param int $severity
670
     */
671
    protected function addFlashMessage(string $message, string $title = '', int $severity = FlashMessage::INFO): void
672
    {
673
        $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $message, $title, $severity, true);
674
        $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
675
        $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
676
        $defaultFlashMessageQueue->enqueue($flashMessage);
677
    }
678
679
    /**
680
     * Returns an instance of LanguageService
681
     *
682
     * @return LanguageService
683
     */
684
    protected function getLanguageService(): LanguageService
685
    {
686
        return $GLOBALS['LANG'];
687
    }
688
689
    /**
690
     * Returns the current BE user.
691
     *
692
     * @return BackendUserAuthentication
693
     */
694
    protected function getBackendUser(): BackendUserAuthentication
695
    {
696
        return $GLOBALS['BE_USER'];
697
    }
698
}
699