'Using BackendController::render() is deprecated and will not be possible anymore in TYPO3 v10.0.', ]; /** * @var string */ protected $content = ''; /** * @var string */ protected $css = ''; /** * @var array */ protected $cssFiles = []; /** * @var string */ protected $js = ''; /** * @var array */ protected $jsFiles = []; /** * @var array */ protected $toolbarItems = []; /** * @var bool */ protected $debug; /** * @var string */ protected $templatePath = 'EXT:backend/Resources/Private/Templates/'; /** * @var string */ protected $partialPath = 'EXT:backend/Resources/Private/Partials/'; /** * @var \TYPO3\CMS\Backend\Domain\Repository\Module\BackendModuleRepository */ protected $backendModuleRepository; /** * @var \TYPO3\CMS\Backend\Module\ModuleLoader Object for loading backend modules */ protected $moduleLoader; /** * @var PageRenderer */ protected $pageRenderer; /** * @var IconFactory */ protected $iconFactory; /** * Constructor */ public function __construct() { $this->getLanguageService()->includeLLFile('EXT:core/Resources/Private/Language/locallang_misc.xlf'); $this->backendModuleRepository = GeneralUtility::makeInstance(BackendModuleRepository::class); $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class); $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class); // Set debug flag for BE development only $this->debug = (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] === 1; // Initializes the backend modules structure for use later. $this->moduleLoader = GeneralUtility::makeInstance(ModuleLoader::class); $this->moduleLoader->load($GLOBALS['TBE_MODULES']); $this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class); // Add default BE javascript $this->jsFiles = [ 'md5' => 'EXT:backend/Resources/Public/JavaScript/md5.js', 'evalfield' => 'EXT:backend/Resources/Public/JavaScript/jsfunc.evalfield.js', 'backend' => 'EXT:backend/Resources/Public/JavaScript/backend.js', ]; $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LoginRefresh', 'function(LoginRefresh) { LoginRefresh.setIntervalTime(' . MathUtility::forceIntegerInRange((int)$GLOBALS['TYPO3_CONF_VARS']['BE']['sessionTimeout'] - 60, 60) . '); LoginRefresh.setLoginFramesetUrl(' . GeneralUtility::quoteJSvalue((string)$uriBuilder->buildUriFromRoute('login_frameset')) . '); LoginRefresh.setLogoutUrl(' . GeneralUtility::quoteJSvalue((string)$uriBuilder->buildUriFromRoute('logout')) . '); LoginRefresh.initialize(); }'); // load module menu $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ModuleMenu'); // load Toolbar class $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Toolbar'); // load Utility class $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Utility'); // load Notification functionality $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Notification'); // load Modals $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Modal'); // load InfoWindow $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/InfoWindow'); // load ContextMenu $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu'); // load the storage API and fill the UC into the PersistentStorage, so no additional AJAX call is needed $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Storage'); $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Storage/Persistent', 'function(PersistentStorage) { PersistentStorage.load(' . json_encode($this->getBackendUser()->uc) . '); }'); // load debug console $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/DebugConsole'); $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/locallang_core.xlf'); $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/locallang_misc.xlf'); $this->pageRenderer->addInlineLanguageLabelFile('EXT:backend/Resources/Private/Language/locallang_layout.xlf'); $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf'); $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/debugger.xlf'); $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/wizard.xlf'); $this->pageRenderer->addInlineSetting('ShowItem', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('show_item')); $this->pageRenderer->addInlineSetting('RecordHistory', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('record_history')); $this->pageRenderer->addInlineSetting('NewRecord', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('db_new')); $this->pageRenderer->addInlineSetting('FormEngine', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('record_edit')); $this->pageRenderer->addInlineSetting('RecordCommit', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('tce_db')); $this->pageRenderer->addInlineSetting('WebLayout', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('web_layout')); $this->css = ''; $this->initializeToolbarItems(); $this->executeHook('constructPostProcess'); } /** * Initialize toolbar item objects * * @throws \RuntimeException */ protected function initializeToolbarItems() { $toolbarItemInstances = []; foreach ($GLOBALS['TYPO3_CONF_VARS']['BE']['toolbarItems'] ?? [] as $className) { $toolbarItemInstance = GeneralUtility::makeInstance($className); if (!$toolbarItemInstance instanceof ToolbarItemInterface) { throw new \RuntimeException( 'class ' . $className . ' is registered as toolbar item but does not implement' . ToolbarItemInterface::class, 1415958218 ); } $index = (int)$toolbarItemInstance->getIndex(); if ($index < 0 || $index > 100) { throw new \RuntimeException( 'getIndex() must return an integer between 0 and 100', 1415968498 ); } // Find next free position in array while (array_key_exists($index, $toolbarItemInstances)) { $index++; } $toolbarItemInstances[$index] = $toolbarItemInstance; } ksort($toolbarItemInstances); $this->toolbarItems = $toolbarItemInstances; } /** * Injects the request object for the current request or subrequest * As this controller goes only through the render() method, it is rather simple for now * * @return ResponseInterface the response with the content */ public function mainAction(): ResponseInterface { $this->render(); return new HtmlResponse($this->content); } /** * Main function generating the BE scaffolding */ protected function render() { $this->executeHook('renderPreProcess'); // Prepare the scaffolding, at this point extension may still add javascript and css $view = $this->getFluidTemplateObject($this->templatePath . 'Backend/Main.html'); $view->assign('moduleMenuCollapsed', $this->getCollapseStateOfMenu()); $view->assign('moduleMenu', $this->generateModuleMenu()); $view->assign('topbar', $this->renderTopbar()); /****************************************************** * Now put the complete backend document together ******************************************************/ foreach ($this->cssFiles as $cssFileName => $cssFile) { $this->pageRenderer->addCssFile($cssFile); // Load additional css files to overwrite existing core styles if (!empty($GLOBALS['TBE_STYLES']['stylesheets'][$cssFileName])) { $this->pageRenderer->addCssFile($GLOBALS['TBE_STYLES']['stylesheets'][$cssFileName]); } } if (!empty($this->css)) { $this->pageRenderer->addCssInlineBlock('BackendInlineCSS', $this->css); } foreach ($this->jsFiles as $jsFile) { $this->pageRenderer->addJsFile($jsFile); } $this->generateJavascript(); $this->pageRenderer->addJsInlineCode('BackendInlineJavascript', $this->js, false); // Set document title: $title = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . ' [TYPO3 CMS ' . TYPO3_version . ']' : 'TYPO3 CMS ' . TYPO3_version; // Renders the module page $this->content = $this->getDocumentTemplate()->render($title, $view->render()); $hookConfiguration = ['content' => &$this->content]; $this->executeHook('renderPostProcess', $hookConfiguration); } /** * Renders the topbar, containing the backend logo, sitename etc. * * @return string */ protected function renderTopbar() { $view = $this->getFluidTemplateObject($this->partialPath . 'Backend/Topbar.html'); // Extension Configuration to find the TYPO3 logo in the left corner $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('backend'); $logoPath = ''; if (!empty($extConf['backendLogo'])) { $customBackendLogo = GeneralUtility::getFileAbsFileName(ltrim($extConf['backendLogo'], '/')); if (!empty($customBackendLogo)) { $logoPath = $customBackendLogo; } } // if no custom logo was set or the path is invalid, use the original one if (empty($logoPath) || !file_exists($logoPath)) { $logoPath = GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Public/Images/typo3_logo_orange.svg'); $logoWidth = 22; $logoHeight = 22; } else { // set width/height for custom logo $imageInfo = GeneralUtility::makeInstance(ImageInfo::class, $logoPath); $logoWidth = $imageInfo->getWidth() ?? '22'; $logoHeight = $imageInfo->getHeight() ?? '22'; // High-resolution? if (strpos($logoPath, '@2x.') !== false) { $logoWidth /= 2; $logoHeight /= 2; } } $view->assign('logoUrl', PathUtility::getAbsoluteWebPath($logoPath)); $view->assign('logoWidth', $logoWidth); $view->assign('logoHeight', $logoHeight); $view->assign('applicationVersion', TYPO3_version); $view->assign('siteName', $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']); $view->assign('toolbar', $this->renderToolbar()); return $view->render(); } /** * Renders the items in the top toolbar * * @return string top toolbar elements as HTML */ protected function renderToolbar() { $toolbar = []; foreach ($this->toolbarItems as $toolbarItem) { /** @var \TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface $toolbarItem */ if ($toolbarItem->checkAccess()) { $hasDropDown = (bool)$toolbarItem->hasDropDown(); $additionalAttributes = (array)$toolbarItem->getAdditionalAttributes(); $liAttributes = []; // Merge class: Add dropdown class if hasDropDown, add classes from additional attributes $classes = []; $classes[] = 'toolbar-item'; $classes[] = 't3js-toolbar-item'; if (isset($additionalAttributes['class'])) { $classes[] = $additionalAttributes['class']; unset($additionalAttributes['class']); } $liAttributes['class'] = implode(' ', $classes); // Add further attributes foreach ($additionalAttributes as $name => $value) { $liAttributes[$name] = $value; } // Create a unique id from class name $fullyQualifiedClassName = \get_class($toolbarItem); $className = GeneralUtility::underscoredToLowerCamelCase($fullyQualifiedClassName); $className = GeneralUtility::camelCaseToLowerCaseUnderscored($className); $className = str_replace(['_', '\\'], '-', $className); $liAttributes['id'] = $className; // Create data attribute identifier $shortName = substr($fullyQualifiedClassName, strrpos($fullyQualifiedClassName, '\\') + 1); $dataToolbarIdentifier = GeneralUtility::camelCaseToLowerCaseUnderscored($shortName); $dataToolbarIdentifier = str_replace('_', '-', $dataToolbarIdentifier); $liAttributes['data-toolbar-identifier'] = $dataToolbarIdentifier; $toolbar[] = '