Passed
Push — master ( d9c103...de74be )
by
unknown
13:15
created

BackendController::getStartupModule()   B

Complexity

Conditions 11
Paths 30

Size

Total Lines 46
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 30
dl 0
loc 46
rs 7.3166
c 0
b 0
f 0
cc 11
nc 30
nop 1

How to fix   Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Backend\Controller;
17
18
use Psr\Http\Message\ResponseInterface;
19
use Psr\Http\Message\ServerRequestInterface;
20
use TYPO3\CMS\Backend\Domain\Repository\Module\BackendModuleRepository;
21
use TYPO3\CMS\Backend\Module\ModuleLoader;
22
use TYPO3\CMS\Backend\Routing\Router;
23
use TYPO3\CMS\Backend\Routing\UriBuilder;
24
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
25
use TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface;
26
use TYPO3\CMS\Backend\Utility\BackendUtility;
27
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
28
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
29
use TYPO3\CMS\Core\Http\HtmlResponse;
30
use TYPO3\CMS\Core\Http\JsonResponse;
31
use TYPO3\CMS\Core\Imaging\IconFactory;
32
use TYPO3\CMS\Core\Information\Typo3Version;
33
use TYPO3\CMS\Core\Localization\LanguageService;
34
use TYPO3\CMS\Core\Page\PageRenderer;
35
use TYPO3\CMS\Core\Type\Bitmask\Permission;
36
use TYPO3\CMS\Core\Type\File\ImageInfo;
37
use TYPO3\CMS\Core\Utility\GeneralUtility;
38
use TYPO3\CMS\Core\Utility\MathUtility;
39
use TYPO3\CMS\Core\Utility\PathUtility;
40
use TYPO3\CMS\Fluid\View\StandaloneView;
41
42
/**
43
 * Class for rendering the TYPO3 backend
44
 */
