[TASK] Refactor getDynTabMenu function 78/37078/13
authorFrank Nägler <typo3@naegler.net>
Sat, 21 Feb 2015 19:25:10 +0000 (20:25 +0100)
committerWouter Wolters <typo3@wouterwolters.nl>
Wed, 11 Mar 2015 20:15:00 +0000 (21:15 +0100)
This patch deprecate the getDynTabMenu() function and
introduce a new function called getDynamicTabMenu()
The new function creates bootstrap HTML markup.
All places in the core was updated to use the new method.

Resolves: #65111
Releases: master
Change-Id: I0965c3cfc0b40da3ef76ef566df28c7c6ad38a71
Reviewed-on: http://review.typo3.org/37078
Reviewed-by: Benjamin Kott <info@bk2k.info>
Tested-by: Benjamin Kott <info@bk2k.info>
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Tested-by: Wouter Wolters <typo3@wouterwolters.nl>
16 files changed:
typo3/sysext/backend/Classes/Controller/ContentElement/NewContentElementController.php
typo3/sysext/backend/Classes/Form/FormEngine.php
typo3/sysext/backend/Classes/Template/DocumentTemplate.php
typo3/sysext/backend/Classes/ViewHelpers/SpriteManagerIconViewHelper.php [new file with mode: 0644]
typo3/sysext/backend/Resources/Private/Templates/DocumentTemplate/Collapse.html [new file with mode: 0644]
typo3/sysext/backend/Resources/Private/Templates/DocumentTemplate/Partials/StateIcon.html [new file with mode: 0644]
typo3/sysext/backend/Resources/Private/Templates/DocumentTemplate/Tabs.html [new file with mode: 0644]
typo3/sysext/backend/Resources/Public/JavaScript/Tabs.js [new file with mode: 0644]
typo3/sysext/core/Documentation/Changelog/master/Deprecation-65111-getDynTabMenu.rst [new file with mode: 0644]
typo3/sysext/impexp/Classes/Controller/ImportExportController.php
typo3/sysext/linkvalidator/Classes/Report/LinkValidatorReport.php
typo3/sysext/setup/Classes/Controller/SetupModuleController.php
typo3/sysext/t3skin/Resources/Private/Styles/TYPO3/_element_animation.less
typo3/sysext/t3skin/Resources/Private/Styles/TYPO3/_element_panel.less
typo3/sysext/t3skin/Resources/Private/Styles/TYPO3/_element_tab.less
typo3/sysext/t3skin/Resources/Public/Css/visual/t3skin.css

