[TASK] Add CSS libs functionality for page renderer 63/27163/5
authorBenjamin Mack <benni@typo3.org>
Fri, 31 Jan 2014 10:20:47 +0000 (11:20 +0100)
committerAnja Leichsenring <aleichsenring@ab-softlab.de>
Thu, 6 Feb 2014 19:07:05 +0000 (20:07 +0100)
As with JS libraries, the PageRenderer should support
CSS files that should be included above CSS files.
This is necessary for e.g. normalize.css

The patch also adds the same procedure for
"includeCSSLibs" as with "includeCSSLibs" for the
frontend.

Releases: 6.2
Resolves: #55474
Change-Id: I3451b8170daa4fc8cf08f4f96771bed53ac04cdc
Reviewed-on: https://review.typo3.org/27163
Reviewed-by: Wouter Wolters
Tested-by: Wouter Wolters
Reviewed-by: Anja Leichsenring
Tested-by: Anja Leichsenring
typo3/sysext/core/Classes/Page/PageRenderer.php
typo3/sysext/core/Resources/Private/Templates/PageRenderer.html
typo3/sysext/frontend/Classes/Page/PageGenerator.php

index ee6775e..a29d0cf 100644 (file)
@@ -146,6 +146,11 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface {
        protected $cssFiles = array();
 
        /**
+        * @var array
+        */
+       protected $cssLibs = array();
+
+       /**
         * The title of the page
         *
         * @var string
@@ -1528,6 +1533,36 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface {
        }
 
        /**
+        * Adds CSS file
+        *
+        * @param string $file
+        * @param string $rel
+        * @param string $media
+        * @param string $title
+        * @param boolean $compress
+        * @param boolean $forceOnTop
+        * @param string $allWrap
+        * @param boolean $excludeFromConcatenation
+        * @param string $splitChar The char used to split the allWrap value, default is "|"
+        * @return void
+        */
+       public function addCssLibrary($file, $rel = 'stylesheet', $media = 'all', $title = '', $compress = TRUE, $forceOnTop = FALSE, $allWrap = '', $excludeFromConcatenation = FALSE, $splitChar = '|') {
+               if (!isset($this->cssLibs[$file])) {
+                       $this->cssLibs[$file] = array(
+                               'file' => $file,
+                               'rel' => $rel,
+                               'media' => $media,
+                               'title' => $title,
+                               'compress' => $compress,
+                               'forceOnTop' => $forceOnTop,
+                               'allWrap' => $allWrap,
+                               'excludeFromConcatenation' => $excludeFromConcatenation,
+                               'splitChar' => $splitChar
+                       );
+               }
+       }
+
+       /**
         * Adds CSS inline code
         *
         * @param string $name
@@ -1908,9 +1943,9 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface {
         */
        public function render($part = self::PART_COMPLETE) {
                $this->prepareRendering();
-               list($jsLibs, $jsFiles, $jsFooterFiles, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs) = $this->renderJavaScriptAndCss();
+               list($jsLibs, $jsFiles, $jsFooterFiles, $cssLibs, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs) = $this->renderJavaScriptAndCss();
                $metaTags = implode(LF, $this->metaTags);
-               $markerArray = $this->getPreparedMarkerArray($jsLibs, $jsFiles, $jsFooterFiles, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs, $metaTags);
+               $markerArray = $this->getPreparedMarkerArray($jsLibs, $jsFiles, $jsFooterFiles, $cssLibs, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs, $metaTags);
                $template = $this->getTemplateForPart($part);
                $this->reset();
                return trim(\TYPO3\CMS\Core\Html\HtmlParser::substituteMarkerArray($template, $markerArray, '###|###'));
@@ -1941,10 +1976,11 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface {
         */
        public function renderJavaScriptAndCssForProcessingOfUncachedContentObjects($cachedPageContent, $substituteHash) {
                $this->prepareRendering();
-               list($jsLibs, $jsFiles, $jsFooterFiles, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs) = $this->renderJavaScriptAndCss();
+               list($jsLibs, $jsFiles, $jsFooterFiles, $cssLibs, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs) = $this->renderJavaScriptAndCss();
                $title = $this->title ? str_replace('|', htmlspecialchars($this->title), $this->titleTag) : '';
                $markerArray = array(
                        '<!-- ###TITLE' . $substituteHash . '### -->' => $title,
+                       '<!-- ###CSS_LIBS' . $substituteHash . '### -->' => $cssLibs,
                        '<!-- ###CSS_INCLUDE' . $substituteHash . '### -->' => $cssFiles,
                        '<!-- ###CSS_INLINE' . $substituteHash . '### -->' => $cssInline,
                        '<!-- ###JS_INLINE' . $substituteHash . '### -->' => $jsInline,
@@ -1998,6 +2034,7 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface {
                        $this->doCompress();
                }
                $this->executeRenderPostTransformHook();
+               $cssLibs = $this->renderCssLibraries();
                $cssFiles = $this->renderCssFiles();
                $cssInline = $this->renderCssInline();
                list($jsLibs, $jsFooterLibs) = $this->renderAdditionalJavaScriptLibraries();
@@ -2012,8 +2049,8 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface {
                        $jsFooterInline = $jsInline . LF . $jsFooterInline;
                        $jsInline = '';
                }
-               $this->executePostRenderHook($jsLibs, $jsFiles, $jsFooterFiles, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs);
-               return array($jsLibs, $jsFiles, $jsFooterFiles, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs);
+               $this->executePostRenderHook($jsLibs, $jsFiles, $jsFooterFiles, $cssLibs, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs);
+               return array($jsLibs, $jsFiles, $jsFooterFiles, $cssLibs, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs);
        }
 
        /**
@@ -2022,6 +2059,7 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface {
         * @param $jsLibs string
         * @param $jsFiles string
         * @param $jsFooterFiles string
+        * @param $cssLibs string
         * @param $cssFiles string
         * @param $jsInline string
         * @param $cssInline string
@@ -2030,7 +2068,7 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface {
         * @param $metaTags string
         * @return array Marker array
         */
-       protected function getPreparedMarkerArray($jsLibs, $jsFiles, $jsFooterFiles, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs, $metaTags) {
+       protected function getPreparedMarkerArray($jsLibs, $jsFiles, $jsFooterFiles, $cssLibs, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs, $metaTags) {
                $markerArray = array(
                        'XMLPROLOG_DOCTYPE' => $this->xmlPrologAndDocType,
                        'HTMLTAG' => $this->htmlTag,
@@ -2039,6 +2077,7 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface {
                        'INLINECOMMENT' => $this->inlineComments ? LF . LF . '<!-- ' . LF . implode(LF, $this->inlineComments) . '-->' . LF . LF : '',
                        'BASEURL' => $this->baseUrl ? str_replace('|', $this->baseUrl, $this->baseUrlTag) : '',
                        'SHORTCUT' => $this->favIcon ? sprintf($this->shortcutTag, htmlspecialchars($this->favIcon), $this->iconMimeType) : '',
+                       'CSS_LIBS' => $cssLibs,
                        'CSS_INCLUDE' => $cssFiles,
                        'CSS_INLINE' => $cssInline,
                        'JS_INLINE' => $jsInline,
@@ -2075,6 +2114,7 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface {
                        'META' => implode(LF, $this->metaTags),
                        'BODY' => $this->bodyContent,
                        'TITLE' => '<!-- ###TITLE' . $substituteHash . '### -->',
+                       'CSS_LIBS' => '<!-- ###CSS_LIBS' . $substituteHash . '### -->',
                        'CSS_INCLUDE' => '<!-- ###CSS_INCLUDE' . $substituteHash . '### -->',
                        'CSS_INLINE' => '<!-- ###CSS_INLINE' . $substituteHash . '### -->',
                        'JS_INLINE' => '<!-- ###JS_INLINE' . $substituteHash . '### -->',
@@ -2228,18 +2268,18 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface {
                        if (TYPO3_MODE === 'BE') {
                                $out .= '<script src="' . $this->processJsFile(($this->backPath . 'sysext/lang/Resources/Public/JavaScript/Typo3Lang.js')) . '" type="text/javascript" charset="utf-8"></script>' . LF;
                        }
-                       if ($this->extJStheme) {
-                               if (isset($GLOBALS['TBE_STYLES']['extJS']['theme'])) {
-                                       $this->addCssFile($this->backPath . $GLOBALS['TBE_STYLES']['extJS']['theme'], 'stylesheet', 'all', '', TRUE, TRUE);
+                       if ($this->extJScss) {
+                               if (isset($GLOBALS['TBE_STYLES']['extJS']['all'])) {
+                                       $this->addCssLibrary($this->backPath . $GLOBALS['TBE_STYLES']['extJS']['all'], 'stylesheet', 'all', '', TRUE);
                                } else {
-                                       $this->addCssFile($this->backPath . $this->extJsPath . 'resources/css/xtheme-blue.css', 'stylesheet', 'all', '', TRUE, TRUE);
+                                       $this->addCssLibrary($this->backPath . $this->extJsPath . 'resources/css/ext-all-notheme.css', 'stylesheet', 'all', '', TRUE);
                                }
                        }
-                       if ($this->extJScss) {
-                               if (isset($GLOBALS['TBE_STYLES']['extJS']['all'])) {
-                                       $this->addCssFile($this->backPath . $GLOBALS['TBE_STYLES']['extJS']['all'], 'stylesheet', 'all', '', TRUE, TRUE);
+                       if ($this->extJStheme) {
+                               if (isset($GLOBALS['TBE_STYLES']['extJS']['theme'])) {
+                                       $this->addCssLibrary($this->backPath . $GLOBALS['TBE_STYLES']['extJS']['theme'], 'stylesheet', 'all', '', TRUE);
                                } else {
-                                       $this->addCssFile($this->backPath . $this->extJsPath . 'resources/css/ext-all-notheme.css', 'stylesheet', 'all', '', TRUE, TRUE);
+                                       $this->addCssLibrary($this->backPath . $this->extJsPath . 'resources/css/xtheme-blue.css', 'stylesheet', 'all', '', TRUE);
                                }
                        }
                } else {
@@ -2300,6 +2340,36 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface {
        }
 
        /**
+        * Render CSS library files
+        *
+        * @return string
+        */
+       protected function renderCssLibraries() {
+               $cssFiles = '';
+               if (count($this->cssLibs)) {
+                       foreach ($this->cssLibs as $file => $properties) {
+                               $file = GeneralUtility::resolveBackPath($file);
+                               $file = GeneralUtility::createVersionNumberedFilename($file);
+                               $tag = '<link rel="' . htmlspecialchars($properties['rel'])
+                                       . '" type="text/css" href="' . htmlspecialchars($file)
+                                       . '" media="' . htmlspecialchars($properties['media']) . '"'
+                                       . ($properties['title'] ? ' title="' . htmlspecialchars($properties['title']) . '"' : '')
+                                       . $this->endingSlash . '>';
+                               if ($properties['allWrap']) {
+                                       $wrapArr = explode($properties['splitChar'] ?: '|', $properties['allWrap'], 2);
+                                       $tag = $wrapArr[0] . $tag . $wrapArr[1];
+                               }
+                               if ($properties['forceOnTop']) {
+                                       $cssFiles = $tag . LF . $cssFiles;
+                               } else {
+                                       $cssFiles .= LF . $tag;
+                               }
+                       }
+               }
+               return $cssFiles;
+       }
+
+       /**
         * Render CSS files
         *
         * @return string
@@ -2776,6 +2846,7 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface {
         * @param $jsLibs string
         * @param $jsFiles string
         * @param $jsFooterFiles string
+        * @param $cssLibs string
         * @param $cssFiles string
         * @param $jsInline string
         * @param $cssInline string
@@ -2783,12 +2854,13 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface {
         * @param $jsFooterLibs string
         * @return void
         */
-       protected function executePostRenderHook(&$jsLibs, &$jsFiles, &$jsFooterFiles, &$cssFiles, &$jsInline, &$cssInline, &$jsFooterInline, &$jsFooterLibs) {
+       protected function executePostRenderHook(&$jsLibs, &$jsFiles, &$jsFooterFiles, &$cssLibs, &$cssFiles, &$jsInline, &$cssInline, &$jsFooterInline, &$jsFooterLibs) {
                if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-postProcess'])) {
                        $params = array(
                                'jsLibs' => &$jsLibs,
                                'jsFiles' => &$jsFiles,
                                'jsFooterFiles' => &$jsFooterFiles,
+                               'cssLibs' => &$cssLibs,
                                'cssFiles' => &$cssFiles,
                                'headerData' => &$this->headerData,
                                'footerData' => &$this->footerData,
index ca4c60b..cb64836 100644 (file)
@@ -462,27 +462,24 @@ class PageGenerator {
                        }
                }
                /**********************************************************************/
-               /* includeCSS
-               /* config.includeCSS {
-               /*
-               /* }
+               /* config.includeCSS / config.includeCSSLibs
                /**********************************************************************/
                if (is_array($GLOBALS['TSFE']->pSetup['includeCSS.'])) {
                        foreach ($GLOBALS['TSFE']->pSetup['includeCSS.'] as $key => $CSSfile) {
                                if (!is_array($CSSfile)) {
-                                       $ss = $GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['external'] ? $CSSfile : $GLOBALS['TSFE']->tmpl->getFileName($CSSfile);
-                                       if (isset($GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['if.']) && !$GLOBALS['TSFE']->cObj->checkIf($GLOBALS['TSFE']->pSetup['includeCSS.'][($key . '.')]['if.'])) {
+                                       $cssFileConfig = &$GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.'];
+                                       if (isset($cssFileConfig['if.']) && !$GLOBALS['TSFE']->cObj->checkIf($cssFileConfig['if.'])) {
                                                continue;
                                        }
+                                       $ss = $cssFileConfig['external'] ? $CSSfile : $GLOBALS['TSFE']->tmpl->getFileName($CSSfile);
                                        if ($ss) {
-                                               if ($GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['import']) {
-                                                       if (!$GLOBALS['TSFE']->pSetup['includeCSS.'][($key . '.')]['external'] && $ss[0] !== '/') {
+                                               if ($cssFileConfig['import']) {
+                                                       if (!$cssFileConfig['external'] && $ss[0] !== '/') {
                                                                // To fix MSIE 6 that cannot handle these as relative paths (according to Ben v Ende)
                                                                $ss = GeneralUtility::dirname(GeneralUtility::getIndpEnv('SCRIPT_NAME')) . '/' . $ss;
                                                        }
-                                                       $pageRenderer->addCssInlineBlock('import_' . $key, '@import url("' . htmlspecialchars($ss) . '") ' . htmlspecialchars($GLOBALS['TSFE']->pSetup['includeCSS.'][($key . '.')]['media']) . ';', empty($GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['disableCompression']), $GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.']['forceOnTop'] ? TRUE : FALSE, '');
+                                                       $pageRenderer->addCssInlineBlock('import_' . $key, '@import url("' . htmlspecialchars($ss) . '") ' . htmlspecialchars($cssFileConfig['media']) . ';', empty($cssFileConfig['disableCompression']), $cssFileConfig['forceOnTop'] ? TRUE : FALSE, '');
                                                } else {
-                                                       $cssFileConfig = &$GLOBALS['TSFE']->pSetup['includeCSS.'][$key . '.'];
                                                        $pageRenderer->addCssFile(
                                                                $ss,
                                                                $cssFileConfig['alternate'] ? 'alternate stylesheet' : 'stylesheet',
@@ -500,6 +497,40 @@ class PageGenerator {
                                }
                        }
                }
+               if (is_array($GLOBALS['TSFE']->pSetup['includeCSSLibs.'])) {
+                       foreach ($GLOBALS['TSFE']->pSetup['includeCSSLibs.'] as $key => $CSSfile) {
+                               if (!is_array($CSSfile)) {
+                                       $cssFileConfig = &$GLOBALS['TSFE']->pSetup['includeCSSLibs.'][$key . '.'];
+                                       if (isset($cssFileConfig['if.']) && !$GLOBALS['TSFE']->cObj->checkIf($cssFileConfig['if.'])) {
+                                               continue;
+                                       }
+                                       $ss = $cssFileConfig['external'] ? $CSSfile : $GLOBALS['TSFE']->tmpl->getFileName($CSSfile);
+                                       if ($ss) {
+                                               if ($cssFileConfig['import']) {
+                                                       if (!$cssFileConfig['external'] && $ss[0] !== '/') {
+                                                               // To fix MSIE 6 that cannot handle these as relative paths (according to Ben v Ende)
+                                                               $ss = GeneralUtility::dirname(GeneralUtility::getIndpEnv('SCRIPT_NAME')) . '/' . $ss;
+                                                       }
+                                                       $pageRenderer->addCssInlineBlock('import_' . $key, '@import url("' . htmlspecialchars($ss) . '") ' . htmlspecialchars($cssFileConfig['media']) . ';', empty($cssFileConfig['disableCompression']), $cssFileConfig['forceOnTop'] ? TRUE : FALSE, '');
+                                               } else {
+                                                       $pageRenderer->addCssLibrary(
+                                                               $ss,
+                                                               $cssFileConfig['alternate'] ? 'alternate stylesheet' : 'stylesheet',
+                                                               $cssFileConfig['media'] ?: 'all',
+                                                               $cssFileConfig['title'] ?: '',
+                                                               empty($cssFileConfig['disableCompression']),
+                                                               $cssFileConfig['forceOnTop'] ? TRUE : FALSE,
+                                                               $cssFileConfig['allWrap'],
+                                                               $cssFileConfig['excludeFromConcatenation'] ? TRUE : FALSE,
+                                                               $cssFileConfig['allWrap.']['splitChar']
+                                                       );
+                                                       unset($cssFileConfig);
+                                               }
+                                       }
+                               }
+                       }
+               }
+
                // Stylesheets
                $style = '';
                if ($GLOBALS['TSFE']->pSetup['insertClassesFromRTE']) {