45
class BackendController
46
{
47
    /**
48
     * @var string
49
     */
50
    protected $css = '';
51
52
    /**
53
     * @var array
54
     */
55
    protected $toolbarItems = [];
56
57
    /**
58
     * @var string
59
     */
60
    protected $templatePath = 'EXT:backend/Resources/Private/Templates/';
61
62
    /**
63
     * @var string
64
     */
65
    protected $partialPath = 'EXT:backend/Resources/Private/Partials/';
66
67
    protected BackendModuleRepository $backendModuleRepository;
68
    protected PageRenderer $pageRenderer;
69
    protected IconFactory $iconFactory;
70
    protected Typo3Version $typo3Version;
71
    protected UriBuilder $uriBuilder;
72
    protected ModuleLoader $moduleLoader;
73
    protected ModuleTemplateFactory $moduleTemplateFactory;
74
75
    /**
76
     * @var \SplObjectStorage
77
     */
78
    protected $moduleStorage;
79
80
    public function __construct(
81
        Typo3Version $typo3Version,
82
        IconFactory $iconFactory,
83
        UriBuilder $uriBuilder,
84
        PageRenderer $pageRenderer,
85
        ModuleLoader $moduleLoader,
86
        BackendModuleRepository $backendModuleRepository,
87
        ModuleTemplateFactory $moduleTemplateFactory
88
    ) {
89
        $this->getLanguageService()->includeLLFile('EXT:core/Resources/Private/Language/locallang_misc.xlf');
90
        $this->backendModuleRepository = $backendModuleRepository;
91
        $this->iconFactory = $iconFactory;
92
        $this->uriBuilder = $uriBuilder;
93
        $this->typo3Version = $typo3Version;
94
        $this->pageRenderer = $pageRenderer;
95
        $this->moduleLoader = $moduleLoader;
96
        $this->moduleLoader->observeWorkspaces = true;
97
        $this->moduleLoader->load($GLOBALS['TBE_MODULES']);
98
        $this->moduleTemplateFactory = $moduleTemplateFactory;
99
100
        // Add default BE javascript
101
        $this->pageRenderer->addJsFile('EXT:backend/Resources/Public/JavaScript/backend.js');
102
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LoginRefresh', 'function(LoginRefresh) {
103
			LoginRefresh.setIntervalTime(' . MathUtility::forceIntegerInRange((int)$GLOBALS['TYPO3_CONF_VARS']['BE']['sessionTimeout'] - 60, 60) . ');
104
			LoginRefresh.setLoginFramesetUrl(' . GeneralUtility::quoteJSvalue((string)$this->uriBuilder->buildUriFromRoute('login_frameset')) . ');
105
			LoginRefresh.setLogoutUrl(' . GeneralUtility::quoteJSvalue((string)$this->uriBuilder->buildUriFromRoute('logout')) . ');
106
			LoginRefresh.initialize();
107
		}');
108
109
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/BroadcastService', 'function(service) { service.listen(); }');
110
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Module/Router');
111
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ModuleMenu');
112
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Toolbar');
113
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Notification');
114
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Modal');
115
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/InfoWindow');
116
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Viewport/ResizableNavigation');
117
118
        // load the storage API and fill the UC into the PersistentStorage, so no additional AJAX call is needed
119
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Storage/Persistent', 'function(PersistentStorage) {
120
            PersistentStorage.load(' . json_encode($this->getBackendUser()->uc) . ');
121
        }');
122
123
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/DebugConsole');
124
125
        $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/locallang_core.xlf');
126
        $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/locallang_misc.xlf');
127
        $this->pageRenderer->addInlineLanguageLabelFile('EXT:backend/Resources/Private/Language/locallang_layout.xlf');
128
        $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf');
129
        $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/debugger.xlf');
130
        $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/wizard.xlf');
131
132
        $this->pageRenderer->addInlineSetting('ContextHelp', 'moduleUrl', (string)$this->uriBuilder->buildUriFromRoute('help_cshmanual'));
133
        $this->pageRenderer->addInlineSetting('ShowItem', 'moduleUrl', (string)$this->uriBuilder->buildUriFromRoute('show_item'));
134
        $this->pageRenderer->addInlineSetting('RecordHistory', 'moduleUrl', (string)$this->uriBuilder->buildUriFromRoute('record_history'));
135
        $this->pageRenderer->addInlineSetting('NewRecord', 'moduleUrl', (string)$this->uriBuilder->buildUriFromRoute('db_new'));
136
        $this->pageRenderer->addInlineSetting('FormEngine', 'moduleUrl', (string)$this->uriBuilder->buildUriFromRoute('record_edit'));
137
        $this->pageRenderer->addInlineSetting('RecordCommit', 'moduleUrl', (string)$this->uriBuilder->buildUriFromRoute('tce_db'));
138
        $this->pageRenderer->addInlineSetting('FileCommit', 'moduleUrl', (string)$this->uriBuilder->buildUriFromRoute('tce_file'));
139
        $this->pageRenderer->addInlineSetting('WebLayout', 'moduleUrl', (string)$this->uriBuilder->buildUriFromRoute('web_layout'));
140
141
        $this->initializeToolbarItems();
142
        $this->executeHook('constructPostProcess');
143
144
        $this->moduleStorage = $this->backendModuleRepository->loadAllowedModules(['user', 'help']);
145
    }
146
147
    /**
148
     * Initialize toolbar item objects
149
     *
150
     * @throws \RuntimeException
151
     */
152
    protected function initializeToolbarItems()
153
    {
154
        $toolbarItemInstances = [];
155
        foreach ($GLOBALS['TYPO3_CONF_VARS']['BE']['toolbarItems'] ?? [] as $className) {
156
            $toolbarItemInstance = GeneralUtility::makeInstance($className);
157
            if (!$toolbarItemInstance instanceof ToolbarItemInterface) {
158
                throw new \RuntimeException(
159
                    'class ' . $className . ' is registered as toolbar item but does not implement'
160
                        . ToolbarItemInterface::class,
161
                    1415958218
162
                );
163
            }
164
            $index = (int)$toolbarItemInstance->getIndex();
165
            if ($index < 0 || $index > 100) {
166
                throw new \RuntimeException(
167
                    'getIndex() must return an integer between 0 and 100',
168
                    1415968498
169
                );
170
            }
171
            // Find next free position in array
172
            while (array_key_exists($index, $toolbarItemInstances)) {
173
                $index++;
174
            }
175
            $toolbarItemInstances[$index] = $toolbarItemInstance;
176
        }
177
        ksort($toolbarItemInstances);
178
        $this->toolbarItems = $toolbarItemInstances;
179
    }
180
181
    /**
182
     * Main function generating the BE scaffolding
183
     *
184
     * @param ServerRequestInterface $request
185
     * @return ResponseInterface the response with the content
186
     */
187
    public function mainAction(ServerRequestInterface $request): ResponseInterface
188
    {
189
        $this->executeHook('renderPreProcess');
190
191
        $moduleMenuCollapsed = $this->getCollapseStateOfMenu();
192
        $hasModules = count($this->moduleStorage) > 0;
193
        $bodyTag = '<body class="scaffold t3js-scaffold' . (!$moduleMenuCollapsed && $hasModules ? ' scaffold-modulemenu-expanded' : '') . '">';
194
195
        // Prepare the scaffolding, at this point extension may still add javascript and css
196
        $moduleTemplate = $this->moduleTemplateFactory->create($request);
197
        $view = $moduleTemplate->getView();
198
        $view->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Partials')]);
199
        $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($this->templatePath . 'Backend/Main.html'));
200
        $moduleTemplate->setBodyTag($bodyTag);
201
        $view->assign('moduleMenu', $this->generateModuleMenu());
202
        $view->assign('topbar', $this->renderTopbar());
203
        $view->assign('hasModules', $hasModules);
204
        $view->assign('startupModule', $this->getStartupModule($request));
205
        $view->assign('stateTracker', (string)$this->uriBuilder->buildUriFromRoute('state-tracker'));
206
207
        if (!empty($this->css)) {
208
            $this->pageRenderer->addCssInlineBlock('BackendInlineCSS', $this->css);
209
        }
210
        $this->generateJavascript($request);
211
212
        // Set document title
213
        $typo3Version = 'TYPO3 CMS ' . $this->typo3Version->getVersion();
214
        $title = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . ' [' . $typo3Version . ']' : $typo3Version;
215
        $moduleTemplate->setTitle($title);
216
217
        // Renders the backend scaffolding
218
        $content = $moduleTemplate->renderContent();
219
        $this->executeHook('renderPostProcess', ['content' => &$content]);
220
        return new HtmlResponse($content);
221
    }