index 54078b3..ca2ce51 100644 (file)
@@ -160,7 +160,7 @@ class NewContentElementController {
                        $code = '';
                        $wizardItems = $this->getWizardItems();
                        // Wrapper for wizards
-                       $this->elementWrapper['section'] = array('<div class="contentelement-wizard panel panel-tab"><div class="panel-body">', '</div></div>');
+                       $this->elementWrapper['section'] = array('', '');
                        // Copy wrapper for tabs
                        $this->elementWrapperForTabs = $this->elementWrapper;
                        // Hook for manipulating wizardItems, wrapper, onClickEvent etc.
@@ -237,7 +237,7 @@ class NewContentElementController {
                        }
                        // Add the wizard table to the content, wrapped in tabs:
                        if ($this->config['renderMode'] == 'tabs') {
-                               $code = '<p>' . $GLOBALS['LANG']->getLL('sel1', 1) . '</p>' . $this->doc->getDynTabMenu($menuItems, 'new-content-element-wizard', FALSE, FALSE);
+                               $code = '<p>' . $GLOBALS['LANG']->getLL('sel1', 1) . '</p>' . $this->doc->getDynamicTabMenu($menuItems, 'new-content-element-wizard');
                        } else {
                                $code = '<p>' . $GLOBALS['LANG']->getLL('sel1', 1) . '</p>';
                                foreach ($menuItems as $section) {
index 21febee..90d6858 100644 (file)
@@ -1592,12 +1592,12 @@ class FormEngine {
        /**
         * Create dynamic tab menu
         *
-        * @param array $parts Parts for the tab menu, fed to template::getDynTabMenu()
-        * @param string $idString ID string for the tab menu
+        * @param array $menuItems Items for the tab menu, fed to template::getDynTabMenu()
+        * @param string $identString ID string for the tab menu
         * @param int $dividersToTabsBehaviour If set to '1' empty tabs will be removed, If set to '2' empty tabs will be disabled, deprecated, and not in use anymore since TYPO3 CMS 7
         * @return string HTML for the menu
         */
-       public function getDynTabMenu($parts, $idString, $dividersToTabsBehaviour = -1) {
+       public function getDynTabMenu($menuItems, $identString, $dividersToTabsBehaviour = -1) {
                // if the third (obsolete) parameter is used, throw a deprecation warning
                if ($dividersToTabsBehaviour !== -1) {
                        GeneralUtility::deprecationLog('The parameter $dividersToTabsBehaviour in FormEngine::getDynTabMenu is deprecated. Please remove this option from your code');
@@ -1605,16 +1605,18 @@ class FormEngine {
                $docTemplate = $this->getDocumentTemplate();
                if (is_object($docTemplate)) {
                        $docTemplate->backPath = '';
-                       return $docTemplate->getDynTabMenu($parts, $idString, 0, FALSE, 1, FALSE, 1);
+                       return $docTemplate->getDynamicTabMenu($menuItems, $identString, 1, FALSE, FALSE);
                } else {
                        $output = '';
-                       foreach ($parts as $singlePad) {
-                               $output .= '
-                               <h3>' . htmlspecialchars($singlePad['label']) . '</h3>
-                               ' . ($singlePad['description'] ? '<p class="c-descr">' . nl2br(htmlspecialchars($singlePad['description'])) . '</p>' : '') . '
-                               ' . $singlePad['content'];
+                       foreach ($menuItems as $menuItem) {
+                               if (!empty($menuItem['content'])) {
+                                       $output .= '
+                                       <h3>' . htmlspecialchars($menuItem['label']) . '</h3>
+                                       ' . ($menuItem['description'] ? '<p>' . nl2br(htmlspecialchars($menuItem['description'])) . '</p>' : '') . '
+                                       ' . $menuItem['content'];
+                               }
                        }
-                       return '<div class="tab-content">' . $output . '</div>';
+                       return $output;
                }
        }
 
index 71d5957..a739f34 100644 (file)
@@ -20,6 +20,7 @@ use TYPO3\CMS\Core\Html\HtmlParser;
 use TYPO3\CMS\Core\Page\PageRenderer;
 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Fluid\View\StandaloneView;
 
 /**
  * TYPO3 Backend Template Class
@@ -1582,6 +1583,33 @@ function jumpToUrl(URL) {
        }
 
        /**
+        * Creates a DYNAMIC tab-menu where the tabs or collapseable 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 $collapseable If set, the tabs are rendered as headers instead over each sheet. Effectively this means there is no tab menu, but rather a foldout/foldin 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, $collapseable = FALSE, $wrapContent = TRUE, $storeLastActiveTab = TRUE) {
+               $this->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/Tabs');
+               $templatePathAndFileName = 'EXT:backend/Resources/Private/Templates/DocumentTemplate/' . ($collapseable ? '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 a DYNAMIC tab-menu where the tabs are switched between with DHTML.
         * Should work in MSIE, Mozilla, Opera and Konqueror. On Konqueror I did find a serious problem: <textarea> fields loose their content when you switch tabs!
         *
@@ -1594,104 +1622,11 @@ function jumpToUrl(URL) {
         * @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 int $tabBehaviour If set to '1' empty tabs will be remove, If set to '2' empty tabs will be disabled. setting this option to '2' is deprecated since TYPO3 CMS 7, and will be removed iwth CMS 8
         * @return string JavaScript section for the HTML header.
+        * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
         */
        public function getDynTabMenu($menuItems, $identString, $toggle = 0, $foldout = FALSE, $noWrap = TRUE, $fullWidth = FALSE, $defaultTabIndex = 1, $tabBehaviour = 1) {
-               if ($tabBehaviour === 2) {
-                       GeneralUtility::deprecationLog('DocumentTemplate::getDynTabMenu parameter $tabBehavior (=2) with showing empty disabled since TYPO3 CMS 7, and will not be supported anymore with CMS 8');
-               }
-               // Load the static code, if not already done with the function below
-               $this->loadJavascriptLib('sysext/backend/Resources/Public/JavaScript/tabmenu.js');
-               $content = '';
-               if (is_array($menuItems)) {
-                       // Init:
-                       $options = array(array());
-                       $divs = array();
-                       $JSinit = array();
-                       $id = $this->getDynTabMenuId($identString);
-
-                       // Traverse menu items
-                       $c = 0;
-                       $tabRows = 0;
-                       $titleLenCount = 0;
-                       foreach ($menuItems as $index => $def) {
-                               // Need to add one so checking for first index in JavaScript
-                               // is different than if it is not set at all.
-                               $index += 1;
-                               // Switch to next tab row if needed
-                               if (!$foldout && ($def['newline'] === TRUE && $titleLenCount > 0)) {
-                                       $titleLenCount = 0;
-                                       $tabRows++;
-                                       $options[$tabRows] = array();
-                               }
-                               if ($toggle == 1) {
-                                       $onclick = 'DTM_toggle("' . $id . '","' . $index . '"); return false;';
-                               } else {
-                                       $onclick = 'DTM_activate("' . $id . '","' . $index . '", ' . ($toggle < 0 ? 1 : 0) . '); return false;';
-                               }
-                               $isEmpty = trim($def['content']) === '' && trim($def['icon']) === '';
-                               // "Removes" empty tabs
-                               if ($isEmpty && $tabBehaviour == 1) {
-                                       continue;
-                               }
-                               $requiredIcon = '<img name="' . $id . '-' . $index . '-REQ" src="' . $GLOBALS['BACK_PATH'] . 'gfx/clear.gif" class="t3-TCEforms-reqTabImg" alt="" />';
-                               if (!$foldout) {
-                                       // Create TAB cell:
-                                       $options[$tabRows][] = '
-                                                       <li class="' . ($isEmpty ? 'disabled' : '') . '" id="' . $id . '-' . $index . '-MENU">' . ($isEmpty ? '' : '<a href="#" onclick="' . htmlspecialchars($onclick) . '"' . ($def['linkTitle'] ? ' title="' . htmlspecialchars($def['linkTitle']) . '"' : '') . '>') . $def['icon'] . ($def['label'] ? htmlspecialchars($def['label']) : '&nbsp;') . $requiredIcon . $this->icons($def['stateIcon'], 'margin-left: 10px;') . ($isEmpty ? '' : '</a>') . '</li>';
-                                       $titleLenCount += strlen($def['label']);
-                               } else {
-                                       // Create DIV layer for content:
-                                       $divs[] = '
-                                               <div class="' . ($isEmpty ? 'disabled' : '') . '" id="' . $id . '-' . $index . '-MENU">' . ($isEmpty ? '' : '<a href="#" onclick="' . htmlspecialchars($onclick) . '"' . ($def['linkTitle'] ? ' title="' . htmlspecialchars($def['linkTitle']) . '"' : '') . '>') . $def['icon'] . ($def['label'] ? htmlspecialchars($def['label']) : '&nbsp;') . $requiredIcon . ($isEmpty ? '' : '</a>') . '</div>';
-                               }
-                               // Create DIV layer for content:
-                               $divs[] = '
-                                               <div id="' . $id . '-' . $index . '-DIV" class="tab-pane">' . ($def['description'] ? '<p class="c-descr">' . nl2br(htmlspecialchars($def['description'])) . '</p>' : '') . $def['content'] . '</div>';
-                               // Create initialization string:
-                               $JSinit[] = '
-                                               DTM_array["' . $id . '"][' . $c . '] = "' . $id . '-' . $index . '";
-                               ';
-                               // If not empty and we have the toggle option on, check if the tab needs to be expanded
-                               if ($toggle == 1 && !$isEmpty) {
-                                       $JSinit[] = '
-                                               if (top.DTM_currentTabs["' . $id . '-' . $index . '"]) { DTM_toggle("' . $id . '","' . $index . '",1); }
-                                       ';
-                               }
-                               $c++;
-                       }
-                       // Render menu:
-                       if (count($options)) {
-                               // Tab menu is compiled:
-                               if (!$foldout) {
-                                       $tabContent = '';
-                                       for ($a = 0; $a <= $tabRows; $a++) {
-                                               $tabContent .= '
-
-                                       <!-- Tab menu -->
-                                       <ul class="nav nav-tabs" role="tablist">
-                                               ' . implode('', $options[$a]) . '
-                                       </ul>';
-                                       }
-                                       $content .= $tabContent;
-                               }
-                               // Div layers are added:
-                               $content .= '
-                               <!-- Div layers for tab menu: -->
-                               <div class="tab-content' . ($foldout ? ' tab-content-foldout' : '') . '">
-                               ' . implode('', $divs) . '</div>';
-                               // Java Script section added:
-                               $content .= '
-                               <!-- Initialization JavaScript for the menu -->
-                               <script type="text/javascript">
-                                       DTM_array["' . $id . '"] = new Array();
-                                       ' . implode('', $JSinit) . '
-                                       ' . ($toggle <= 0 ? 'DTM_activate("' . $id . '", top.DTM_currentTabs["' . $id . '"]?top.DTM_currentTabs["' . $id . '"]:' . (int)$defaultTabIndex . ', 0);' : '') . '
-                               </script>
-
-                               ';
-                       }
-               }
-               return $content;
+               GeneralUtility::logDeprecatedFunction();
+               return $this->getDynamicTabMenu($menuItems, $identString, $defaultTabIndex, $foldout, $noWrap);
        }
 
        /**
diff --git a/typo3/sysext/backend/Classes/ViewHelpers/SpriteManagerIconViewHelper.php b/typo3/sysext/backend/Classes/ViewHelpers/SpriteManagerIconViewHelper.php
new file mode 100644 (file)
index 0000000..e421f52
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+namespace TYPO3\CMS\Backend\ViewHelpers;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Backend\Utility\IconUtility;
+use TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface;
+use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
+use TYPO3\CMS\Fluid\Core\ViewHelper\Facets\CompilableInterface;
+
+/**
+ * Displays sprite icon identified by iconName key
+ *
+ * @author Felix Kopp <felix-source@phorax.com>
+ * @internal
+ */
+class SpriteManagerIconViewHelper extends AbstractViewHelper implements CompilableInterface {
+
+       /**
+        * Prints sprite icon html for $iconName key
+        *
+        * @param string $iconName
+        * @param array $options
+        * @return string
+        */
+       public function render($iconName, $options = array()) {
+               return self::renderStatic(array('iconName' => $iconName, 'options' => $options), $this->buildRenderChildrenClosure(), $this->renderingContext);
+       }
+
+       /**
+        * Print sprite icon html for $iconName key
+        *
+        * @param array $arguments
+        * @param \Closure $renderChildrenClosure
+        * @param RenderingContextInterface $renderingContext
+        * @return string
+        */
+       static public function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) {
+               $iconName = $arguments['iconName'];
+               $options = $arguments['options'];
+               return IconUtility::getSpriteIcon($iconName, $options);
+       }
+
+}
diff --git a/typo3/sysext/backend/Resources/Private/Templates/DocumentTemplate/Collapse.html b/typo3/sysext/backend/Resources/Private/Templates/DocumentTemplate/Collapse.html
new file mode 100644 (file)
index 0000000..3d63bfc
--- /dev/null
@@ -0,0 +1,40 @@
+<div class="panel-group" id="{id}" role="tablist" aria-multiselectable="true">
+       <f:for each="{items}" as="item" iteration="iteration">
+               <f:if condition="{item.content}">
+                       <div class="panel panel-default">
+                               <div class="panel-heading" role="tab" id="{id}-{iteration.cycle}-heading">
+                                       <a data-toggle="collapse" title="{item.linkTitle}" data-parent="#{id}" href="#{id}-{iteration.cycle}" aria-expanded="true" aria-controls="{id}-{iteration.cycle}">
+                                               <f:if condition="{item.icon}">
+                                                       <f:format.raw>{item.icon}</f:format.raw>
+                                               </f:if>
+                                               {item.label}
+                                               <img name="{id}-{iteration.cycle}-REQ" src="{BACK_PATH}gfx/clear.gif" class="t3-TCEforms-reqTabImg" alt="" />
+                                               <f:if condition="{item.requiredIcon}">
+                                                       <f:format.raw>{item.requiredIcon}</f:format.raw>
+                                               </f:if>
+                                               <f:if condition="{item.stateIcon}">
+                                                       <f:render partial="StateIcon" arguments="{item: item}" />
+                                               </f:if>
+                                       </a>
+                               </div>
+                               <div id="{id}-{iteration.cycle}" class="panel-collapse collapse{f:if(condition: '{iteration.cycle} == {defaultTabIndex}', then: ' in')}" role="tabpanel" aria-labelledby="{id}-{iteration.cycle}-heading">
+                                       <f:if condition="{item.description}">
+                                               <div class="panel-body">
+                                                       <p><f:format.nl2br>{item.description}</f:format.nl2br></p>
+                                               </div>
+                                       </f:if>
+                                       <f:if condition="{wrapContent}">
+                                               <f:then>
+                                                       <div class="panel-body">
+                                                               <f:format.raw>{item.content}</f:format.raw>
+                                                       </div>
+                                               </f:then>
+                                               <f:else>
+                                                       <f:format.raw>{item.content}</f:format.raw>
+                                               </f:else>
+                                       </f:if>
+                               </div>
+                       </div>
+               </f:if>
+       </f:for>
+</div>
diff --git a/typo3/sysext/backend/Resources/Private/Templates/DocumentTemplate/Partials/StateIcon.html b/typo3/sysext/backend/Resources/Private/Templates/DocumentTemplate/Partials/StateIcon.html
new file mode 100644 (file)
index 0000000..beedc5c
--- /dev/null
@@ -0,0 +1,7 @@
+{namespace be = TYPO3\CMS\Backend\ViewHelpers}
+<f:switch expression="{item.stateIcon}">
+       <f:case value="-1"><be:spriteManagerIcon iconName="status-dialog-ok" /></f:case>
+       <f:case value="1"><be:spriteManagerIcon iconName="status-dialog-notification" /></f:case>
+       <f:case value="2"><be:spriteManagerIcon iconName="status-dialog-warning" /></f:case>
+       <f:case value="3"><be:spriteManagerIcon iconName="status-dialog-error" /></f:case>
+</f:switch>
diff --git a/typo3/sysext/backend/Resources/Private/Templates/DocumentTemplate/Tabs.html b/typo3/sysext/backend/Resources/Private/Templates/DocumentTemplate/Tabs.html
new file mode 100644 (file)
index 0000000..d88297b
--- /dev/null
@@ -0,0 +1,50 @@
+<div role="tabpanel">
+       <ul class="nav nav-tabs t3js-tabs" role="tablist" id="tabs-{id}" data-store-last-tab="{storeLastActiveTab}">
+               <f:for each="{items}" as="item" iteration="iteration">
+                       <f:if condition="{item.content}">
+                               <li role="presentation"{f:if(condition: '{iteration.cycle} == {defaultTabIndex}', then: ' class="active"')}>
+                                       <a href="#{id}-{iteration.cycle}" title="{item.linkTitle}" aria-controls="{id}-{iteration.cycle}" role="tab" data-toggle="tab">
+                                               <f:if condition="{item.icon}">
+                                                       <f:format.raw>{item.icon}</f:format.raw>
+                                               </f:if>
+                                               {item.label}
+                                               <img name="{id}-{iteration.cycle}-REQ" src="{BACK_PATH}gfx/clear.gif" class="t3-TCEforms-reqTabImg" alt="" />
+                                               <f:if condition="{item.requiredIcon}">
+                                                       <f:format.raw>{item.requiredIcon}</f:format.raw>
+                                               </f:if>
+                                               <f:if condition="{item.stateIcon}">
+                                                       <f:render partial="StateIcon" arguments="{item: item}" />
+                                               </f:if>
+                                       </a>
+                               </li>
+                       </f:if>
+               </f:for>
+       </ul>
+       <div class="tab-content">
+               <f:for each="{items}" as="item" iteration="iteration">
+                       <f:if condition="{item.content}">
+                               <div role="tabpanel" class="tab-pane{f:if(condition: '{iteration.cycle} == {defaultTabIndex}', then: ' active')}" id="{id}-{iteration.cycle}">
+                                       <f:if condition="{item.description}">
+                                               <div class="panel panel-tab">
+                                                       <div class="panel-body">
+                                                               <p><f:format.nl2br>{item.description}</f:format.nl2br></p>
+                                                       </div>
+                                               </div>
+                                       </f:if>
+                                       <f:if condition="{wrapContent}">
+                                               <f:then>
+                                                       <div class="panel panel-tab">
+                                                               <div class="panel-body">
+                                                                       <f:format.raw>{item.content}</f:format.raw>
+                                                               </div>
+                                                       </div>
+                                               </f:then>
+                                               <f:else>
+                                                       <f:format.raw>{item.content}</f:format.raw>
+                                               </f:else>
+                                       </f:if>
+                               </div>
+                       </f:if>
+               </f:for>
+       </div>
+</div>
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/Tabs.js b/typo3/sysext/backend/Resources/Public/JavaScript/Tabs.js
new file mode 100644 (file)
index 0000000..f21f366
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * This class handle the tabs in the TYPO3 backend.
+ * It stores the last active tab and open it again after a reload,
+ */
+define('TYPO3/CMS/Backend/Tabs', ['jquery', 'TYPO3/CMS/Backend/Storage'], function ($) {
+
+       /**
+        * Tabs helper
+        *
+        * @type {{storage: (Storage.Client|*), cacheTimeInSeconds: number, storeLastActiveTab: number}}
+        */
+       var Tabs = {
+               storage: top.TYPO3.Storage.Client,
+               // cache liftime in seconds
+               cacheTimeInSeconds: 1800,
+               storeLastActiveTab: 1
+       };
+
+       /**
+        * initialize Tabs Helper
+        */
+       Tabs.initialize = function() {
+               $('.t3js-tabs').each(function() {
+                       var $tabContainer = $(this);
+                       Tabs.storeLastActiveTab = $tabContainer.data('store-last-tab') == '1' ? 1 : 0;
+                       $tabContainer.find('a[href="' + Tabs.receiveActiveTab($tabContainer.attr('id')) + '"]').tab('show');
+                       $tabContainer.on('show.bs.tab', function(e) {
+                               if (Tabs.storeLastActiveTab == 1) {
+                                       var id = e.currentTarget.id;
+                                       var target = e.target.hash;
+                                       Tabs.storeActiveTab(id, target);
+                               }
+                       });
+               });
+       };
+
+       /**
+        * receive active tab from storage
+        *
+        * @param id
+        * @returns {string}
+        */
+       Tabs.receiveActiveTab = function(id) {
+               var target = Tabs.storage.get(id) || '';
+               var expire = Tabs.storage.get(id + '.expire') || 0;
+               if (expire > Tabs.getTimestamp()) {
+                       return target;
+               }
+               return '';
+       };
+
+       /**
+        * store active tab in storage
+        *
+        * @param id
+        * @param target
+        */
+       Tabs.storeActiveTab = function(id, target) {
+               Tabs.storage.set(id, target);
+               Tabs.storage.set(id + '.expire', Tabs.getTimestamp() + Tabs.cacheTimeInSeconds);
+       };
+
+       /**
+        * get unixtimestamp
+        *
+        * @returns {number}
+        */
+       Tabs.getTimestamp = function() {
+               return Math.round((new Date()).getTime() / 1000);
+       };
+
+       /**
+        * return the Tabs object
+        */
+       return function() {
+               Tabs.initialize();
+               return Tabs;
+       }();
+});
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-65111-getDynTabMenu.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-65111-getDynTabMenu.rst
new file mode 100644 (file)
index 0000000..b20a1c6
--- /dev/null
@@ -0,0 +1,27 @@
+===================================
+Deprecation: #65111 - getDynTabMenu
+===================================
+
+Description
+===========
+
+The DocumentTemplate method ``getDynTabMenu()`` is deprecated.
+
+
+Impact
+======
+
+The method was refactored and renamed. The new method ``getDynamicTabMenu()`` should be used.
+The method ``getDynTabMenu()`` is now deprecated.
+
+
+Affected installations
+======================
+
+All installations which make use of ``DocumentTemplate::getDynTabMenu()``
+
+
+Migration
+=========
+
+Use ``DocumentTemplate::getDynamicTabMenu()`` instead of ``DocumentTemplate::getDynTabMenu()``
index f213487..e086a14 100644 (file)
@@ -440,7 +440,8 @@ class ImportExportController extends \TYPO3\CMS\Backend\Module\BaseScriptClass {
                        'stateIcon' => $errors ? 2 : 0
                );
                // Add hidden fields and create tabs:
-               $content = $this->doc->getDynTabMenu($menuItems, 'tx_impexp_export', -1);
+
+               $content = $this->doc->getDynamicTabMenu($menuItems, 'tx_impexp_export', 1, FALSE, TRUE, FALSE);
                $content .= '<input type="hidden" name="tx_impexp[action]" value="export" />';
                $this->content .= $this->doc->section('', $content, 0, 1);
                // Output Overview:
@@ -1149,7 +1150,7 @@ class ImportExportController extends \TYPO3\CMS\Backend\Module\BaseScriptClass {
                                'stateIcon' => $errors ? 2 : 0
                        );
                        // Output tabs:
-                       $content = $this->doc->getDynTabMenu($menuItems, 'tx_impexp_import', -1);
+                       $content = $this->doc->getDynamicTabMenu($menuItems, 'tx_impexp_import', 1, FALSE, TRUE, FALSE);
                        if ($extensionInstallationMessage) {
                                $content = '<div style="border: 1px black solid; margin: 10px 10px 10px 10px; padding: 10px 10px 10px 10px;">'
                                        . $this->doc->icons(1) . htmlspecialchars($extensionInstallationMessage) . '</div>' . $content;
index e07ffc1..c770e1b 100644 (file)
@@ -213,7 +213,7 @@ class LinkValidatorReport extends \TYPO3\CMS\Backend\Module\AbstractFunctionModu
                        );
                }
 
-               return $this->doc->getDynTabMenu($menuItems, 'ident');
+               return $this->doc->getDynamicTabMenu($menuItems, 'report-linkvalidator');
        }
 
        /**
index ee36e4c..2f27298 100644 (file)
@@ -404,7 +404,7 @@ class SetupModuleController {
 
                // Render the menu items
                $menuItems = $this->renderUserSetup();
-               $this->content .= $this->doc->getDynTabMenu($menuItems, 'user-setup', FALSE, FALSE, 1, FALSE, 1, 1);
+               $this->content .= $this->doc->getDynamicTabMenu($menuItems, 'user-setup', 1, FALSE, FALSE);
                $formToken = $this->formProtection->generateToken('BE user setup', 'edit');
                $this->content .= $this->doc->section('', '<input type="hidden" name="simUser" value="' . $this->simUser . '" />
                        <input type="hidden" name="formToken" value="' . $formToken . '" />
index 0492f28..2d16ec8 100644 (file)
                .transition-property(~"width, visibility");
        }
 }
+
+//
+// Hotfix display collapse always to prevent RTE initialisation problems
+//
+.collapse {
+       display: block;
+       height: 0;
+       overflow: hidden;
+       &.in {
+               height: auto;
+       }
+}
index fa80a8e..6152040 100644 (file)
        border: 1px solid @nav-tabs-active-link-hover-border-color;
        background-color: @nav-tabs-active-link-bg;
 }
-.panel-tab + .panel-tab {
-       margin-top: -19px;
+.panel-tab + .panel-tab,
+.panel-tab + .form-section {
+       margin-top: -(@line-height-computed+1);
 }
 .tab-pane {
        > .panel-tab:first-child {
index 47a2e48..3ab849b 100644 (file)
@@ -5,6 +5,12 @@
        background-color: darken(@nav-tabs-active-link-bg, 5%);
        > li {
                > a {
+                       // Hotfix to prevent prototype / scriptaculous hiding the links
+                       // this can be removed after prototype / scriptaculous removed completely.
+                       // at the moment prototype / scriptaculous hide each tab after clicking on it.
+                       // at the moment this seems to be the only way to fix the wrong behavior.
+                       display: block!important;
+
                        margin-right: 3px;
                        &:hover {
                                background: @nav-tabs-link-hover-bg;
@@ -79,4 +85,4 @@ div.typo3-dyntabmenu-divs-foldout div.disabled {
 div.typo3-dyntabmenu-divs-foldout div.disabled:hover {
        background: #f6eab7;
        color: #999;
-}
\ No newline at end of file
+}
index 34ba989..0640fac 100644 (file)
@@ -7514,6 +7514,14 @@ button.close {
   -webkit-transition-property: width, visibility;
   transition-property: width, visibility;
 }
+.collapse {
+  display: block;
+  height: 0;
+  overflow: hidden;
+}
+.collapse.in {
+  height: auto;
+}
 table#typo3-clipboard {
   background-color: #efeff4;
   border: 1px solid #a2aab8;
@@ -8542,6 +8550,7 @@ span.spinner {
   background-color: #ededed;
 }
 .nav-tabs > li > a {
+  display: block!important;
   margin-right: 3px;
 }
 .nav-tabs > li > a:hover {
@@ -8887,7 +8896,8 @@ fieldset[disabled] .table .btn-default.active {
   border: 1px solid #cccccc;
   background-color: #fafafa;
 }
-.panel-tab + .panel-tab {
+.panel-tab + .panel-tab,
+.panel-tab + .form-section {
   margin-top: -19px;
 }
 .tab-pane > .panel-tab:first-child {