Passed
Push — master ( 14d509...cdc6b7 )
by
unknown
15:15
created

FileListController::getBackendUser()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
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 ?Folder $folderObject = null;
68
    protected ?DuplicationBehavior $overwriteExistingFiles = null;
69
70
    protected UriBuilder $uriBuilder;
71
    protected PageRenderer $pageRenderer;
72
    protected IconFactory $iconFactory;
73
    protected ResourceFactory $resourceFactory;
74
    protected ModuleTemplateFactory $moduleTemplateFactory;
75
    protected ResponseFactoryInterface $responseFactory;
76
77
    protected ?ModuleTemplate $moduleTemplate = null;
78
    protected ?ViewInterface $view = null;
79
    protected ?FileList $filelist = null;
80
81
    public function __construct(
82
        UriBuilder $uriBuilder,
83
        PageRenderer $pageRenderer,
84
        IconFactory $iconFactory,
85
        ResourceFactory $resourceFactory,
86
        ModuleTemplateFactory $moduleTemplateFactory,
87
        ResponseFactoryInterface $responseFactory
88
    ) {
89
        $this->uriBuilder = $uriBuilder;
90
        $this->pageRenderer = $pageRenderer;
91
        $this->iconFactory = $iconFactory;
92
        $this->resourceFactory = $resourceFactory;
93
        $this->moduleTemplateFactory = $moduleTemplateFactory;
94
        $this->responseFactory = $responseFactory;
95
    }
96
97
    public function handleRequest(ServerRequestInterface $request): ResponseInterface
98
    {
99
        $lang = $this->getLanguageService();
100
        $backendUser = $this->getBackendUser();
101
102
        $this->moduleTemplate = $this->moduleTemplateFactory->create($request);
103
        $this->moduleTemplate->setTitle($lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:mlang_tabs_tab'));
104
105
        $queryParams = $request->getQueryParams();
106
        $parsedBody = $request->getParsedBody();
107
108
        $this->id = (string)($parsedBody['id'] ?? $queryParams['id'] ?? '');
109
        $this->cmd = (string)($parsedBody['cmd'] ?? $queryParams['cmd'] ?? '');
110
        $this->searchTerm = (string)($parsedBody['searchTerm'] ?? $queryParams['searchTerm'] ?? '');
111
        $this->overwriteExistingFiles = DuplicationBehavior::cast(
112
            $parsedBody['overwriteExistingFiles'] ?? $queryParams['overwriteExistingFiles'] ?? null
113
        );
114
115
        try {
116
            if ($this->id !== '') {
117
                $backendUser->evaluateUserSpecificFileFilterSettings();
118
                $storage = GeneralUtility::makeInstance(StorageRepository::class)->findByCombinedIdentifier($this->id);
119
                if ($storage !== null) {
120
                    $identifier = substr($this->id, strpos($this->id, ':') + 1);
121
                    if (!$storage->hasFolder($identifier)) {
122
                        $identifier = $storage->getFolderIdentifierFromFileIdentifier($identifier);
123
                    }
124
                    $this->folderObject = $storage->getFolder($identifier);
125
                    // Disallow access to fallback storage 0
126
                    if ($storage->getUid() === 0) {
127
                        throw new InsufficientFolderAccessPermissionsException(
128
                            'You are not allowed to access files outside your storages',
129
                            1434539815
130
                        );
131
                    }
132
                    // Disallow the rendering of the processing folder (e.g. could be called manually)
133
                    if ($this->folderObject && $storage->isProcessingFolder($this->folderObject)) {
134
                        $this->folderObject = $storage->getRootLevelFolder();
135
                    }
136
                }
137
            } else {
138
                // Take the first object of the first storage
139
                $fileStorages = $backendUser->getFileStorages();
140
                $fileStorage = reset($fileStorages);
141
                if ($fileStorage) {
142
                    $this->folderObject = $fileStorage->getRootLevelFolder();
143
                } else {
144
                    throw new \RuntimeException('Could not find any folder to be displayed.', 1349276894);
145
                }
146
            }
147
148
            if ($this->folderObject && !$this->folderObject->getStorage()->isWithinFileMountBoundaries($this->folderObject)) {
149
                throw new \RuntimeException('Folder not accessible.', 1430409089);
150
            }
151
        } catch (InsufficientFolderAccessPermissionsException $permissionException) {
152
            $this->folderObject = null;
153
            $this->addFlashMessage(
154
                sprintf($lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:missingFolderPermissionsMessage'), $this->id),
155
                $lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:missingFolderPermissionsTitle'),
156
                FlashMessage::ERROR
157
            );
158
        } catch (Exception $fileException) {
159
            $this->folderObject = null;
160
            // Take the first object of the first storage
161
            $fileStorages = $backendUser->getFileStorages();
162
            $fileStorage = reset($fileStorages);
163
            if ($fileStorage instanceof ResourceStorage) {
164
                $this->folderObject = $fileStorage->getRootLevelFolder();
165
                if (!$fileStorage->isWithinFileMountBoundaries($this->folderObject)) {
166
                    $this->folderObject = null;
167
                }
168
            }
169
            if (!$this->folderObject) {
170
                $this->addFlashMessage(
171
                    sprintf($lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:folderNotFoundMessage'), $this->id),
172
                    $lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:folderNotFoundTitle'),
173
                    FlashMessage::ERROR
174
                );
175
            }
176
        } catch (\RuntimeException $e) {
177
            $this->folderObject = null;
178
            $this->addFlashMessage(
179
                $e->getMessage() . ' (' . $e->getCode() . ')',
180
                $lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:folderNotFoundTitle'),
181
                FlashMessage::ERROR
182
            );
183
        }
184
185
        if ($this->folderObject
186
            && !$this->folderObject->getStorage()->checkFolderActionPermission('read', $this->folderObject)
187
        ) {
188
            $this->folderObject = null;
189
        }
190
191
        $this->initializeView();
192
        $this->initializeModule($request);
193
194
        // In case the folderObject is NULL, the request is either invalid or the user
195
        // does not have necessary permissions. Just render and return the "empty" view.
196
        if ($this->folderObject === null) {
197
            return $this->htmlResponse(
198
                $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

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

232
            '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...
233
            'searchTerm' => $this->searchTerm,
234
        ]);
235
236
        // Overwrite the default module title, adding the specific module headline (the folder name)
237
        $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

237
        $this->moduleTemplate->/** @scrutinizer ignore-call */ 
238
                               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...
238
            $lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:mlang_tabs_tab'),
239
            $this->getModuleHeadline()
240
        );
241
242
        // Additional doc header information: current path and folder info
243
        $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation([
244
            '_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

244
            '_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...
245
            'combined_identifier' => $this->folderObject->getCombinedIdentifier(),
246
        ]);
247
248
        // Render view and return the response
249
        return $this->htmlResponse(
250
            $this->moduleTemplate->setContent($this->view->render())->renderContent()
251
        );
252
    }
253
254
    protected function initializeView(): void
255
    {
256
        $this->view = GeneralUtility::makeInstance(StandaloneView::class);
257
        $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

257
        $this->view->/** @scrutinizer ignore-call */ 
258
                     setTemplateRootPaths(['EXT:filelist/Resources/Private/Templates/FileList']);
Loading history...
258
        $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

258
        $this->view->/** @scrutinizer ignore-call */ 
259
                     setPartialRootPaths(['EXT:filelist/Resources/Private/Partials']);
Loading history...
259
        $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

259
        $this->view->/** @scrutinizer ignore-call */ 
260
                     setLayoutRootPaths(['EXT:filelist/Resources/Private/Layouts']);
Loading history...
260
        $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

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

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

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

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