222
223
    /**
224
     * Renders the topbar, containing the backend logo, sitename etc.
225
     *
226
     * @return string
227
     */
228
    protected function renderTopbar()
229
    {
230
        $view = $this->getFluidTemplateObject($this->partialPath . 'Backend/Topbar.html');
231
232
        // Extension Configuration to find the TYPO3 logo in the left corner
233
        $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('backend');
234
        $logoPath = '';
235
        if (!empty($extConf['backendLogo'])) {
236
            $customBackendLogo = GeneralUtility::getFileAbsFileName(ltrim($extConf['backendLogo'], '/'));
237
            if (!empty($customBackendLogo)) {
238
                $logoPath = $customBackendLogo;
239
            }
240
        }
241
        // if no custom logo was set or the path is invalid, use the original one
242
        if (empty($logoPath) || !file_exists($logoPath)) {
243
            $logoPath = GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Public/Images/typo3_logo_orange.svg');
244
            $logoWidth = 22;
245
            $logoHeight = 22;
246
        } else {
247
            // set width/height for custom logo
248
            $imageInfo = GeneralUtility::makeInstance(ImageInfo::class, $logoPath);
249
            $logoWidth = $imageInfo->getWidth() ?? '22';
250
            $logoHeight = $imageInfo->getHeight() ?? '22';
251
252
            // High-resolution?
253
            if (strpos($logoPath, '@2x.') !== false) {
254
                $logoWidth /= 2;
255
                $logoHeight /= 2;
256
            }
257
        }
258
259
        $view->assign('hasModules', count($this->moduleStorage) > 0);
260
        $view->assign('logoUrl', PathUtility::getAbsoluteWebPath($logoPath));
261
        $view->assign('logoWidth', $logoWidth);
262
        $view->assign('logoHeight', $logoHeight);
263
        $view->assign('applicationVersion', $this->typo3Version->getVersion());
264
        $view->assign('siteName', $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']);
265
        $view->assign('toolbar', $this->renderToolbar());
266
267
        return $view->render();
268
    }
269
270
    /**
271
     * Renders the items in the top toolbar
272
     *
273
     * @return string top toolbar elements as HTML
274
     */
275
    protected function renderToolbar()
276
    {
277
        $toolbar = [];
278
        foreach ($this->toolbarItems as $toolbarItem) {
279
            /** @var ToolbarItemInterface $toolbarItem */
280
            if ($toolbarItem->checkAccess()) {
281
                $hasDropDown = (bool)$toolbarItem->hasDropDown();
282
                $additionalAttributes = (array)$toolbarItem->getAdditionalAttributes();
283
284
                $liAttributes = [];
285
286
                // Merge class: Add dropdown class if hasDropDown, add classes from additional attributes
287
                $classes = [];
288
                $classes[] = 'toolbar-item';
289
                $classes[] = 't3js-toolbar-item';
290
                if (isset($additionalAttributes['class'])) {
291
                    $classes[] = $additionalAttributes['class'];
292
                    unset($additionalAttributes['class']);
293
                }
294
                $liAttributes['class'] = implode(' ', $classes);
295
296
                // Add further attributes
297
                foreach ($additionalAttributes as $name => $value) {
298
                    $liAttributes[(string)$name] = (string)$value;
299
                }
300
301
                // Create a unique id from class name
302
                $fullyQualifiedClassName = \get_class($toolbarItem);
303
                $className = GeneralUtility::underscoredToLowerCamelCase($fullyQualifiedClassName);
304
                $className = GeneralUtility::camelCaseToLowerCaseUnderscored($className);
305
                $className = str_replace(['_', '\\'], '-', $className);
306
                $liAttributes['id'] = $className;
307
308
                // Create data attribute identifier
309
                $shortName = substr($fullyQualifiedClassName, (int)strrpos($fullyQualifiedClassName, '\\') + 1);
310
                $dataToolbarIdentifier = GeneralUtility::camelCaseToLowerCaseUnderscored($shortName);
311
                $dataToolbarIdentifier = str_replace('_', '-', $dataToolbarIdentifier);
312
                $liAttributes['data-toolbar-identifier'] = $dataToolbarIdentifier;
313
314
                $toolbar[] = '<li ' . GeneralUtility::implodeAttributes($liAttributes, true) . '>';
315
316
                if ($hasDropDown) {
317
                    $toolbar[] = '<a href="#" class="toolbar-item-link dropdown-toggle" data-bs-toggle="dropdown" data-bs-offset="0,-2">';
318
                    $toolbar[] = $toolbarItem->getItem();
319
                    $toolbar[] = '</a>';
320
                    $toolbar[] = '<div class="dropdown-menu" role="menu">';
321
                    $toolbar[] = $toolbarItem->getDropDown();
322
                    $toolbar[] = '</div>';
323
                } else {
324
                    $toolbar[] = $toolbarItem->getItem();
325
                }
326
                $toolbar[] = '</li>';
327
            }
328
        }
329
        return implode(LF, $toolbar);
330
    }
331
332
    /**
333
     * Generates the JavaScript code for the backend.
334
     *
335
     * @param ServerRequestInterface $request
336
     */
337
    protected function generateJavascript(ServerRequestInterface $request)
338
    {
339
        $beUser = $this->getBackendUser();
340
        // Needed for FormEngine manipulation (date picker)
341
        $dateFormat = ($GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat'] ? ['MM-DD-Y', 'HH:mm MM-DD-Y'] : ['DD-MM-Y', 'HH:mm DD-MM-Y']);
342
        $this->pageRenderer->addInlineSetting('DateTimePicker', 'DateFormat', $dateFormat);
343
344
        // If another page module was specified, replace the default Page module with the new one
345
        $newPageModule = trim($beUser->getTSConfig()['options.']['overridePageModule'] ?? '');
346
        $pageModule = BackendUtility::isModuleSetInTBE_MODULES($newPageModule) ? $newPageModule : 'web_layout';
347
        $pageModuleUrl = '';
348
        if (!$beUser->check('modules', $pageModule)) {
349
            $pageModule = '';
350
        } else {
351
            $pageModuleUrl = (string)$this->uriBuilder->buildUriFromRoute($pageModule);
352
        }
353
        $t3Configuration = [
354
            'username' => htmlspecialchars($beUser->user['username']),
355
            'pageModule' => $pageModule,
356
            'pageModuleUrl' => $pageModuleUrl,
357
            'inWorkspace' => $beUser->workspace !== 0,
358
            'showRefreshLoginPopup' => (bool)($GLOBALS['TYPO3_CONF_VARS']['BE']['showRefreshLoginPopup'] ?? false)
359
        ];
360
361
        $this->pageRenderer->addJsInlineCode(
362
            'BackendConfiguration',
363
            '
364
        TYPO3.configuration = ' . json_encode($t3Configuration) . ';
365
        /**
366
         * Frameset Module object
367
         *
368
         * Used in main modules with a frameset for submodules to keep the ID between modules
369
         * Typically that is set by something like this in a Web>* sub module:
370
         *		if (top.fsMod) top.fsMod.recentIds["web"] = "\'.(int)$this->id.\'";
371
         * 		if (top.fsMod) top.fsMod.recentIds["file"] = "...(file reference/string)...";
372
         */
373
        var fsMod = {
374
            recentIds: [],					// used by frameset modules to track the most recent used id for list frame.
375
            navFrameHighlightedID: [],		// used by navigation frames to track which row id was highlighted last time
376
            currentBank: "0"
377
        };
378
379
        top.goToModule = function(modName, addGetVars) {
380
            TYPO3.ModuleMenu.App.showModule(modName, addGetVars);
381
        }
382
        ' . $this->handlePageEditing($request),
383
            false
384
        );
385
    }
386
387
    /**
388
     * Checking if the "&edit" variable was sent so we can open it for editing the page.
389
     */
390
    protected function handlePageEditing(ServerRequestInterface $request): string
391
    {
392
        $beUser = $this->getBackendUser();
393
        $userTsConfig = $this->getBackendUser()->getTSConfig();
394
        // EDIT page
395
        $editId = preg_replace('/[^[:alnum:]_]/', '', $request->getQueryParams()['edit'] ?? '') ?? '';
396
        if ($editId) {
397
            // Looking up the page to edit, checking permissions:
398
            $where = ' AND (' . $beUser->getPagePermsClause(Permission::PAGE_EDIT) . ' OR ' . $beUser->getPagePermsClause(Permission::CONTENT_EDIT) . ')';
399
            $editRecord = null;
400
            if (MathUtility::canBeInterpretedAsInteger($editId)) {
401
                $editRecord = BackendUtility::getRecordWSOL('pages', (int)$editId, '*', $where);
402
            }
403
            // If the page was accessible, then let the user edit it.
404
            if (is_array($editRecord) && $beUser->isInWebMount($editRecord)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $beUser->isInWebMount($editRecord) of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
405
                // Checking page edit parameter:
406
                if (!($userTsConfig['options.']['bookmark_onEditId_dontSetPageTree'] ?? false)) {
407
                    $bookmarkKeepExpanded = (bool)($userTsConfig['options.']['bookmark_onEditId_keepExistingExpanded'] ?? false);
408
                    // Expanding page tree:
409
                    BackendUtility::openPageTree((int)$editRecord['pid'], !$bookmarkKeepExpanded);
410
                }
411
                // Setting JS code to open editing:
412
                return '
413
		// Load page to edit:
414
	window.setTimeout("top.loadEditId(' . (int)$editRecord['uid'] . ');", 500);
415
			';
416
            }
417
            return '
418
            // Warning about page editing:
419
            require(["TYPO3/CMS/Backend/Modal", "TYPO3/CMS/Backend/Severity"], function(Modal, Severity) {
420
                Modal.show("", ' . GeneralUtility::quoteJSvalue(sprintf($this->getLanguageService()->getLL('noEditPage'), (string)$editId)) . ', Severity.notice, [{
421
                    text: ' . GeneralUtility::quoteJSvalue($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:close')) . ',
422
                    active: true,
423
                    btnClass: "btn-info",
424
                    name: "cancel",
425
                    trigger: function () {
426
                        Modal.currentModal.trigger("modal-dismiss");
427
                    }
428
                }])
429
            });';
430
        }
431
        return '';
432
    }
