'; /** * Flash message queue * * @var \TYPO3\CMS\Core\Messaging\FlashMessageQueue */ protected $flashMessageQueue; /** * Returns the current body tag * * @return string */ public function getBodyTag() { return $this->bodyTag; } /** * Sets the body tag * * @param string $bodyTag * @return self */ public function setBodyTag($bodyTag): self { $this->bodyTag = $bodyTag; return $this; } /** * Gets the standalone view. * * @return StandaloneView */ public function getView() { return $this->view; } /** * Set content * * @param string $content Content of the module * @return self */ public function setContent($content): self { $this->view->assign('content', $content); return $this; } /** * Set title tag * * @param string $title * @return self */ public function setTitle($title): self { $this->title = $title; return $this; } /** * Returns the IconFactory * * @return IconFactory */ public function getIconFactory() { return $this->iconFactory; } /** * Class constructor * Sets up view and property objects * * @throws InvalidTemplateResourceException In case a template is invalid */ public function __construct() { $this->view = GeneralUtility::makeInstance(StandaloneView::class); $this->view->setPartialRootPaths($this->partialRootPaths); $this->view->setTemplateRootPaths($this->templateRootPaths); $this->view->setLayoutRootPaths($this->layoutRootPaths); $this->view->setTemplate($this->templateFile); $this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class); $this->docHeaderComponent = GeneralUtility::makeInstance(DocHeaderComponent::class); $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class); } /** * Loads all necessary Javascript Files */ protected function loadJavaScripts() { $this->pageRenderer->loadRequireJsModule('bootstrap'); if (TYPO3_MODE === 'BE' && $this->getBackendUserAuthentication() && !empty($this->getBackendUserAuthentication()->user)) { $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ContextHelp'); $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/DocumentHeader'); $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/SplitButtons'); } } /** * Loads all necessary stylesheets */ protected function loadStylesheets() { if (!empty($GLOBALS['TBE_STYLES']['stylesheet'])) { $this->pageRenderer->addCssFile($GLOBALS['TBE_STYLES']['stylesheet']); } if (!empty($GLOBALS['TBE_STYLES']['stylesheet2'])) { $this->pageRenderer->addCssFile($GLOBALS['TBE_STYLES']['stylesheet2']); } } /** * Sets mandatory parameters for the view (pageRenderer) */ protected function setupPage() { // Yes, hardcoded on purpose $this->pageRenderer->setXmlPrologAndDocType(''); $this->pageRenderer->setCharSet('utf-8'); $this->pageRenderer->setLanguage($GLOBALS['LANG']->lang); $this->pageRenderer->setMetaTag('name', 'viewport', 'width=device-width, initial-scale=1'); } /** * Wrapper function for adding JS inline blocks */ protected function setJavaScriptCodeArray() { foreach ($this->javascriptCodeArray as $name => $code) { $this->pageRenderer->addJsInlineCode($name, $code, false); } } /** * Adds JS inline blocks of code to the internal registry * * @param string $name Javascript code block name * @param string $code Inline Javascript * @return self */ public function addJavaScriptCode($name = '', $code = ''): self { $this->javascriptCodeArray[$name] = $code; return $this; } /** * Get the DocHeader * * @return DocHeaderComponent */ public function getDocHeaderComponent() { return $this->docHeaderComponent; } /** * Returns the fully rendered view * * @return string */ public function renderContent() { $this->setupPage(); $this->pageRenderer->setTitle($this->title); $this->loadJavaScripts(); $this->setJavaScriptCodeArray(); $this->loadStylesheets(); $this->view->assign('docHeader', $this->docHeaderComponent->docHeaderContent()); if ($this->moduleId) { $this->view->assign('moduleId', $this->moduleId); } if ($this->moduleName) { $this->view->assign('moduleName', $this->moduleName); } $this->view->assign('uiBlock', $this->uiBlock); $this->view->assign('flashMessageQueueIdentifier', $this->getFlashMessageQueue()->getIdentifier()); $renderedPage = $this->pageRenderer->render(PageRenderer::PART_HEADER); $renderedPage .= $this->bodyTag; $renderedPage .= $this->view->render(); $this->pageRenderer->addJsFooterInlineCode('updateSignals', BackendUtility::getUpdateSignalCode()); $renderedPage .= $this->pageRenderer->render(PageRenderer::PART_FOOTER); return $renderedPage; } /** * Get PageRenderer * * @return PageRenderer */ public function getPageRenderer() { return $this->pageRenderer; } /** * Set form tag * * @param string $formTag Form tag to add * @return self */ public function setForm($formTag = ''): self { $this->view->assign('formTag', $formTag); return $this; } /** * Sets the ModuleId * * @param string $moduleId ID of the module * @return self */ public function setModuleId($moduleId): self { $this->moduleId = $moduleId; $this->registerModuleMenu($moduleId); return $this; } /** * Sets the ModuleName * * @param string $moduleName Name of the module * @return self */ public function setModuleName($moduleName): self { $this->moduleName = $moduleName; return $this; } /** * Generates the Menu for things like Web->Info * * @param $moduleMenuIdentifier * @return self */ public function registerModuleMenu($moduleMenuIdentifier): self { if (isset($GLOBALS['TBE_MODULES_EXT'][$moduleMenuIdentifier])) { $menuEntries = $GLOBALS['TBE_MODULES_EXT'][$moduleMenuIdentifier]['MOD_MENU']['function']; $menu = $this->getDocHeaderComponent()->getMenuRegistry()->makeMenu()->setIdentifier('MOD_FUNC'); foreach ($menuEntries as $menuEntry) { $menuItem = $menu->makeMenuItem() ->setTitle($menuEntry['title']) ->setHref('#'); $menu->addMenuItem($menuItem); } $this->docHeaderComponent->getMenuRegistry()->addMenu($menu); } return $this; } /** * Creates a tab menu where the tabs or collapsible are rendered with bootstrap markup * * @param array $menuItems Tab elements, each element is an array with "label" and "content" * @param string $domId DOM id attribute, will be appended with an iteration number per tab. * @param int $defaultTabIndex Default tab to open (for toggle <=0). Value corresponds to integer-array index + 1 * (index zero is "1", index "1" is 2 etc.). A value of zero (or something non-existing * will result in no default tab open. * @param bool $collapsible If set, the tabs are rendered as headers instead over each sheet. Effectively this means * there is no tab menu, but rather a foldout/fold-in menu. * @param bool $wrapContent If set, the content is wrapped in div structure which provides a padding and border * style. Set this FALSE to get unstyled content pane with fullsize content area. * @param bool $storeLastActiveTab If set, the last open tab is stored in local storage and will be re-open again. * If you don't need this feature, e.g. for wizards like import/export you can * disable this behaviour. * @return string */ public function getDynamicTabMenu(array $menuItems, $domId, $defaultTabIndex = 1, $collapsible = false, $wrapContent = true, $storeLastActiveTab = true) { $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Tabs'); $templatePath = ExtensionManagementUtility::extPath('backend') . 'Resources/Private/Templates/DocumentTemplate/'; $view = GeneralUtility::makeInstance(StandaloneView::class); $view->setTemplatePathAndFilename($templatePath . ($collapsible ? 'Collapse.html' : 'Tabs.html')); $view->setPartialRootPaths([$templatePath . 'Partials']); $view->assignMultiple([ 'id' => 'DTM-' . GeneralUtility::shortMD5($domId), 'items' => $menuItems, 'defaultTabIndex' => $defaultTabIndex, 'wrapContent' => $wrapContent, 'storeLastActiveTab' => $storeLastActiveTab, ]); return $view->render(); } /******************************************* * THE FOLLOWING METHODS ARE SUBJECT TO BE DEPRECATED / DROPPED! * * These methods have been copied over from DocumentTemplate and enables * core modules to drop the dependency to DocumentTemplate altogether without * rewriting these modules now. * The methods below are marked as internal and will be removed * one-by-one with further refactoring of modules. * * Do not use these methods within own extensions if possible or * be prepared to change this later again. *******************************************/ /** * Includes a javascript library that exists in the core /typo3/ directory * * @param string $lib Library name. Call it with the full path like * "sysext/core/Resources/Public/JavaScript/QueryGenerator.js" to load it * * @internal * @return self * @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0, use PageRenderer directly. * @see \TYPO3\CMS\Core\Page\PageRenderer::addJsFile */ public function loadJavascriptLib($lib): self { trigger_error('ModuleTemplate->loadJavascriptLib() should not be used any longer, as this method will be removed in TYPO3 v10.0.', E_USER_DEPRECATED); $this->pageRenderer->addJsFile($lib); return $this; } /** * Returns a linked shortcut-icon which will call the shortcut frame and set a * shortcut there back to the calling page/module * * @param string $gvList Is the list of GET variables to store (if any) * @param string $setList Is the list of SET[] variables to store * (if any) - SET[] variables a stored in $GLOBALS["SOBE"]->MOD_SETTINGS * for backend modules * @param string $modName Module name string * @param string|int $motherModName Is used to enter the "parent module * name" if the module is a submodule under eg. Web>* or File>*. You * can also set this value to 1 in which case the currentLoadedModule * is sent to the shortcut script (so - not a fixed value!) - that is used * in file_edit and wizard_rte modules where those are really running as * a part of another module. * @param string $displayName When given this name is used instead of the * module name. * @param string $classes Additional CSS classes for the link around the icon * * @return string HTML content * @todo Make this thing return a button object * @internal */ public function makeShortcutIcon($gvList, $setList, $modName, $motherModName = '', $displayName = '', $classes = 'btn btn-default btn-sm') { $gvList = 'route,id,' . $gvList; $storeUrl = $this->makeShortcutUrl($gvList, $setList); $pathInfo = parse_url(GeneralUtility::getIndpEnv('REQUEST_URI')); // Fallback for alt_mod. We still pass in the old xMOD... stuff, // but TBE_MODULES only knows about "record_edit". // We still need to pass the xMOD name to createShortcut below, // since this is used for icons. $moduleName = $modName === 'xMOD_alt_doc.php' ? 'record_edit' : $modName; // Add the module identifier automatically if typo3/index.php is used: // @todo: routing if (GeneralUtility::_GET('route') !== null) { $storeUrl = '&route=' . $moduleName . $storeUrl; } if ((int)$motherModName === 1) { $motherModule = 'top.currentModuleLoaded'; } elseif ($motherModName) { $motherModule = GeneralUtility::quoteJSvalue($motherModName); } else { $motherModule = '\'\''; } $confirmationText = GeneralUtility::quoteJSvalue( $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.makeBookmark') ); $shortcutUrl = $pathInfo['path'] . '?' . $storeUrl; $shortcutRepository = GeneralUtility::makeInstance(ShortcutRepository::class); $shortcutExist = $shortcutRepository->shortcutExists($shortcutUrl); if ($shortcutExist) { return '' . $this->iconFactory->getIcon('actions-system-shortcut-active', Icon::SIZE_SMALL)->render() . ''; } $url = GeneralUtility::quoteJSvalue(rawurlencode($shortcutUrl)); $onClick = 'top.TYPO3.ShortcutMenu.createShortcut(' . GeneralUtility::quoteJSvalue(rawurlencode($modName)) . ', ' . $url . ', ' . $confirmationText . ', ' . $motherModule . ', this, ' . GeneralUtility::quoteJSvalue($displayName) . ');return false;'; return '' . $this->iconFactory->getIcon('actions-system-shortcut-new', Icon::SIZE_SMALL)->render() . ''; } /** * MAKE url for storing * Internal func * * @param string $gvList Is the list of GET variables to store (if any) * @param string $setList Is the list of SET[] variables to store (if any) * - SET[] variables a stored in $GLOBALS["SOBE"]->MOD_SETTINGS for backend * modules * * @return string GET-parameters for the shortcut-url only(!). String starts with '&' * @internal */ public function makeShortcutUrl($gvList, $setList) { $getParams = GeneralUtility::_GET(); $storeArray = array_merge( GeneralUtility::compileSelectedGetVarsFromArray($gvList, $getParams), ['SET' => GeneralUtility::compileSelectedGetVarsFromArray($setList, (array)$GLOBALS['SOBE']->MOD_SETTINGS)] ); return HttpUtility::buildQueryString($storeArray, '&'); } /** * Returns the BE USER Object * * @return BackendUserAuthentication */ protected function getBackendUserAuthentication() { return $GLOBALS['BE_USER']; } /** * Returns the LanguageService * * @return LanguageService */ protected function getLanguageService() { return $GLOBALS['LANG']; } /** * Returns an image-tag with an 18x16 icon of the following types: * * $type: * -1:» OK icon (Check-mark) * 1:» Notice (Speach-bubble) * 2:» Warning (Yellow triangle) * 3:» Fatal error (Red stop sign) * * @param int $type See description * * @return string HTML image tag (if applicable) * @internal * @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0 */ public function icons($type) { trigger_error('ModuleTemplate->icons() should not be used any longer, as this will be removed in TYPO3 v10.0.', E_USER_DEPRECATED); $icon = ''; switch ($type) { case self::STATUS_ICON_ERROR: $icon = 'status-dialog-error'; break; case self::STATUS_ICON_WARNING: $icon = 'status-dialog-warning'; break; case self::STATUS_ICON_NOTIFICATION: $icon = 'status-dialog-notification'; break; case self::STATUS_ICON_OK: $icon = 'status-dialog-ok'; break; default: // Do nothing } if ($icon != '') { return $this->iconFactory->getIcon($icon, Icon::SIZE_SMALL)->render(); } return ''; } /** * Returns JavaScript variables setting the returnUrl and thisScript location for use by JavaScript on the page. * Used in fx. db_list.php (Web>List) * * @param string $thisLocation URL to "this location" / current script * @return string Urls are returned as JavaScript variables T3_RETURN_URL and T3_THIS_LOCATION * @see typo3/db_list.php * @internal */ public function redirectUrls($thisLocation = '') { $thisLocation = $thisLocation ? $thisLocation : GeneralUtility::linkThisScript([ 'CB' => '', 'SET' => '', 'cmd' => '', 'popViewId' => '' ]); $out = ' var T3_RETURN_URL = ' . GeneralUtility::quoteJSvalue(str_replace('%20', '', rawurlencode(GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'))))) . '; var T3_THIS_LOCATION = ' . GeneralUtility::quoteJSvalue(str_replace('%20', '', rawurlencode($thisLocation))) . ' '; return $out; } /** * Returns the header-bar in the top of most backend modules * Closes section if open. * * @param string $text The text string for the header * @return string HTML content * @internal */ public function header($text) { return '