Index: update.php =================================================================== RCS file: /cvs/drupal/drupal/update.php,v retrieving revision 1.205 diff -u -F^f -r1.205 update.php --- update.php 2 Oct 2006 11:36:17 -0000 1.205 +++ update.php 21 Nov 2006 21:27:14 -0000 @@ -785,6 +785,9 @@ function update_create_cache_tables() { update_fix_watchdog(); update_fix_sessions(); + // Clear any cached CSS files, in case any module updates its CSS as well + drupal_flush_css_cache(); + $op = isset($_REQUEST['op']) ? $_REQUEST['op'] : ''; switch ($op) { case 'Update': Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.589 diff -u -F^f -r1.589 common.inc --- includes/common.inc 21 Nov 2006 19:40:09 -0000 1.589 +++ includes/common.inc 21 Nov 2006 21:27:16 -0000 @@ -1295,10 +1295,12 @@ function drupal_add_link($attributes) { * An array of CSS files. */ function drupal_add_css($path = NULL, $type = 'module', $media = 'all') { - static $css = array('core' => array(), 'module' => array(), 'theme' => array()); + static $css = array(); - if (!is_null($path)) { - $css[$type][$path] = array('path' => $path, 'media' => $media); + // Create an array of CSS files for each media type first, since each type needs to be served + // to the browser differently. + if (isset($path)) { + $css[$media][$type][$path] = array('path' => $path, 'media' => $media); } return $css; @@ -1316,17 +1318,89 @@ function drupal_add_css($path = NULL, $t */ function drupal_get_css($css = NULL) { $output = ''; - if (is_null($css)) { + if (!isset($css)) { $css = drupal_add_css(); } + $directory_path = file_directory_path(); + + if (is_writable($directory_path) && variable_get('cache_css', FALSE) == TRUE) { + foreach ($css as $media => $types) { + $filename = md5(serialize($types)) .'.css'; + $path = $directory_path .'/css/'; + + if (!file_exists($path . $filename)) { + $filename = drupal_build_css_cache($types); + } + + $output .= ''. "\n"; + } + } + else { + foreach ($css as $media => $types) { + foreach ($types as $type) { + foreach ($type as $file) { + $output .= '' ."\n"; + } + } + } + } + + return $output; +} + +/** + * Aggregate and optimize CSS files, putting them in the files directory. + * + * @param $css + * An array of CSS files to aggregate and compress into one file. + * @return + * The name of the CSS file. + */ +function drupal_build_css_cache($css) { + $data = ''; + $filename = md5(serialize($css)) .'.css'; + + // Create the css/ within the files folder + file_check_directory(file_create_path('css'), FILE_CREATE_DIRECTORY); + + // Build aggregate CSS file foreach ($css as $type) { foreach ($type as $file) { - $output .= '\n"; + $contents = file_get_contents($file['path']); + // Return the path to either the module or theme where this CSS file originated from + $path = base_path() . substr($file['path'], 0, strrpos($file['path'], '/')) .'/'; + // Fix all paths within this CSS file, ignoring absolute paths and paths that go up a directory, ../ + $contents = preg_replace('/url\(([\'"]?)(?![a-z]+:)(?!\.\.\/)/i', 'url(\1'. $path . '\2', $contents); + // Fix all paths to misc/ + $data .= str_replace('url(../../misc/', 'url('. base_path() .'/misc/', $contents); } } - return $output; + // If any modules implement hook_compress_css, let them handle compressing + // the css, otherwise just strip out the whitespace, which can be done safely. + if (module_invoke_all('hook_compress_css')) { + foreach (module_implements('compress_css') as $module) { + $function = $module .'_compress_css'; + $function($contents); + } + } + else { + // Preform some safe CSS optimizations for removing white space + $data = str_replace(array(": ", "; ", "\n", "\t", "\r", ), array(':', ';', '', '', ''), $data); + } + + // Create the CSS file + file_save_data($data, file_create_path('css/') .'/'. $filename, FILE_EXISTS_REPLACE); + + return $filename; +} + +/** + * Delete all cached CSS files + */ +function drupal_flush_css_cache() { + file_scan_directory(file_create_path('css'), '.*', array('.', '..', 'CVS'), 'file_delete', TRUE); } /** Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.402 diff -u -F^f -r1.402 system.module --- modules/system/system.module 21 Nov 2006 20:55:35 -0000 1.402 +++ modules/system/system.module 21 Nov 2006 21:27:18 -0000 @@ -674,6 +674,14 @@ function system_page_caching_settings() '#options' => $period, '#description' => t('On high-traffic sites it can become necessary to enforce a minimum cache lifetime. The minimum cache lifetime is the minimum amount of time that will go by before the cache is emptied and recreated. A larger minimum cache lifetime offers better performance, but users will not see new content for a longer period of time.') ); + + $form['cache_css'] = array( + '#type' => 'select', + '#title' => t('Cache and compress CSS files'), + '#default_value' => variable_get('cache_css', FALSE), + '#options' => array(t('No'), t('Yes')), + '#description' => t("A lot of Drupal modules include their own CSS files. When these modules are enabled, each module's CSS file adds an additional HTTP request to the page, which can slow down the initial page load for a user browsing the site. Additionally, this additional HTTP request can increase the server load. It is recommended to only turn this option on when your site is in production, as leaving it on can interfere with theming development."), + ); return system_settings_form($form); } @@ -1102,6 +1110,12 @@ function system_settings_form_submit($fo else { drupal_set_message(t('The configuration options have been saved.')); } + + // Only clear the CSS cache if the settings on the caching paged are saved + if ($form_id == 'system_page_caching_settings') { + drupal_flush_css_cache(); + } + menu_rebuild(); } @@ -1109,6 +1123,8 @@ function system_settings_form_submit($fo * Menu callback; displays a listing of all themes. */ function system_themes() { + // Clear cached CSS files + drupal_flush_css_cache(); $themes = system_theme_data(); ksort($themes);