433
434
    /**
435
     * Sets the startup module from either GETvars module and modParams or user configuration.
436
     *
437
     * @param ServerRequestInterface $request
438
     * @return array
439
     */
440
    protected function getStartupModule(ServerRequestInterface $request): array
441
    {
442
        $startModule = null;
443
        $redirectRoute = $request->getQueryParams()['redirect'] ?? '';
444
        // Check if the route has been registered
445
        if ($redirectRoute !== '' && $redirectRoute !== 'main' && isset(GeneralUtility::makeInstance(Router::class)->getRoutes()[$redirectRoute])) {
446
            $startModule = $redirectRoute;
447
            $moduleParameters = $request->getQueryParams()['redirectParams'] ?? '';
448
            $moduleParameters = rawurldecode($moduleParameters);
449
        } else {
450
            $startModule = preg_replace('/[^[:alnum:]_]/', '', $request->getQueryParams()['module'] ?? '');
451
            $startModuleParameters = '';
452
            if (!$startModule) {
453
                $beUser = $this->getBackendUser();
454
                // start module on first login, will be removed once used the first time
455
                if (isset($beUser->uc['startModuleOnFirstLogin'])) {
456
                    $startModule = $beUser->uc['startModuleOnFirstLogin'];
457
                    unset($beUser->uc['startModuleOnFirstLogin']);
458
                    $beUser->writeUC();
459
                } elseif ($this->moduleLoader->checkMod($beUser->uc['startModule']) !== 'notFound') {
460
                    $startModule = $beUser->uc['startModule'];
461
                } else {
462
                    $startModule = $this->determineFirstAvailableBackendModule();
463
                }
464
465
                // check if the start module has additional parameters, so a redirect to a specific
466
                // action is possible
467
                if (strpos($startModule, '->') !== false) {
468
                    [$startModule, $startModuleParameters] = explode('->', $startModule, 2);
469
                }
470
            }
471
472
            $moduleParameters = $request->getQueryParams()['modParams'] ?? '';
473
            // if no GET parameters are set, check if there are parameters given from the UC
474
            if (!$moduleParameters && $startModuleParameters) {
475
                $moduleParameters = $startModuleParameters;
476
            }
477
        }
478
479
        if ($startModule) {
480
            $parameters = [];
481
            parse_str($moduleParameters, $parameters);
482
            $deepLink = $this->uriBuilder->buildUriFromRoute($startModule, $parameters);
483
            return [$startModule, (string)$deepLink];
484
        }
485
        return [null, null];
486
    }
