[!!!][TASK] Remove deprecated `TYPO3.Utility` object
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Controller / BackendController.php
1 <?php
2 namespace TYPO3\CMS\Backend\Controller;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use Psr\Http\Message\ResponseInterface;
18 use TYPO3\CMS\Backend\Domain\Repository\Module\BackendModuleRepository;
19 use TYPO3\CMS\Backend\Module\ModuleLoader;
20 use TYPO3\CMS\Backend\Routing\UriBuilder;
21 use TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface;
22 use TYPO3\CMS\Backend\Utility\BackendUtility;
23 use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
24 use TYPO3\CMS\Core\Database\ConnectionPool;
25 use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction;
26 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
27 use TYPO3\CMS\Core\Http\HtmlResponse;
28 use TYPO3\CMS\Core\Http\JsonResponse;
29 use TYPO3\CMS\Core\Imaging\IconFactory;
30 use TYPO3\CMS\Core\Page\PageRenderer;
31 use TYPO3\CMS\Core\Type\Bitmask\Permission;
32 use TYPO3\CMS\Core\Type\File\ImageInfo;
33 use TYPO3\CMS\Core\Utility\GeneralUtility;
34 use TYPO3\CMS\Core\Utility\MathUtility;
35 use TYPO3\CMS\Core\Utility\PathUtility;
36 use TYPO3\CMS\Fluid\View\StandaloneView;
37
38 /**
39 * Class for rendering the TYPO3 backend
40 */
41 class BackendController
42 {
43 /**
44 * @var string
45 */
46 protected $content = '';
47
48 /**
49 * @var string
50 */
51 protected $css = '';
52
53 /**
54 * @var array
55 */
56 protected $cssFiles = [];
57
58 /**
59 * @var string
60 */
61 protected $js = '';
62
63 /**
64 * @var array
65 */
66 protected $jsFiles = [];
67
68 /**
69 * @var array
70 */
71 protected $toolbarItems = [];
72
73 /**
74 * @var bool
75 */
76 protected $debug;
77
78 /**
79 * @var string
80 */
81 protected $templatePath = 'EXT:backend/Resources/Private/Templates/';
82
83 /**
84 * @var string
85 */
86 protected $partialPath = 'EXT:backend/Resources/Private/Partials/';
87
88 /**
89 * @var \TYPO3\CMS\Backend\Domain\Repository\Module\BackendModuleRepository
90 */
91 protected $backendModuleRepository;
92
93 /**
94 * @var \TYPO3\CMS\Backend\Module\ModuleLoader Object for loading backend modules
95 */
96 protected $moduleLoader;
97
98 /**
99 * @var PageRenderer
100 */
101 protected $pageRenderer;
102
103 /**
104 * @var IconFactory
105 */
106 protected $iconFactory;
107
108 /**
109 * Constructor
110 */
111 public function __construct()
112 {
113 $this->getLanguageService()->includeLLFile('EXT:core/Resources/Private/Language/locallang_misc.xlf');
114
115 $this->backendModuleRepository = GeneralUtility::makeInstance(BackendModuleRepository::class);
116 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
117 $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
118 // Set debug flag for BE development only
119 $this->debug = (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] === 1;
120 // Initializes the backend modules structure for use later.
121 $this->moduleLoader = GeneralUtility::makeInstance(ModuleLoader::class);
122 $this->moduleLoader->load($GLOBALS['TBE_MODULES']);
123 $this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
124 // Add default BE javascript
125 $this->jsFiles = [
126 'md5' => 'EXT:backend/Resources/Public/JavaScript/md5.js',
127 'evalfield' => 'EXT:backend/Resources/Public/JavaScript/jsfunc.evalfield.js',
128 'backend' => 'EXT:backend/Resources/Public/JavaScript/backend.js',
129 ];
130 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LoginRefresh', 'function(LoginRefresh) {
131 LoginRefresh.setIntervalTime(' . MathUtility::forceIntegerInRange((int)$GLOBALS['TYPO3_CONF_VARS']['BE']['sessionTimeout'] - 60, 60) . ');
132 LoginRefresh.setLoginFramesetUrl(' . GeneralUtility::quoteJSvalue((string)$uriBuilder->buildUriFromRoute('login_frameset')) . ');
133 LoginRefresh.setLogoutUrl(' . GeneralUtility::quoteJSvalue((string)$uriBuilder->buildUriFromRoute('logout')) . ');
134 LoginRefresh.initialize();
135 }');
136
137 // load module menu
138 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ModuleMenu');
139
140 // load Toolbar class
141 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Toolbar');
142
143 // load Notification functionality
144 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Notification');
145
146 // load Modals
147 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Modal');
148
149 // load InfoWindow
150 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/InfoWindow');
151
152 // load ContextMenu
153 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
154
155 // load the storage API and fill the UC into the PersistentStorage, so no additional AJAX call is needed
156 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Storage/Persistent', 'function(PersistentStorage) {
157 PersistentStorage.load(' . json_encode($this->getBackendUser()->uc) . ');
158 }');
159
160 // load debug console
161 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/DebugConsole');
162
163 $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/locallang_core.xlf');
164 $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/locallang_misc.xlf');
165 $this->pageRenderer->addInlineLanguageLabelFile('EXT:backend/Resources/Private/Language/locallang_layout.xlf');
166 $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf');
167
168 $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/debugger.xlf');
169 $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/wizard.xlf');
170
171 $this->pageRenderer->addInlineSetting('ShowItem', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('show_item'));
172 $this->pageRenderer->addInlineSetting('RecordHistory', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('record_history'));
173 $this->pageRenderer->addInlineSetting('NewRecord', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('db_new'));
174 $this->pageRenderer->addInlineSetting('FormEngine', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('record_edit'));
175 $this->pageRenderer->addInlineSetting('RecordCommit', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('tce_db'));
176 $this->pageRenderer->addInlineSetting('WebLayout', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('web_layout'));
177
178 $this->css = '';
179
180 $this->initializeToolbarItems();
181 $this->executeHook('constructPostProcess');
182 }
183
184 /**
185 * Initialize toolbar item objects
186 *
187 * @throws \RuntimeException
188 */
189 protected function initializeToolbarItems()
190 {
191 $toolbarItemInstances = [];
192 foreach ($GLOBALS['TYPO3_CONF_VARS']['BE']['toolbarItems'] ?? [] as $className) {
193 $toolbarItemInstance = GeneralUtility::makeInstance($className);
194 if (!$toolbarItemInstance instanceof ToolbarItemInterface) {
195 throw new \RuntimeException(
196 'class ' . $className . ' is registered as toolbar item but does not implement'
197 . ToolbarItemInterface::class,
198 1415958218
199 );
200 }
201 $index = (int)$toolbarItemInstance->getIndex();
202 if ($index < 0 || $index > 100) {
203 throw new \RuntimeException(
204 'getIndex() must return an integer between 0 and 100',
205 1415968498
206 );
207 }
208 // Find next free position in array
209 while (array_key_exists($index, $toolbarItemInstances)) {
210 $index++;
211 }
212 $toolbarItemInstances[$index] = $toolbarItemInstance;
213 }
214 ksort($toolbarItemInstances);
215 $this->toolbarItems = $toolbarItemInstances;
216 }
217
218 /**
219 * Injects the request object for the current request or subrequest
220 * As this controller goes only through the render() method, it is rather simple for now
221 *
222 * @return ResponseInterface the response with the content
223 */
224 public function mainAction(): ResponseInterface
225 {
226 $this->render();
227 return new HtmlResponse($this->content);
228 }
229
230 /**
231 * Main function generating the BE scaffolding
232 */
233 protected function render()
234 {
235 $this->executeHook('renderPreProcess');
236
237 // Prepare the scaffolding, at this point extension may still add javascript and css
238 $view = $this->getFluidTemplateObject($this->templatePath . 'Backend/Main.html');
239
240 $view->assign('moduleMenu', $this->generateModuleMenu());
241 $view->assign('topbar', $this->renderTopbar());
242
243 /******************************************************
244 * Now put the complete backend document together
245 ******************************************************/
246 foreach ($this->cssFiles as $cssFileName => $cssFile) {
247 $this->pageRenderer->addCssFile($cssFile);
248 // Load additional css files to overwrite existing core styles
249 if (!empty($GLOBALS['TBE_STYLES']['stylesheets'][$cssFileName])) {
250 $this->pageRenderer->addCssFile($GLOBALS['TBE_STYLES']['stylesheets'][$cssFileName]);
251 }
252 }
253 if (!empty($this->css)) {
254 $this->pageRenderer->addCssInlineBlock('BackendInlineCSS', $this->css);
255 }
256 foreach ($this->jsFiles as $jsFile) {
257 $this->pageRenderer->addJsFile($jsFile);
258 }
259 $this->generateJavascript();
260 $this->pageRenderer->addJsInlineCode('BackendInlineJavascript', $this->js, false);
261
262 // Set document title:
263 $title = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . ' [TYPO3 CMS ' . TYPO3_version . ']' : 'TYPO3 CMS ' . TYPO3_version;
264 // Renders the module page
265 $this->content = $this->getDocumentTemplate()->render($title, $view->render());
266 $hookConfiguration = ['content' => &$this->content];
267 $this->executeHook('renderPostProcess', $hookConfiguration);
268 }
269
270 /**
271 * Renders the topbar, containing the backend logo, sitename etc.
272 *
273 * @return string
274 */
275 protected function renderTopbar()
276 {
277 $view = $this->getFluidTemplateObject($this->partialPath . 'Backend/Topbar.html');
278
279 // Extension Configuration to find the TYPO3 logo in the left corner
280 $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('backend');
281 $logoPath = '';
282 if (!empty($extConf['backendLogo'])) {
283 $customBackendLogo = GeneralUtility::getFileAbsFileName($extConf['backendLogo']);
284 if (!empty($customBackendLogo)) {
285 $logoPath = $customBackendLogo;
286 }
287 }
288 // if no custom logo was set or the path is invalid, use the original one
289 if (empty($logoPath) || !file_exists($logoPath)) {
290 $logoPath = GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Public/Images/typo3_logo_orange.svg');
291 $logoWidth = 22;
292 $logoHeight = 22;
293 } else {
294 // set width/height for custom logo
295 $imageInfo = GeneralUtility::makeInstance(ImageInfo::class, $logoPath);
296 $logoWidth = $imageInfo->getWidth() ?? '22';
297 $logoHeight = $imageInfo->getHeight() ?? '22';
298
299 // High-resolution?
300 if (strpos($logoPath, '@2x.') !== false) {
301 $logoWidth /= 2;
302 $logoHeight /= 2;
303 }
304 }
305
306 $view->assign('logoUrl', PathUtility::getAbsoluteWebPath($logoPath));
307 $view->assign('logoWidth', $logoWidth);
308 $view->assign('logoHeight', $logoHeight);
309 $view->assign('applicationVersion', TYPO3_version);
310 $view->assign('siteName', $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']);
311 $view->assign('toolbar', $this->renderToolbar());
312
313 return $view->render();
314 }
315
316 /**
317 * Renders the items in the top toolbar
318 *
319 * @return string top toolbar elements as HTML
320 */
321 protected function renderToolbar()
322 {
323 $toolbar = [];
324 foreach ($this->toolbarItems as $toolbarItem) {
325 /** @var \TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface $toolbarItem */
326 if ($toolbarItem->checkAccess()) {
327 $hasDropDown = (bool)$toolbarItem->hasDropDown();
328 $additionalAttributes = (array)$toolbarItem->getAdditionalAttributes();
329
330 $liAttributes = [];
331
332 // Merge class: Add dropdown class if hasDropDown, add classes from additional attributes
333 $classes = [];
334 $classes[] = 'toolbar-item';
335 $classes[] = 't3js-toolbar-item';
336 if (isset($additionalAttributes['class'])) {
337 $classes[] = $additionalAttributes['class'];
338 unset($additionalAttributes['class']);
339 }
340 $liAttributes['class'] = implode(' ', $classes);
341
342 // Add further attributes
343 foreach ($additionalAttributes as $name => $value) {
344 $liAttributes[$name] = $value;
345 }
346
347 // Create a unique id from class name
348 $fullyQualifiedClassName = \get_class($toolbarItem);
349 $className = GeneralUtility::underscoredToLowerCamelCase($fullyQualifiedClassName);
350 $className = GeneralUtility::camelCaseToLowerCaseUnderscored($className);
351 $className = str_replace(['_', '\\'], '-', $className);
352 $liAttributes['id'] = $className;
353
354 // Create data attribute identifier
355 $shortName = substr($fullyQualifiedClassName, strrpos($fullyQualifiedClassName, '\\') + 1);
356 $dataToolbarIdentifier = GeneralUtility::camelCaseToLowerCaseUnderscored($shortName);
357 $dataToolbarIdentifier = str_replace('_', '-', $dataToolbarIdentifier);
358 $liAttributes['data-toolbar-identifier'] = $dataToolbarIdentifier;
359
360 $toolbar[] = '<li ' . GeneralUtility::implodeAttributes($liAttributes, true) . '>';
361
362 if ($hasDropDown) {
363 $toolbar[] = '<a href="#" class="toolbar-item-link dropdown-toggle" data-toggle="dropdown">';
364 $toolbar[] = $toolbarItem->getItem();
365 $toolbar[] = '</a>';
366 $toolbar[] = '<div class="dropdown-menu" role="menu">';
367 $toolbar[] = $toolbarItem->getDropDown();
368 $toolbar[] = '</div>';
369 } else {
370 $toolbar[] = $toolbarItem->getItem();
371 }
372 $toolbar[] = '</li>';
373 }
374 }
375 return implode(LF, $toolbar);
376 }
377
378 /**
379 * Generates the JavaScript code for the backend.
380 */
381 protected function generateJavascript()
382 {
383 $beUser = $this->getBackendUser();
384 // Needed for FormEngine manipulation (date picker)
385 $dateFormat = ($GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat'] ? ['MM-DD-YYYY', 'HH:mm MM-DD-YYYY'] : ['DD-MM-YYYY', 'HH:mm DD-MM-YYYY']);
386 $this->pageRenderer->addInlineSetting('DateTimePicker', 'DateFormat', $dateFormat);
387
388 // If another page module was specified, replace the default Page module with the new one
389 $newPageModule = trim($beUser->getTSConfig()['options.']['overridePageModule'] ?? '');
390 $pageModule = BackendUtility::isModuleSetInTBE_MODULES($newPageModule) ? $newPageModule : 'web_layout';
391 if (!$beUser->check('modules', $pageModule)) {
392 $pageModule = '';
393 }
394 $t3Configuration = [
395 'username' => htmlspecialchars($beUser->user['username']),
396 'pageModule' => $pageModule,
397 'inWorkspace' => $beUser->workspace !== 0,
398 'showRefreshLoginPopup' => (bool)($GLOBALS['TYPO3_CONF_VARS']['BE']['showRefreshLoginPopup'] ?? false)
399 ];
400 $this->js .= '
401 TYPO3.configuration = ' . json_encode($t3Configuration) . ';
402 /**
403 * Frameset Module object
404 *
405 * Used in main modules with a frameset for submodules to keep the ID between modules
406 * Typically that is set by something like this in a Web>* sub module:
407 * if (top.fsMod) top.fsMod.recentIds["web"] = "\'.(int)$this->id.\'";
408 * if (top.fsMod) top.fsMod.recentIds["file"] = "...(file reference/string)...";
409 */
410 var fsMod = {
411 recentIds: [], // used by frameset modules to track the most recent used id for list frame.
412 navFrameHighlightedID: [], // used by navigation frames to track which row id was highlighted last time
413 currentBank: "0"
414 };
415
416 top.goToModule = function(modName, cMR_flag, addGetVars) {
417 TYPO3.ModuleMenu.App.showModule(modName, addGetVars);
418 }
419 ' . $this->setStartupModule();
420 // Check editing of page:
421 $this->handlePageEditing();
422 }
423
424 /**
425 * Checking if the "&edit" variable was sent so we can open it for editing the page.
426 */
427 protected function handlePageEditing()
428 {
429 $beUser = $this->getBackendUser();
430 $userTsConfig = $this->getBackendUser()->getTSConfig();
431 // EDIT page:
432 $editId = preg_replace('/[^[:alnum:]_]/', '', GeneralUtility::_GET('edit'));
433 if ($editId) {
434 // Looking up the page to edit, checking permissions:
435 $where = ' AND (' . $beUser->getPagePermsClause(Permission::PAGE_EDIT) . ' OR ' . $beUser->getPagePermsClause(Permission::CONTENT_EDIT) . ')';
436 if (MathUtility::canBeInterpretedAsInteger($editId)) {
437 $editRecord = BackendUtility::getRecordWSOL('pages', $editId, '*', $where);
438 } else {
439 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
440 $queryBuilder->getRestrictions()
441 ->removeAll()
442 ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
443 ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
444
445 $editRecord = $queryBuilder->select('*')
446 ->from('pages')
447 ->where(
448 $queryBuilder->expr()->eq(
449 'alias',
450 $queryBuilder->createNamedParameter($editId, \PDO::PARAM_STR)
451 ),
452 $queryBuilder->expr()->orX(
453 $beUser->getPagePermsClause(Permission::PAGE_EDIT),
454 $beUser->getPagePermsClause(Permission::CONTENT_EDIT)
455 )
456 )
457 ->setMaxResults(1)
458 ->execute()
459 ->fetch();
460
461 if ($editRecord !== false) {
462 BackendUtility::workspaceOL('pages', $editRecord);
463 }
464 }
465 // If the page was accessible, then let the user edit it.
466 if (is_array($editRecord) && $beUser->isInWebMount($editRecord['uid'])) {
467 // Setting JS code to open editing:
468 $this->js .= '
469 // Load page to edit:
470 window.setTimeout("top.loadEditId(' . (int)$editRecord['uid'] . ');", 500);
471 ';
472 // Checking page edit parameter:
473 if (!($userTsConfig['options.']['bookmark_onEditId_dontSetPageTree'] ?? false)) {
474 $bookmarkKeepExpanded = (bool)($userTsConfig['options.']['bookmark_onEditId_keepExistingExpanded'] ?? false);
475 // Expanding page tree:
476 BackendUtility::openPageTree((int)$editRecord['pid'], !$bookmarkKeepExpanded);
477 }
478 } else {
479 $this->js .= '
480 // Warning about page editing:
481 require(["TYPO3/CMS/Backend/Modal", "TYPO3/CMS/Backend/Severity"], function(Modal, Severity) {
482 Modal.show("", ' . GeneralUtility::quoteJSvalue(sprintf($this->getLanguageService()->getLL('noEditPage'), $editId)) . ', Severity.notice, [{
483 text: ' . GeneralUtility::quoteJSvalue($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:close')) . ',
484 active: true,
485 btnClass: "btn-info",
486 name: "cancel",
487 trigger: function () {
488 Modal.currentModal.trigger("modal-dismiss");
489 }
490 }])
491 });';
492 }
493 }
494 }
495
496 /**
497 * Sets the startup module from either GETvars module and modParams or user configuration.
498 *
499 * @return string the JavaScript code for the startup module
500 */
501 protected function setStartupModule()
502 {
503 $startModule = preg_replace('/[^[:alnum:]_]/', '', GeneralUtility::_GET('module'));
504 $startModuleParameters = '';
505 if (!$startModule) {
506 $beUser = $this->getBackendUser();
507 // start module on first login, will be removed once used the first time
508 if (isset($beUser->uc['startModuleOnFirstLogin'])) {
509 $startModule = $beUser->uc['startModuleOnFirstLogin'];
510 unset($beUser->uc['startModuleOnFirstLogin']);
511 $beUser->writeUC();
512 } elseif ($beUser->uc['startModule']) {
513 $startModule = $beUser->uc['startModule'];
514 }
515
516 // check if the start module has additional parameters, so a redirect to a specific
517 // action is possible
518 if (strpos($startModule, '->') !== false) {
519 list($startModule, $startModuleParameters) = explode('->', $startModule, 2);
520 }
521 }
522
523 $moduleParameters = GeneralUtility::_GET('modParams');
524 // if no GET parameters are set, check if there are parameters given from the UC
525 if (!$moduleParameters && $startModuleParameters) {
526 $moduleParameters = $startModuleParameters;
527 }
528
529 if ($startModule) {
530 return '
531 // start in module:
532 top.startInModule = [' . GeneralUtility::quoteJSvalue($startModule) . ', ' . GeneralUtility::quoteJSvalue($moduleParameters) . '];
533 ';
534 }
535 return '';
536 }
537
538 /**
539 * Adds a css snippet to the backend
540 *
541 * @param string $css Css snippet
542 * @throws \InvalidArgumentException
543 */
544 public function addCss($css)
545 {
546 if (!is_string($css)) {
547 throw new \InvalidArgumentException('parameter $css must be of type string', 1195129642);
548 }
549 $this->css .= $css;
550 }
551
552 /**
553 * Executes defined hooks functions for the given identifier.
554 *
555 * These hook identifiers are valid:
556 * + constructPostProcess
557 * + renderPreProcess
558 * + renderPostProcess
559 *
560 * @param string $identifier Specific hook identifier
561 * @param array $hookConfiguration Additional configuration passed to hook functions
562 */
563 protected function executeHook($identifier, array $hookConfiguration = [])
564 {
565 $options = &$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/backend.php'];
566 foreach ($options[$identifier] ?? [] as $hookFunction) {
567 GeneralUtility::callUserFunction($hookFunction, $hookConfiguration, $this);
568 }
569 }
570
571 /**
572 * loads all modules from the repository
573 * and renders it with a template
574 *
575 * @return string
576 */
577 protected function generateModuleMenu()
578 {
579 // get all modules except the user modules for the side menu
580 $moduleStorage = $this->backendModuleRepository->loadAllowedModules(['user', 'help']);
581
582 $view = $this->getFluidTemplateObject($this->templatePath . 'ModuleMenu/Main.html');
583 $view->assign('modules', $moduleStorage);
584 return $view->render();
585 }
586
587 /**
588 * Returns the Module menu for the AJAX request
589 *
590 * @return ResponseInterface
591 */
592 public function getModuleMenu(): ResponseInterface
593 {
594 return new JsonResponse(['menu' => $this->generateModuleMenu()]);
595 }
596
597 /**
598 * Returns the toolbar for the AJAX request
599 *
600 * @return ResponseInterface
601 */
602 public function getTopbar(): ResponseInterface
603 {
604 return new JsonResponse(['topbar' => $this->renderTopbar()]);
605 }
606
607 /**
608 * returns a new standalone view, shorthand function
609 *
610 * @param string $templatePathAndFileName optional the path to set the template path and filename
611 * @return \TYPO3\CMS\Fluid\View\StandaloneView
612 */
613 protected function getFluidTemplateObject($templatePathAndFileName = null)
614 {
615 $view = GeneralUtility::makeInstance(StandaloneView::class);
616 $view->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Partials')]);
617 if ($templatePathAndFileName) {
618 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templatePathAndFileName));
619 }
620 return $view;
621 }
622
623 /**
624 * Returns LanguageService
625 *
626 * @return \TYPO3\CMS\Core\Localization\LanguageService
627 */
628 protected function getLanguageService()
629 {
630 return $GLOBALS['LANG'];
631 }
632
633 /**
634 * Returns the current BE user.
635 *
636 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
637 */
638 protected function getBackendUser()
639 {
640 return $GLOBALS['BE_USER'];
641 }
642
643 /**
644 * Returns an instance of DocumentTemplate
645 *
646 * @return \TYPO3\CMS\Backend\Template\DocumentTemplate
647 */
648 protected function getDocumentTemplate()
649 {
650 return $GLOBALS['TBE_TEMPLATE'];
651 }
652 }