Index: t3lib/class.t3lib_compressor.php =================================================================== --- t3lib/class.t3lib_compressor.php (Revision 7483) +++ t3lib/class.t3lib_compressor.php (Arbeitskopie) @@ -66,13 +66,20 @@ // we remove BACK_PATH from $filename, so make it relative to TYPO3_mainDir $filenameFromMainDir = substr($filename, strlen($GLOBALS['BACK_PATH'])); // if $options['baseDirectories'] set, we only include files below these directroies - if (!isset($options['baseDirectories']) || $this->checkBaseDirectory($filenameFromMainDir, $options['baseDirectories'])) { + if (( + !isset($options['baseDirectories']) + || $this->checkBaseDirectory($filenameFromMainDir, array_merge($options['baseDirectories'], array($this->targetDirectory))) + ) && ( + $fileOptions['media'] === 'all' + ) + ) + { $filesToInclude[] = $filenameFromMainDir; // remove the file from the incoming file array unset($cssFiles[$filename]); } } - + if (count($filesToInclude)) { $targetFile = $this->createMergedCssFile($filesToInclude); $concatenatedOptions = array( @@ -100,27 +107,147 @@ $unique = ''; foreach ($filesToInclude as $filename) { - $unique .= $filename . filemtime($GLOBALS['BACK_PATH'] . $filename) . filesize($GLOBALS['BACK_PATH'] . $filename); + $unique .= $filename . + filemtime(t3lib_div::resolveBackPath(PATH_typo3 . $filename)) . + filesize(t3lib_div::resolveBackPath(PATH_typo3 . $filename)); } $targetFile = $this->targetDirectory . TYPO3_MODE . '-'. md5($unique) . '.css'; // if the file doesn't already exist, we create it - if (!file_exists($targetFile)) { + if (!file_exists(PATH_site . $targetFile)) { $concatenated = ''; // concatenate all the files together foreach ($filesToInclude as $filename) { - $contents = file_get_contents($GLOBALS['BACK_PATH'] . $filename); - $concatenated .= $this->cssFixRelativeUrlPaths($contents, dirname($filename) . '/', $this->targetDirectory); + $contents = t3lib_div::getUrl(t3lib_div::resolveBackPath(PATH_typo3 . $filename)); + // only fix paths if files aren't compressed already (and therefore fixed) + if(!is_int(strpos($filename, $this->targetDirectory))) { + $concatenated .= $this->cssFixRelativeUrlPaths($contents, dirname($filename) . '/'); + } else { + $concatenated .= $contents; + } } + t3lib_div::writeFile(PATH_site . $targetFile, $concatenated); - if (strlen($concatenated)) { - t3lib_div::writeFile(PATH_site . $targetFile, $concatenated); + } + return $targetFile; + } + + /** + * Compress multiple css files + * + * @param array $cssFiles The files to compress (array key = filename) + * @param array $options Additional options + * @return array The CSS files after compression (array key = new filename) + */ + public function compressCssFiles(array $cssFiles, $options) { + $filesCompressed = array(); + $leaveUncompressed = array(); + foreach ($cssFiles as $filename => $fileOptions) { + // we remove BACK_PATH from $filename, so make it relative to TYPO3_mainDir + $filenameFromMainDir = substr($filename, strlen($GLOBALS['BACK_PATH'])); + // if $options['baseDirectories'] set, we only include files below these directroies + if (!isset($options['baseDirectories']) + || $this->checkBaseDirectory($filenameFromMainDir, array_merge($options['baseDirectories'], array($this->targetDirectory))) + ) { + $filesCompressed[$GLOBALS['BACK_PATH'] . '../' . $this->compressCssFile($filename)] = $fileOptions; + // remove the file from the incoming file array + unset($cssFiles[$filename]); + } else { + // leave files not in the stylesheet directories uncompressed + $leaveUncompressed[$filename] = $fileOptions; } } + return array_merge($filesCompressed, $leaveUncompressed); + } + + /** + * Compresses a CSS file + * + * Options: + * baseDirectories If set, only include files below one of the base directories + * + * 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 + */ + public function compressCssFile($filename) { + // generate the unique name of the file + $filenameFromMainDir = substr($filename, strlen($GLOBALS['BACK_PATH'])); + $unique = $filenameFromMainDir . filemtime($filename) . filesize($filename); + $targetFile = $this->targetDirectory . TYPO3_MODE . '-compressed-' . md5($unique) . '.css'; + // only create it, if it doesn't exist, yet + if (!file_exists(PATH_site . $targetFile)) { + $contents = t3lib_div::getUrl(t3lib_div::resolveBackPath(PATH_typo3 . $filename)); + // Perform some safe CSS optimizations. + $contents = str_replace("\r", '', $contents); // Strip any and all carriage returns. + // Match and process strings, comments and everything else, one chunk at a time. + // To understand this regex, read: "Mastering Regular Expressions 3rd Edition" chapter 6. + $contents = preg_replace_callback('% + # One-regex-to-rule-them-all! - version: 20100220_0100 + # Group 1: Match a double quoted string. + ("[^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+") | # or... + # Group 2: Match a single quoted string. + (\'[^\'\\\\]*+(?:\\\\.[^\'\\\\]*+)*+\') | # or... + # Group 3: Match a regular non-MacIE5-hack comment. + (/\*[^\\\\*]*+\*++(?:[^\\\\*/][^\\\\*]*+\*++)*+/) | # or... + # Group 4: Match a MacIE5-type1 comment. + (/\*(?:[^*\\\\]*+\**+(?!/))*+\\\\[^*]*+\*++(?:[^*/][^*]*+\*++)*+/(?cssFixRelativeUrlPaths($contents, dirname($filename) . '/'); + + t3lib_div::writeFile(PATH_site . $targetFile, $contents); + } return $targetFile; } /** + * Callback function for preg_replace + * + * @see compressCssFile + * @param array $matches + * @return string the compressed string + */ + public static function compressCssPregCallback($matches) { + if ($matches[1]) { // Group 1: Double quoted string. + return $matches[1]; // Return the string unmodified. + } elseif ($matches[2]) { // Group 2: Single quoted string. + return $matches[2]; // Return the string unmodified. + } elseif ($matches[3]) { // Group 3: Regular non-MacIE5-hack comment. + return "\n"; // Return single space. + } elseif ($matches[4]) { // Group 4: MacIE5-hack-type-1 comment. + return "\n/*\\T1*/\n"; // Return minimal MacIE5-hack-type-1 comment. + } + elseif ($matches[5]) { // Group 5,6,7: MacIE5-hack-type-2 comment + $matches[6] = preg_replace('/\s++([+>{};,)])/S', '$1', $matches[6]); // Clean pre-punctuation. + $matches[6] = preg_replace('/([+>{}:;,(])\s++/S', '$1', $matches[6]); // Clean post-punctuation. + $matches[6] = preg_replace('/;?\}/S', "}\n", $matches[6]); // Add a touch of formatting. + return "\n/*T2\\*/" . $matches[6] . "\n/*T2E*/\n"; // Minify and reassemble composite type2 comment. + } elseif (isset($matches[8])) { // Group 8: Non-string, non-comment. Safe to clean whitespace here. + $matches[8] = preg_replace('/^\s++/', '', $matches[8]); // Strip all leading whitespace. + $matches[8] = preg_replace('/\s++$/', '', $matches[8]); // Strip all trailing whitespace. + $matches[8] = preg_replace('/\s{2,}+/', ' ', $matches[8]); // Consolidate multiple whitespace. + $matches[8] = preg_replace('/\s++([+>{};,)])/S', '$1', $matches[8]); // Clean pre-punctuation. + $matches[8] = preg_replace('/([+>{}:;,(])\s++/S', '$1', $matches[8]); // Clean post-punctuation. + $matches[8] = preg_replace('/;?\}/S', "}\n", $matches[8]); // Add a touch of formatting. + return $matches[8]; + } + return $matches[0] . "\n/* ERROR! Unexpected _proccess_css_minify() parameter */\n"; // never get here + } + + /** * Decides whether a CSS file comes from one of the baseDirectories * * @param string $filename Filename @@ -129,7 +256,7 @@ protected function checkBaseDirectory($filename, array $baseDirectories) { foreach ($baseDirectories as $baseDirectory) { // check, if $filename starts with $skinStylesheetDirectory (it's position 0, not FALSE!) - if (strpos($filename, $baseDirectory) === 0) { + if (is_int(strpos($filename, $baseDirectory))) { return TRUE; } } @@ -141,18 +268,18 @@ * * @param string $contents Data to process * @param string $oldDir Directory of the originial file, relative to TYPO3_mainDir - * @param string $newDir Directory of the resulting file * @return string Processed data */ - protected function cssFixRelativeUrlPaths($contents, $oldDir, $newDir) { + protected function cssFixRelativeUrlPaths($contents, $oldDir) { $matches = array(); - preg_match_all('/url[\s]*\([\'\"]?(.*)[\'\"]?\)/iU', $contents, $matches); - foreach ($matches[1] as $match) { + + preg_match_all('/url(\(\s*["\']?([^"\']+)["\']?\s*\))/iU', $contents, $matches); + foreach ($matches[2] as $matchCount => $match) { // remove '," or white-spaces around $match = preg_replace('/[\"\'\s]/', '', $match); + $newPath = t3lib_div::resolveBackPath('../../' . TYPO3_mainDir . $oldDir . $match); - $newPath = t3lib_div::resolveBackPath('../../' . TYPO3_mainDir . $oldDir . $match); - $contents = str_replace($match, $newPath, $contents); + $contents = str_replace($matches[1][$matchCount], '(\'' . $newPath . '\')', $contents); } return $contents; } Index: t3lib/class.t3lib_pagerenderer.php =================================================================== --- t3lib/class.t3lib_pagerenderer.php (Revision 7483) +++ t3lib/class.t3lib_pagerenderer.php (Arbeitskopie) @@ -1306,8 +1306,7 @@ protected function doConcatenate() { // traverse the arrays, concatenate in one file // then remove concatenated files from array and add the concatenated file - - + if ($this->concatenateFiles) { $params = array ( 'jsLibs' => &$this->jsLibs, @@ -1365,8 +1364,7 @@ } } } - - if ($this->compressCss && $GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssCompressHandler']) { + if ($this->compressCss) { // use extern compress routine $params = array ( 'cssInline' => &$this->cssInline, @@ -1374,10 +1372,14 @@ 'headerData' => &$this->headerData, 'footerData' => &$this->footerData, ); - t3lib_div::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssCompressHandler'], $params, $this); - } else { - if ($this->compressCss) { - // own method, nothing implemented atm + + if ($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssCompressHandler']) { + // 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'); + $cssOptions = array('baseDirectories' => $GLOBALS['TBE_TEMPLATE']->getSkinStylesheetDirectories()); + $this->cssFiles = $compressor->compressCssFiles($this->cssFiles, $cssOptions); } } } Index: typo3/template.php =================================================================== --- typo3/template.php (Revision 7483) +++ typo3/template.php (Arbeitskopie) @@ -305,6 +305,7 @@ ); $this->pageRenderer->setLanguage($GLOBALS['LANG']->lang); $this->pageRenderer->enableConcatenateFiles(); + $this->pageRenderer->enableCompressCss(); } return $this->pageRenderer; }