487
488
    protected function determineFirstAvailableBackendModule(): string
489
    {
490
        foreach ($this->moduleLoader->modules as $mainMod => $modData) {
491
            $hasSubmodules = !empty($modData['sub']) && is_array($modData['sub']);
492
            $isStandalone = $modData['standalone'] ?? false;
493
            if ($isStandalone) {
494
                return $modData['name'];
495
            }
496
497
            if ($hasSubmodules) {
498
                $firstSubmodule = reset($modData['sub']);
499
                return $firstSubmodule['name'];
500
            }
501
        }
502
503
        return '';
504
    }
505
506
    /**
507
     * Adds a css snippet to the backend
508
     *
509
     * @param string $css Css snippet
510
     * @throws \InvalidArgumentException
511
     */
512
    public function addCss($css)
513
    {
514
        if (!is_string($css)) {
0 ignored issues
show
introduced by
The condition is_string($css) is always true.
Loading history...
515
            throw new \InvalidArgumentException('parameter $css must be of type string', 1195129642);
516
        }
517
        $this->css .= $css;
518
    }
519
520
    /**
521
     * Executes defined hooks functions for the given identifier.
522
     *
523
     * These hook identifiers are valid:
524
     * + constructPostProcess
525
     * + renderPreProcess
526
     * + renderPostProcess
527
     *
528
     * @param string $identifier Specific hook identifier
529
     * @param array $hookConfiguration Additional configuration passed to hook functions
530
     */
