diff --git a/typo3/sysext/backend/Classes/Controller/ContentElement/NewContentElementController.php b/typo3/sysext/backend/Classes/Controller/ContentElement/NewContentElementController.php index f8791221200a055a07ec711e803ee3b3041013e3..4a3f3168aeaf3505ca37f6363ec3901ff83aff79 100644 --- a/typo3/sysext/backend/Classes/Controller/ContentElement/NewContentElementController.php +++ b/typo3/sysext/backend/Classes/Controller/ContentElement/NewContentElementController.php @@ -16,18 +16,23 @@ namespace TYPO3\CMS\Backend\Controller\ContentElement; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; +use TYPO3\CMS\Backend\Module\AbstractModule; +use TYPO3\CMS\Backend\Template\DocumentTemplate; use TYPO3\CMS\Backend\Utility\BackendUtility; +use TYPO3\CMS\Backend\View\BackendLayoutView; +use TYPO3\CMS\Backend\Wizard\NewContentElementWizardHookInterface; use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Imaging\IconProvider\BitmapIconProvider; use TYPO3\CMS\Core\Imaging\IconRegistry; +use TYPO3\CMS\Core\Utility\ArrayUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\StringUtility; /** * Script Class for the New Content element wizard */ -class NewContentElementController +class NewContentElementController extends AbstractModule { /** * Page id @@ -72,7 +77,7 @@ class NewContentElementController /** * Internal backend template object * - * @var \TYPO3\CMS\Backend\Template\DocumentTemplate + * @var DocumentTemplate */ public $doc; @@ -117,16 +122,12 @@ class NewContentElementController */ protected $MCONF; - /** - * @var IconFactory - */ - protected $iconFactory; - /** * Constructor */ public function __construct() { + parent::__construct(); $GLOBALS['SOBE'] = $this; $this->init(); } @@ -142,7 +143,7 @@ class NewContentElementController $lang->includeLLFile('EXT:lang/locallang_misc.xlf'); $LOCAL_LANG_orig = $GLOBALS['LOCAL_LANG']; $lang->includeLLFile('EXT:backend/Resources/Private/Language/locallang_db_new_content_el.xlf'); - \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($LOCAL_LANG_orig, $GLOBALS['LOCAL_LANG']); + ArrayUtility::mergeRecursiveWithOverrule($LOCAL_LANG_orig, $GLOBALS['LOCAL_LANG']); $GLOBALS['LOCAL_LANG'] = $LOCAL_LANG_orig; // Setting internal vars: @@ -156,18 +157,17 @@ class NewContentElementController $config = BackendUtility::getPagesTSconfig($this->id); $this->config = $config['mod.']['wizards.']['newContentElement.']; // Starting the document template object: - $this->doc = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Template\DocumentTemplate::class); - $this->doc->setModuleTemplate('EXT:backend/Resources/Private/Templates/db_new_content_el.html'); - $this->doc->JScode = ''; - $this->doc->form = '<form action="" name="editForm"><input type="hidden" name="defValues" value="" />'; + // We keep this here in case somebody relies on it in a hook or alike + $this->doc = GeneralUtility::makeInstance(DocumentTemplate::class); + $this->moduleTemplate->setForm( + '<form action="" name="editForm"><input type="hidden" name="defValues" value="" />' + ); // Setting up the context sensitive menu: - $this->doc->getContextMenuCode(); + $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ClickMenu'); // Getting the current page and receiving access information (used in main()) $perms_clause = $this->getBackendUser()->getPagePermsClause(1); $this->pageInfo = BackendUtility::readPageAccess($this->id, $perms_clause); $this->access = is_array($this->pageInfo) ? 1 : 0; - - $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class); } /** @@ -181,14 +181,15 @@ class NewContentElementController public function mainAction(ServerRequestInterface $request, ResponseInterface $response) { $this->main(); - - $response->getBody()->write($this->content); + $this->moduleTemplate->setContent($this->content); + $response->getBody()->write($this->moduleTemplate->renderContent()); return $response; } /** * Creating the module output. * + * @throws \UnexpectedValueException * @return void */ public function main() @@ -206,14 +207,20 @@ class NewContentElementController } else { $row = ''; } - $this->onClickEvent = $posMap->onClickInsertRecord($row, $this->colPos, '', $this->uid_pid, $this->sys_language); + $this->onClickEvent = $posMap->onClickInsertRecord( + $row, + $this->colPos, + '', + $this->uid_pid, + $this->sys_language + ); } else { $this->onClickEvent = ''; } // *************************** // Creating content // *************************** - $this->content .= $this->doc->header($lang->getLL('newContentElement')); + $this->content .= '<h1>' . $lang->getLL('newContentElement') . '</h1>'; // Wizard $wizardItems = $this->wizardArray(); // Wrapper for wizards @@ -222,14 +229,19 @@ class NewContentElementController if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms']['db_new_content_el']['wizardItemsHook'])) { foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms']['db_new_content_el']['wizardItemsHook'] as $classData) { $hookObject = GeneralUtility::getUserObj($classData); - if (!$hookObject instanceof \TYPO3\CMS\Backend\Wizard\NewContentElementWizardHookInterface) { - throw new \UnexpectedValueException('$hookObject must implement interface ' . \TYPO3\CMS\Backend\Wizard\NewContentElementWizardHookInterface::class, 1227834741); + if (!$hookObject instanceof NewContentElementWizardHookInterface) { + throw new \UnexpectedValueException( + '$hookObject must implement interface ' . NewContentElementWizardHookInterface::class, + 1227834741 + ); } $hookObject->manipulateWizardItems($wizardItems, $this); } } // Add document inline javascript - $this->doc->JScode = $this->doc->wrapScriptTags(' + $this->moduleTemplate->addJavaScriptCode( + 'NewContentElementWizardInlineJavascript', + ' function goToalt_doc() { // ' . $this->onClickEvent . ' } @@ -238,8 +250,8 @@ class NewContentElementController top.refreshMenu(); } else { top.TYPO3ModuleMenu.refreshMenu(); - } - '); + }' + ); $iconRegistry = GeneralUtility::makeInstance(IconRegistry::class); @@ -271,8 +283,7 @@ class NewContentElementController GeneralUtility::deprecationLog('The PageTS-Config: mod.wizards.newContentElement.wizardItems.*.elements.*.icon' . ' is deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8.' . ' Register your icon in IconRegistry::registerIcon and use the new setting:' - . ' mod.wizards.newContentElement.wizardItems.*.elements.*.iconIdentifier' - ); + . ' mod.wizards.newContentElement.wizardItems.*.elements.*.iconIdentifier'); $wInfo['iconIdentifier'] = 'content-' . $k; $icon = $wInfo['icon']; if (StringUtility::beginsWith($icon, '../typo3conf/ext/')) { @@ -285,7 +296,7 @@ class NewContentElementController 'source' => $icon )); } - $icon = $this->iconFactory->getIcon($wInfo['iconIdentifier'])->render(); + $icon = $this->moduleTemplate->getIconFactory()->getIcon($wInfo['iconIdentifier'])->render(); $menuItems[$key]['content'] .= ' <div class="media"> <a href="#" onclick="' . htmlspecialchars($aOnClick) . '"> @@ -308,73 +319,64 @@ class NewContentElementController $menuItems[$key]['content'] .= $this->elementWrapper['section'][1]; } // Add the wizard table to the content, wrapped in tabs - $code = '<p>' . $lang->getLL('sel1', 1) . '</p>' . $this->doc->getDynamicTabMenu($menuItems, 'new-content-element-wizard'); - - $this->content .= $this->doc->section(!$this->onClickEvent ? $lang->getLL('1_selectType') : '', $code, 0, 1); + $code = '<p>' . $lang->getLL('sel1', 1) . '</p>' . $this->moduleTemplate->getDynamicTabMenu( + $menuItems, + 'new-content-element-wizard' + ); + + $this->content .= $this->moduleTemplate->section( + !$this->onClickEvent ? $lang->getLL('1_selectType') : '', + $code, + 0, + 1 + ); // If the user must also select a column: if (!$this->onClickEvent) { // Add anchor "sel2" - $this->content .= $this->doc->section('', '<a name="sel2"></a>'); + $this->content .= $this->moduleTemplate->section('', '<a name="sel2"></a>'); // Select position $code = '<p>' . $lang->getLL('sel2', 1) . '</p>'; // Load SHARED page-TSconfig settings and retrieve column list from there, if applicable: - $colPosArray = GeneralUtility::callUserFunction(\TYPO3\CMS\Backend\View\BackendLayoutView::class . '->getColPosListItemsParsed', $this->id, $this); + $colPosArray = GeneralUtility::callUserFunction( + BackendLayoutView::class . '->getColPosListItemsParsed', + $this->id, + $this + ); $colPosIds = array_column($colPosArray, 1); // Removing duplicates, if any $colPosList = implode(',', array_unique(array_map('intval', $colPosIds))); // Finally, add the content of the column selector to the content: $code .= $posMap->printContentElementColumns($this->id, 0, $colPosList, 1, $this->R_URI); - $this->content .= $this->doc->section($lang->getLL('2_selectPosition'), $code, 0, 1); + $this->content .= $this->moduleTemplate->section($lang->getLL('2_selectPosition'), $code, 0, 1); } } else { // In case of no access: $this->content = ''; - $this->content .= $this->doc->header($lang->getLL('newContentElement')); - $this->content .= $this->doc->spacer(5); + $this->content .= '<h1>' . $lang->getLL('newContentElement') . '</h1>'; } // Setting up the buttons and markers for docheader - $docHeaderButtons = $this->getButtons(); - $markers['CSH'] = $docHeaderButtons['csh']; - $markers['CONTENT'] = $this->content; - // Build the <body> for the module - $this->content = $this->doc->startPage($lang->getLL('newContentElement')); - $this->content .= $this->doc->moduleBody($this->pageInfo, $docHeaderButtons, $markers); - $this->content .= $this->doc->sectionEnd(); - $this->content .= $this->doc->endPage(); - $this->content = $this->doc->insertStylesAndJS($this->content); - } - - /** - * Print out the accumulated content: - * - * @return void - * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8, use mainAction() instead - */ - public function printContent() - { - GeneralUtility::logDeprecatedFunction(); - echo $this->content; + $this->getButtons(); } /** * Create the panel of buttons for submitting the form or otherwise perform operations. - * - * @return array All available buttons as an assoc. array */ protected function getButtons() { - $buttons = array( - 'csh' => '', - 'back' => '' - ); - if ($this->id && $this->access) { - $buttons['csh'] = BackendUtility::cshItem('xMOD_csh_corebe', 'new_ce'); - if ($this->R_URI) { - $buttons['back'] = '<a href="' . htmlspecialchars($this->R_URI) . '" class="typo3-goBack" title="' . $this->getLanguageService()->getLL('goBack', true) . '">' . $this->iconFactory->getIcon('actions-view-go-back', Icon::SIZE_SMALL)->render() . '</a>'; - } + $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar(); + if ($this->R_URI) { + $backButton = $buttonBar->makeLinkButton() + ->setHref($this->R_URI) + ->setTitle($this->getLanguageService()->getLL('goBack', true)) + ->setIcon($this->moduleTemplate->getIconFactory()->getIcon( + 'actions-view-go-back', + Icon::SIZE_SMALL + )); + $buttonBar->addButton($backButton); } - return $buttons; + $cshButton = $buttonBar->makeHelpButton()->setModuleName('xMOD_csh_corebe')->setFieldName('new_ce'); + $buttonBar->addButton($cshButton); } /*************************** @@ -494,7 +496,8 @@ class NewContentElementController /** * Checks the array for elements which might contain unallowed default values and will unset them! - * Looks for the "tt_content_defValues" key in each element and if found it will traverse that array as fieldname / value pairs and check. + * Looks for the "tt_content_defValues" key in each element and if found it will traverse that array as fieldname / + * value pairs and check. * The values will be added to the "params" key of the array (which should probably be unset or empty by default). * * @param array $wizardItems Wizard items, passed by reference @@ -517,9 +520,7 @@ class NewContentElementController // (in case remaining parameters are around). if (is_array($tempGetVars['defVals']['tt_content'])) { $wizardItems[$key]['tt_content_defValues'] = array_merge( - is_array($wizardItems[$key]['tt_content_defValues']) - ? $wizardItems[$key]['tt_content_defValues'] - : array(), + is_array($wizardItems[$key]['tt_content_defValues']) ? $wizardItems[$key]['tt_content_defValues'] : array(), $tempGetVars['defVals']['tt_content'] ); unset($tempGetVars['defVals']['tt_content']); @@ -538,10 +539,18 @@ class NewContentElementController && !$backendUser->checkAuthMode('tt_content', $fN, $fV, $config['authMode']); // explode TSconfig keys only as needed if (!isset($removeItems[$fN])) { - $removeItems[$fN] = GeneralUtility::trimExplode(',', $TCEFORM_TSconfig[$fN]['removeItems'], true); + $removeItems[$fN] = GeneralUtility::trimExplode( + ',', + $TCEFORM_TSconfig[$fN]['removeItems'], + true + ); } if (!isset($keepItems[$fN])) { - $keepItems[$fN] = GeneralUtility::trimExplode(',', $TCEFORM_TSconfig[$fN]['keepItems'], true); + $keepItems[$fN] = GeneralUtility::trimExplode( + ',', + $TCEFORM_TSconfig[$fN]['keepItems'], + true + ); } $isNotInKeepItems = !empty($keepItems[$fN]) && !in_array($fV, $keepItems[$fN]); if ($authModeDeny || $fN === 'CType' && in_array($fV, $removeItems[$fN]) || $isNotInKeepItems) { diff --git a/typo3/sysext/backend/Classes/Template/ModuleTemplate.php b/typo3/sysext/backend/Classes/Template/ModuleTemplate.php index a551fe537bd6d968b590489a8e9648f8d339b0b3..0f60921964e8e421d35089aaeb62bd128c42e2da 100644 --- a/typo3/sysext/backend/Classes/Template/ModuleTemplate.php +++ b/typo3/sysext/backend/Classes/Template/ModuleTemplate.php @@ -395,6 +395,57 @@ class ModuleTemplate } } + /** + * Creates a DYNAMIC tab-menu where the tabs or collapsible are rendered with bootstrap markup + * + * @param array $menuItems Numeric array where each entry is an array in itself with associative keys: "label" + * contains the label for the TAB, "content" contains the HTML content that goes into the + * div-layer of the tabs content. "description" contains description text to be shown in the + * layer. "linkTitle" is short text for the title attribute of the tab-menu link (mouse-over + * text of tab). "stateIcon" indicates a standard status icon (see ->icon(), + * values: -1, 1, 2, 3). "icon" is an image tag placed before the text. + * @param string $identString Identification string. This should be unique for every instance of a dynamic menu! + * @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, $identString, $defaultTabIndex = 1, $collapsible = false, $wrapContent = true, $storeLastActiveTab = true) + { + $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Tabs'); + $templatePathAndFileName = 'EXT:backend/Resources/Private/Templates/DocumentTemplate/' . ($collapsible ? 'Collapse.html' : 'Tabs.html'); + $view = GeneralUtility::makeInstance(StandaloneView::class); + $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templatePathAndFileName)); + $view->assignMultiple(array( + 'id' => $this->getDynTabMenuId($identString), + 'items' => $menuItems, + 'defaultTabIndex' => $defaultTabIndex, + 'wrapContent' => $wrapContent, + 'storeLastActiveTab' => $storeLastActiveTab, + 'BACK_PATH' => $GLOBALS['BACK_PATH'] + )); + return $view->render(); + } + + /** + * Creates the id for dynTabMenus. + * + * @param string $identString Identification string. This should be unique for every instance of a dynamic menu! + * @return string The id with a short MD5 of $identString and prefixed "DTM-", like "DTM-2e8791854a + */ + public function getDynTabMenuId($identString) + { + $id = 'DTM-' . GeneralUtility::shortMD5($identString); + return $id; + } + /******************************************* @@ -602,9 +653,8 @@ class ModuleTemplate */ public function getVersionSelector($id, $noAction = false) { - if ( - ExtensionManagementUtility::isLoaded('version') && - !ExtensionManagementUtility::isLoaded('workspaces') + if (ExtensionManagementUtility::isLoaded('version') + && !ExtensionManagementUtility::isLoaded('workspaces') ) { /** * For Code Completion diff --git a/typo3/sysext/backend/Resources/Private/Templates/db_new_content_el.html b/typo3/sysext/backend/Resources/Private/Templates/db_new_content_el.html deleted file mode 100644 index 8d0bd28e46301cf3b5afb68d5bae9c88c632686c..0000000000000000000000000000000000000000 --- a/typo3/sysext/backend/Resources/Private/Templates/db_new_content_el.html +++ /dev/null @@ -1,31 +0,0 @@ -<!-- ###FULLDOC### begin --> -<div class="typo3-fullDoc"> - <div id="typo3-docheader"> - <div class="typo3-docheader-functions"> - <div class="left">###CSH###</div> - <div class="right">###PAGEPATH######PAGEINFO###</div> - </div> - <div class="typo3-docheader-buttons"> - <div class="left">###BUTTONLIST_LEFT###</div> - <div class="right">###BUTTONLIST_RIGHT###</div> - </div> - </div> - - <div id="typo3-docbody"> - <div id="typo3-inner-docbody"> - ###CONTENT### - </div> - </div> -</div> -<!-- ###FULLDOC### end --> - -<!-- ###BUTTON_GROUP_WRAP### --> -<div class="buttongroup">###BUTTONS###</div> -<!-- ###BUTTON_GROUP_WRAP### --> - -<!-- ###BUTTON_GROUPS_LEFT### --> -<!-- ###BUTTON_GROUP1### -->###BACK###<!-- ###BUTTON_GROUP1### --> -<!-- ###BUTTON_GROUPS_LEFT### --> - -<!-- ###BUTTON_GROUPS_RIGHT### --> -<!-- ###BUTTON_GROUPS_RIGHT### -->