Fixed bug #14277: Improve t3lib_compressor (thanks to Steffen Gebert)
authorSteffen Kamper <info@sk-typo3.de>
Sun, 2 May 2010 15:14:10 +0000 (15:14 +0000)
committerSteffen Kamper <info@sk-typo3.de>
Sun, 2 May 2010 15:14:10 +0000 (15:14 +0000)
git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@7508 709f56b5-9817-0410-a4d7-c38de5d9e867

ChangeLog
misc/advanced.htaccess
t3lib/class.t3lib_compressor.php
t3lib/class.t3lib_pagerenderer.php
typo3/backend.php
typo3/template.php

index 0841a1d..42ed811 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,6 @@
 2010-05-02  Steffen Kamper  <info@sk-typo3.de>
 
+       * Fixed bug #14277: Improve t3lib_compressor (thanks to Steffen Gebert)
        * Fixed bug #14279: Some non-XHTML tags in Backend (thanks to Steffen Gebert)
 
 2010-05-02  Sebastian Kurfuerst  <sebastian@typo3.org>
index b49fc3f..ef23b49 100644 (file)
 # 
 ####
 
+##
+# Compressed .js and .css files
+##
+# uncomment the following lines if you use compression
+#
+#<FilesMatch "\.js\.gzip$">
+#  AddType "text/javascript" .gzip
+#</FilesMatch>
+#<FilesMatch "\.css\.gzip$">
+#  AddType "text/css" .gzip
+#</FilesMatch>
+#AddEncoding gzip .gzip
+
+
 ### Begin: Rewrite stuff ###
 
 # Enable URL rewriting
index 74bf065..06e26cc 100644 (file)
@@ -27,7 +27,7 @@
 
 /**
  * Compressor
- * This class can currently merge CSS files of the TYPO3 Backend.
+ * This merges and compresses CSS and JavaScript files of the TYPO3 Backend.
  *
  * @author     Steffen Gebert <steffen@steffen-gebert.de>
  * @package TYPO3
@@ -63,6 +63,17 @@ class t3lib_compressor {
                                $this->gzipCompressionLevel = $compressionLevel;
                        }
                }
+
+                       // decide whether we should create gzipped versions or not
+               $compressionLevel = $GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['compressionLevel'];
+                       // we need zlib for gzencode()
+               if (extension_loaded('zlib') && $compressionLevel) {
+                       $this->createGzipped = TRUE;
+                               // $compressionLevel can also be TRUE
+                       if (t3lib_div::testInt($compressionLevel)) {
+                               $this->gzipCompressionLevel = $compressionLevel;
+                       }
+               }
        }
 
        /**
@@ -71,7 +82,7 @@ class t3lib_compressor {
         * Options:
         *   baseDirectories            If set, only include files below one of the base directories
         *
-        * @param       array   $cssFiles               CSS files added to the PageRenderer
+        * @param       array   $cssFiles               CSS files to process
         * @param       array   $options                Additional options
         * @return      array   CSS files
         */
