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