531
    protected function executeHook($identifier, array $hookConfiguration = [])
532
    {
533
        $options = &$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/backend.php'];
534
        foreach ($options[$identifier] ?? [] as $hookFunction) {
535
            GeneralUtility::callUserFunction($hookFunction, $hookConfiguration, $this);
536
        }
537
    }
538
539
    /**
540
     * loads all modules from the repository
541
     * and renders it with a template
542
     *
543
     * @return string
544
     */
545
    protected function generateModuleMenu()
546
    {
547
        $view = $this->getFluidTemplateObject($this->templatePath . 'ModuleMenu/Main.html');
548
        $view->assign('modules', $this->moduleStorage);
549
        return $view->render();
550
    }
551
552
    protected function getCollapseStateOfMenu(): bool
553
    {
554
        $uc = json_decode((string)json_encode($this->getBackendUser()->uc), true);
555
        $collapseState = $uc['BackendComponents']['States']['typo3-module-menu']['collapsed'] ?? false;
556
557
        return $collapseState === true || $collapseState === 'true';
558
    }
559
560
    /**
561
     * Returns the Module menu for the AJAX request
562
     *
563
     * @return ResponseInterface
564
     */
565
    public function getModuleMenu(): ResponseInterface
566
    {
567
        return new JsonResponse(['menu' => $this->generateModuleMenu()]);
568
    }