@@ -144,8 +155,8 @@ class t3lib_compressor {
        /**
         * Compress multiple css files
         *
-        * @param array $cssFiles       The files to compress (array key = filename)
-        * @return array                        The CSS files after compression (array key = new filename)
+        * @param array $cssFiles       The files to compress (array key = filename), relative to requested page
+        * @return array                        The CSS files after compression (array key = new filename), relative to requested page
         */
        public function compressCssFiles(array $cssFiles) {
                $filesAfterCompression = array();
@@ -154,7 +165,7 @@ class t3lib_compressor {
                        $filenameFromMainDir = substr($filename, strlen($GLOBALS['BACK_PATH']));
                                // if compression is enabled
                        if ($fileOptions['compress']) {
-                               $filesAfterCompression[$GLOBALS['BACK_PATH'] . $this->compressCssFile($filename)] = $fileOptions;
+                               $filesAfterCompression[$this->compressCssFile($filename)] = $fileOptions;
                        } else {
                                $filesAfterCompression[$filename] = $fileOptions;
                        }
@@ -171,8 +182,8 @@ class t3lib_compressor {
         * removes comments and whitespaces
         * Adopted from http://drupal.org/files/issues/minify_css.php__1.txt
         *
-        * @param       string  $filename               Source filename
-        * @return      string          Filename of the compressed file
+        * @param       string  $filename               Source filename, relative to requested page
+        * @return      string          Compressed filename, relative to requested page
         */
        public function compressCssFile($filename) {
                        // generate the unique name of the file
@@ -218,7 +229,7 @@ class t3lib_compressor {
                        $this->writeFileAndCompressed($targetFile, $contents);
                }
 
-               return '../' . $this->returnFileReference($targetFile);
+               return $GLOBALS['BACK_PATH'] . '../' . $this->returnFileReference($targetFile);
        }
 
        /**
@@ -256,6 +267,51 @@ class t3lib_compressor {
        }
 
        /**
+        * Compress multiple javascript files
+        *
+        * @param       array   $jsFiles                The files to compress (array key = filename), relative to requested page
+        * @return      array           The js files after compression (array key = new filename), relative to requested page
+               */
+       public function compressJsFiles(array $jsFiles) {
+               $filesAfterCompression = array();
+               foreach ($jsFiles as $filename => $fileOptions) {
+                               // we remove BACK_PATH from $filename, so make it relative to TYPO3_mainDir
+                       $filenameFromMainDir = substr($filename, strlen($GLOBALS['BACK_PATH']));
+                               // if compression is enabled
+                       if ($fileOptions['compress']) {
+                               $filesAfterCompression[$this->compressJsFile($filename)] = $fileOptions;
+                       } else {
+                               $filesAfterCompression[$filename] = $fileOptions;
+                       }
+               }
+               return $filesAfterCompression;
+       }
+
+       /**
+        * Compresses a javascript file
+        *
+        * Options:
+        *   baseDirectories            If set, only include files below one of the base directories
+        *
+        * @param       string  $filename               Source filename, relative to requested page
+        * @return      string          Filename of the compressed file, relative to requested page
+        */
+       public function compressJsFile($filename) {
+                       // generate the unique name of the file
+               $filenameAbsolute = t3lib_div::resolveBackPath(PATH_typo3 . substr($filename, strlen($GLOBALS['BACK_PATH'])));
+               $unique = $filenameAbsolute . filemtime($filenameAbsolute) . filesize($filenameAbsolute);
+
+               $pathinfo = pathinfo($filename);
+               $targetFile = $this->targetDirectory . $pathinfo['filename'] . '-' . md5($unique) . '.js';
+                       // only create it, if it doesn't exist, yet
+               if (!file_exists(PATH_site . $targetFile) || ($this->createGzipped && !file_exists(PATH_site . $targetFile . '.gz'))) {
+                       $contents = t3lib_div::getUrl($filenameAbsolute);
+                       $this->writeFileAndCompressed($targetFile, $contents);
+               }
+               return $GLOBALS['BACK_PATH'] . '../' . $this->returnFileReference($targetFile);
+       }
+
+       /**
         * Decides whether a CSS file comes from one of the baseDirectories
         *
         * @param       string  $filename               Filename
@@ -309,7 +365,7 @@ class t3lib_compressor {
                        if (isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['compressionLevel']) && is_numeric($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['compressionLevel'])) {
                                $compressionLevel = intval($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['compressionLevel']);
                        }
-                       t3lib_div::writeFile(PATH_site . $filename . '.gz', gzencode($contents, $compressionLevel));
+                       t3lib_div::writeFile(PATH_site . $filename . '.gzip', gzencode($contents, $compressionLevel));
                }
        }
 
@@ -323,7 +379,7 @@ class t3lib_compressor {
        protected function returnFileReference($filename) {
                        // if the client accepts gzip and we can create gzipped files, we give him compressed versions
                if ($this->createGzipped && strpos(t3lib_div::getIndpEnv('HTTP_ACCEPT_ENCODING'), 'gzip') !== FALSE) {
-                       return $filename . '.gz';
+                       return $filename . '.gzip';
                } else {
                        return $filename;
                }
index b6ce148..9781430 100644 (file)
@@ -47,6 +47,9 @@ class t3lib_PageRenderer implements t3lib_Singleton {
        protected $csConvObj;
        protected $lang;
 
+       /* @var t3lib_compressor Instance of t3lib_compressor */
+       protected $compressor;
+
        // static array containing associative array for the included files
        protected static $jsFiles = array ();
        protected static $jsFooterFiles = array ();
@@ -1200,8 +1203,8 @@ class t3lib_PageRenderer implements t3lib_Singleton {
                $out = '';
 
                if ($this->addPrototype) {
-                       $out .= '<script src="' . t3lib_div::createVersionNumberedFilename($this->backPath .
-                               'contrib/prototype/prototype.js') . '" type="text/javascript"></script>' . LF;
+                       $out .= '<script src="' . $this->processJsFile($this->backPath  . 'contrib/prototype/prototype.js') .
+                               '" type="text/javascript"></script>' . LF;
                        unset($this->jsFiles[$this->backPath . 'contrib/prototype/prototype.js']);
                }
 
@@ -1218,17 +1221,20 @@ class t3lib_PageRenderer implements t3lib_Singleton {
                        }
 
                        if (count($mods)) {
-                               $moduleLoadString = '?load=' . implode(',', $mods);
+                               foreach ($mods as $module) {
+                                       $out .= '<script src="' . $this->processJsFile($this->backPath .
+                                               'contrib/scriptaculous/' . $module . '.js') . '" type="text/javascript"></script>' . LF;
+                                       unset($this->jsFiles[$this->backPath . 'contrib/scriptaculous/' . $module . '.js']);
                        }
-                       $out .= '<script src="' . t3lib_div::createVersionNumberedFilename($this->backPath .
-                               'contrib/scriptaculous/scriptaculous.js' . $moduleLoadString, TRUE) .
-                               '" type="text/javascript"></script>' . LF;
-                       unset($this->jsFiles[$this->backPath . 'contrib/scriptaculous/scriptaculous.js' . $moduleLoadString]);
+               }
+                       $out .= '<script src="' . $this->processJsFile($this->backPath .
+                               'contrib/scriptaculous/scriptaculous.js') . '" type="text/javascript"></script>' . LF;
+                       unset($this->jsFiles[$this->backPath . 'contrib/scriptaculous/scriptaculous.js']);
                }
 
                        // include extCore
                if ($this->addExtCore) {
-                       $out .= '<script src="' . t3lib_div::createVersionNumberedFilename($this->backPath .
+                       $out .= '<script src="' . $this->processJsFile($this->backPath .
                                'contrib/extjs/ext-core' . ($this->enableExtCoreDebug ? '-debug' : '') . '.js') .
                                '" type="text/javascript"></script>' . LF;
                        unset($this->jsFiles[$this->backPath . 'contrib/extjs/ext-core' . ($this->enableExtCoreDebug ? '-debug' : '') . '.js']);
@@ -1237,11 +1243,11 @@ class t3lib_PageRenderer implements t3lib_Singleton {
                        // include extJS
                if ($this->addExtJS) {
                                // use the base adapter all the time
-                       $out .= '<script src="' . t3lib_div::createVersionNumberedFilename($this->backPath .
+                       $out .= '<script src="' . $this->processJsFile($this->backPath .
                                'contrib/extjs/adapter/' . ($this->enableExtJsDebug ?
                                        str_replace('.js', '-debug.js', $this->extJSadapter) : $this->extJSadapter)) .
                                '" type="text/javascript"></script>' . LF;
-                       $out .= '<script src="' . t3lib_div::createVersionNumberedFilename($this->backPath .
+                       $out .= '<script src="' . $this->processJsFile($this->backPath .
                                'contrib/extjs/ext-all' . ($this->enableExtJsDebug ? '-debug' : '') . '.js') .
                                '" type="text/javascript"></script>' . LF;
 
@@ -1258,7 +1264,7 @@ class t3lib_PageRenderer implements t3lib_Singleton {
                                // TODO autoconvert file from UTF8 to current BE charset if necessary!!!!
                        $extJsLocaleFile = 'contrib/extjs/locale/ext-lang-' . $extJsLang . '.js';
                        if (file_exists(PATH_typo3 . $extJsLocaleFile)) {
-                               $out .= '<script src="' . t3lib_div::createVersionNumberedFilename($this->backPath .
+                               $out .= '<script src="' . $this->processJsFile($this->backPath .
                                        $extJsLocaleFile) . '" type="text/javascript" charset="utf-8"></script>' . LF;
                        }
 
@@ -1302,16 +1308,16 @@ class t3lib_PageRenderer implements t3lib_Singleton {
 
                        if ($this->extJStheme) {
                                if (isset($GLOBALS['TBE_STYLES']['extJS']['theme'])) {
-                                       $this->addCssFile($this->backPath . $GLOBALS['TBE_STYLES']['extJS']['theme'], 'stylesheet', 'screen', '', FALSE, TRUE);
+                                       $this->addCssFile($this->backPath . $GLOBALS['TBE_STYLES']['extJS']['theme'], 'stylesheet', 'all', '', TRUE, TRUE);
                                } else {
-                                       $this->addCssFile($this->backPath . 'contrib/extjs/resources/css/xtheme-blue.css', 'stylesheet', 'screen', '', FALSE, TRUE);
+                                       $this->addCssFile($this->backPath . 'contrib/extjs/resources/css/xtheme-blue.css', 'stylesheet', 'all', '', TRUE, TRUE);
                                }
                        }
                        if ($this->extJScss) {
                                if (isset($GLOBALS['TBE_STYLES']['extJS']['all'])) {
-                                       $this->addCssFile($this->backPath . $GLOBALS['TBE_STYLES']['extJS']['all'], 'stylesheet', 'screen', '', FALSE, TRUE);
+                                       $this->addCssFile($this->backPath . $GLOBALS['TBE_STYLES']['extJS']['all'], 'stylesheet', 'all', '', TRUE, TRUE);
                                } else {
-                                       $this->addCssFile($this->backPath . 'contrib/extjs/resources/css/ext-all-notheme.css', 'stylesheet', 'screen', '', FALSE, TRUE);
+                                       $this->addCssFile($this->backPath . 'contrib/extjs/resources/css/ext-all-notheme.css', 'stylesheet', 'all', '', TRUE, TRUE);
                                }
                        }
                } else {
@@ -1333,7 +1339,6 @@ class t3lib_PageRenderer implements t3lib_Singleton {
        /**
         * concatenate files into one file
         * registered handler
-        * TODO: implement own method
         *
         * @return void
         */
@@ -1355,9 +1360,8 @@ class t3lib_PageRenderer implements t3lib_Singleton {
                                // use extern concatenate routine
                                t3lib_div::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['concatenateHandler'], $params, $this);
                        } elseif (TYPO3_MODE === 'BE') {
-                               $compressor = t3lib_div::makeInstance('t3lib_compressor');
                                $cssOptions = array('baseDirectories' => $GLOBALS['TBE_TEMPLATE']->getSkinStylesheetDirectories());
-                               $this->cssFiles = $compressor->concatenateCssFiles($this->cssFiles, $cssOptions);
+                               $this->cssFiles = $this->getCompressor()->concatenateCssFiles($this->cssFiles, $cssOptions);
                        }
                }
        }
@@ -1396,8 +1400,12 @@ class t3lib_PageRenderer implements t3lib_Singleton {
                                                }
                                        }
                                }
+                               if (TYPO3_MODE === 'BE') {
+                                       $this->jsFiles = $this->getCompressor()->compressJsFiles($this->jsFiles);
+                                       $this->jsFooterFiles = $this->getCompressor()->compressJsFiles($this->jsFooterFiles);
                        }
                }
+               }
                if ($this->compressCss) {
                                // use extern compress routine
                        $params = array (
@@ -1411,12 +1419,42 @@ class t3lib_PageRenderer implements t3lib_Singleton {
                                // use extern concatenate routine
                                t3lib_div::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssCompressHandler'], $params, $this);
                        } elseif (TYPO3_MODE === 'BE') {
-                               $compressor = t3lib_div::makeInstance('t3lib_compressor');
-                               $this->cssFiles = $compressor->compressCssFiles($this->cssFiles);
+                               $this->cssFiles = $this->getCompressor()->compressCssFiles($this->cssFiles);
                        }
                }
        }
 
+       /**
+        * Returns instance of t3lib_compressor
+        *
+        * @return      t3lib_compressor                Instance of t3lib_compressor
+        */
+       protected function getCompressor() {
+               if ($this->compressor === NULL) {
+                       $this->compressor = t3lib_div::makeInstance('t3lib_compressor');
+}
+               return $this->compressor;
+       }
+
+       /**
+        * Processes a Javascript file dependent on the current context
+        *
+        * Adds the version number for Frontend, compresses the file for Backend
+        *
+        * @param       string  $filename               Filename
+        * @return      string          new filename
+        */
+       protected function processJsFile($filename) {
+               switch (TYPO3_MODE) {
+                       case 'FE':
+                               $filename = t3lib_div::createVersionNumberedFilename($filename);
+                               break;
+                       case 'BE':
+                               $filename = $this->getCompressor()->compressJsFile($filename);
+                               break;
+               }
+               return $filename;
+       }
 }
 
 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_pagerenderer.php']) {
index 2676acb..376241c 100644 (file)
@@ -127,7 +127,6 @@ class TYPO3backend {
                        'js/iecompatibility.js',
                        'js/flashupload.js',
                        '../t3lib/jsfunc.evalfield.js',
-                       'ajax.php?ajaxID=ExtDirect::getAPI&namespace=TYPO3.Backend',
                        '../t3lib/js/extjs/ux/flashmessages.js',
                        'js/backend.js',
                        'js/loginrefresh.js',
@@ -238,6 +237,8 @@ class TYPO3backend {
                foreach ($this->jsFiles as $jsFile) {
                        $this->pageRenderer->addJsFile($jsFile);
                }
+                       // we mustn't compress this file
+               $this->pageRenderer->addJsFile('ajax.php?ajaxID=ExtDirect::getAPI&namespace=TYPO3.Backend', NULL, FALSE);
 
                $this->generateJavascript();
                $this->pageRenderer->addJsInlineCode('BackendInlineJavascript', $this->js);
index 9aa78af..c431e5e 100644 (file)
@@ -306,6 +306,7 @@ class template {
                        $this->pageRenderer->setLanguage($GLOBALS['LANG']->lang);
                        $this->pageRenderer->enableConcatenateFiles();
                        $this->pageRenderer->enableCompressCss();
+                       $this->pageRenderer->enableCompressJavascript();
                }
                return $this->pageRenderer;
        }