569
570
    /**
571
     * Returns the toolbar for the AJAX request
572
     *
573
     * @return ResponseInterface
574
     */
575
    public function getTopbar(): ResponseInterface
576
    {
577
        return new JsonResponse(['topbar' => $this->renderTopbar()]);
578
    }
579
580
    /**
581
     * returns a new standalone view, shorthand function
582
     *
583
     * @param string $templatePathAndFileName optional the path to set the template path and filename
584
     * @return \TYPO3\CMS\Fluid\View\StandaloneView
585
     */
586
    protected function getFluidTemplateObject($templatePathAndFileName = null)
587
    {
588
        $view = GeneralUtility::makeInstance(StandaloneView::class);
589
        $view->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Partials')]);
590
        if ($templatePathAndFileName) {
591
            $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templatePathAndFileName));
592
        }
593
        return $view;
594
    }
595
596
    /**
597
     * Returns LanguageService
598
     *
599
     * @return LanguageService
600
     */
601
    protected function getLanguageService()
602
    {
603
        return $GLOBALS['LANG'];
604
    }
605
606
    /**
607
     * Returns the current BE user.
608
     *
609
     * @return BackendUserAuthentication
610
     */
611
    protected function getBackendUser()
612
    {
613
        return $GLOBALS['BE_USER'];
614
    }
615
}
616