Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.1193 diff -u -p -r1.1193 common.inc --- includes/common.inc 24 Jul 2010 17:28:25 -0000 1.1193 +++ includes/common.inc 28 Jul 2010 21:02:18 -0000 @@ -87,6 +87,34 @@ define('JS_DEFAULT', 0); define('JS_THEME', 100); /** + * Constants defining aggregation of JavaScript and CSS files. + */ + +/** + * No aggregation. + */ +define('DRUPAL_AGGREGATION_DISABLED', 0); + +/** + * Default aggregation. The filename of the aggregate file is an md5 hash of + * the array of source file information. The last modified timestamp of each + * source file is part of this information, so when a source file is updated, it + * results in a new aggregate file being made. + */ +define('DRUPAL_AGGREGATION_ENABLED', 1); + +/** + * Optimized aggregation. The filename of the aggregate file is an md5 hash of + * the array of source file information. The last modified timestamp of each + * source file is not part of this information, so modification of source files + * does not automatically result in a new aggregate file. This saves on file + * stats, which can be significant for high traffic websites, but requires the + * site administrator to empty the aggregation cache whenever a source file is + * modified. + */ +define('DRUPAL_AGGREGATION_ENABLED_WITHOUT_STAT', 2); + +/** * Error code indicating that the request made by drupal_http_request() exceeded * the specified timeout. */ @@ -2937,8 +2965,11 @@ function drupal_group_css($css) { * @see drupal_pre_render_styles() */ function drupal_aggregate_css(&$css_groups) { - $preprocess_css = (variable_get('preprocess_css', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update')); - + $preprocess_css = variable_get('preprocess_css', DRUPAL_AGGREGATION_ENABLED); + // Force aggregation to disabled if we are in maintanance mode. + if (defined('MAINTENANCE_MODE')) { + $preprocess_css = DRUPAL_AGGREGATION_DISABLED; + } // For each group that needs aggregation, aggregate its items. foreach ($css_groups as $key => $group) { switch ($group['type']) { @@ -2946,6 +2977,19 @@ function drupal_aggregate_css(&$css_grou // the group's data property to the file path of the aggregate file. case 'file': if ($group['preprocess'] && $preprocess_css) { + // Unless aggregation is enabled without file stats, add each file's + // last modified timestamp to the information from which the aggregate + // filename will be generated. + if ($preprocess_css != DRUPAL_AGGREGATION_ENABLED_WITHOUT_STAT) { + foreach ($group['items'] as &$item) { + // Don't override information that may have been set earlier, such + // as in hook_css_alter(). + if (!isset($item['mtime']) && file_exists($item['data'])) { + $item['mtime'] = filemtime($item['data']); + } + } + unset($item); + } $css_groups[$key]['data'] = drupal_build_css_cache($group['items']); } break; @@ -3770,7 +3814,11 @@ function drupal_get_js($scope = 'header' $index = 1; $processed = array(); $files = array(); - $preprocess_js = (variable_get('preprocess_js', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update')); + $preprocess_js = variable_get('preprocess_js', DRUPAL_AGGREGATION_ENABLED); + // Force aggregation to disabled if we are in maintanance mode. + if (defined('MAINTENANCE_MODE')) { + $preprocess_js = DRUPAL_AGGREGATION_DISABLED; + } // A dummy query-string is added to filenames, to gain control over // browser-caching. The string changes on every update or full cache @@ -3858,6 +3906,19 @@ function drupal_get_js($scope = 'header' // Aggregate any remaining JS files that haven't already been output. if ($preprocess_js && count($files) > 0) { foreach ($files as $key => $file_set) { + // Unless aggregation is enabled without file stats, add each file's + // last modified timestamp to the information from which the aggregate + // filename will be generated. + if ($preprocess_js != DRUPAL_AGGREGATION_ENABLED_WITHOUT_STAT) { + foreach ($file_set as &$item) { + // Don't override information that may have been set earlier, such + // as in hook_js_alter(). + if (!isset($item['mtime']) && file_exists($item['data'])) { + $item['mtime'] = filemtime($item['data']); + } + } + unset($item); + } $uri = drupal_build_js_cache($file_set); // Only include the file if was written successfully. Errors are logged // using watchdog. Index: modules/profile/profile.test =================================================================== RCS file: /cvs/drupal/drupal/modules/profile/profile.test,v retrieving revision 1.26 diff -u -p -r1.26 profile.test --- modules/profile/profile.test 29 May 2010 11:37:33 -0000 1.26 +++ modules/profile/profile.test 28 Jul 2010 21:02:18 -0000 @@ -339,7 +339,12 @@ class ProfileTestAutocomplete extends Pr $field_html = ''; // Check that autocompletion html is found on the user's profile edit page. + // Since we're checking for autocomplete.js in the HTML source, we need to + // disable JavaScript aggregation for this request. + $preprocess_js = variable_get('preprocess_js', DRUPAL_AGGREGATION_ENABLED); + variable_set('preprocess_js', DRUPAL_AGGREGATION_DISABLED); $this->drupalGet('user/' . $this->admin_user->uid . '/edit/' . $category); + variable_set('preprocess_js', $preprocess_js); $this->assertRaw($autocomplete_html, t('Autocomplete found.')); $this->assertRaw('misc/autocomplete.js', t('Autocomplete JavaScript found.')); $this->assertRaw('class="form-text form-autocomplete"', t('Autocomplete form element class found.')); Index: modules/simpletest/tests/common.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common.test,v retrieving revision 1.116 diff -u -p -r1.116 common.test --- modules/simpletest/tests/common.test 7 Jul 2010 17:00:43 -0000 1.116 +++ modules/simpletest/tests/common.test 28 Jul 2010 21:02:18 -0000 @@ -546,6 +546,11 @@ class DrupalTagsHandlingTestCase extends * Test the Drupal CSS system. */ class CascadingStylesheetsTestCase extends DrupalWebTestCase { + /** + * Store configured value for CSS preprocessing. + */ + protected $preprocess_css = NULL; + public static function getInfo() { return array( 'name' => 'Cascading stylesheets', @@ -556,10 +561,21 @@ class CascadingStylesheetsTestCase exten function setUp() { parent::setUp('php', 'locale', 'common_test'); + + // Disable preprocessing. + $this->preprocess_css = variable_get('preprocess_css', DRUPAL_AGGREGATION_ENABLED); + variable_set('preprocess_css', DRUPAL_AGGREGATION_DISABLED); + // Reset drupal_add_css() before each test. drupal_static_reset('drupal_add_css'); } + function tearDown() { + // Restore configured value for CSS preprocessing. + variable_set('preprocess_css', $this->preprocess_css); + parent::tearDown(); + } + /** * Check default stylesheets as empty. */ @@ -1092,8 +1108,8 @@ class JavaScriptTestCase extends DrupalW parent::setUp('locale', 'simpletest', 'common_test'); // Disable preprocessing - $this->preprocess_js = variable_get('preprocess_js', 0); - variable_set('preprocess_js', 0); + $this->preprocess_js = variable_get('preprocess_js', DRUPAL_AGGREGATION_ENABLED); + variable_set('preprocess_js', DRUPAL_AGGREGATION_DISABLED); // Reset drupal_add_js() and drupal_add_library() statics before each test. drupal_static_reset('drupal_add_js'); Index: modules/simpletest/tests/common_test.module =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common_test.module,v retrieving revision 1.11 diff -u -p -r1.11 common_test.module --- modules/simpletest/tests/common_test.module 18 May 2010 06:59:46 -0000 1.11 +++ modules/simpletest/tests/common_test.module 28 Jul 2010 21:02:18 -0000 @@ -220,7 +220,8 @@ function common_test_library() { * Adds a JavaScript file and a CSS file with a query string appended. */ function common_test_js_and_css_querystring() { - drupal_add_js(drupal_get_path('module', 'node') . '/node.js?arg1=value1&arg2=value2'); - drupal_add_css(drupal_get_path('module', 'node') . '/node.css?arg1=value1&arg2=value2'); - return ''; + // It makes no sense to preprocess files with query strings. + drupal_add_js(drupal_get_path('module', 'node') . '/node.js?arg1=value1&arg2=value2', array('preprocess' => FALSE)); + drupal_add_css(drupal_get_path('module', 'node') . '/node.css?arg1=value1&arg2=value2', array('preprocess' => FALSE)); + return ''; } Index: modules/simpletest/tests/menu.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/menu.test,v retrieving revision 1.31 diff -u -p -r1.31 menu.test --- modules/simpletest/tests/menu.test 8 Jul 2010 03:41:27 -0000 1.31 +++ modules/simpletest/tests/menu.test 28 Jul 2010 21:02:18 -0000 @@ -7,6 +7,11 @@ */ class MenuRouterTestCase extends DrupalWebTestCase { + /** + * Store configured value for CSS preprocessing. + */ + protected $preprocess_css = NULL; + public static function getInfo() { return array( 'name' => 'Menu router', @@ -18,11 +23,24 @@ class MenuRouterTestCase extends DrupalW function setUp() { // Enable dummy module that implements hook_menu. parent::setUp('menu_test'); + // Make the tests below more robust by explicitly setting the default theme // and administrative theme that they expect. theme_enable(array('garland')); variable_set('theme_default', 'garland'); variable_set('admin_theme', 'seven'); + + // Several tests determine which theme got used by asserting the existence + // of a particular CSS file in the HTML source, so we disable CSS + // aggregation for these tests. + $this->preprocess_css = variable_get('preprocess_css', DRUPAL_AGGREGATION_ENABLED); + variable_set('preprocess_css', DRUPAL_AGGREGATION_DISABLED); + } + + function tearDown() { + // Restore configured value for CSS preprocessing. + variable_set('preprocess_css', $this->preprocess_css); + parent::tearDown(); } /** Index: modules/system/system.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.admin.inc,v retrieving revision 1.293 diff -u -p -r1.293 system.admin.inc --- modules/system/system.admin.inc 28 Jul 2010 02:57:36 -0000 1.293 +++ modules/system/system.admin.inc 28 Jul 2010 21:02:18 -0000 @@ -1680,15 +1680,25 @@ function system_performance_settings() { '#suffix' => '', ); $form['bandwidth_optimization']['preprocess_css'] = array( - '#type' => 'checkbox', + '#type' => 'radios', '#title' => t('Aggregate and compress CSS files into one file.'), - '#default_value' => intval(variable_get('preprocess_css', 0) && $is_writable), + '#options' => array( + DRUPAL_AGGREGATION_DISABLED => t('Disabled. This can be useful during development, but slows down page download and display.'), + DRUPAL_AGGREGATION_ENABLED => t('Normal (recommended)'), + DRUPAL_AGGREGATION_ENABLED_WITHOUT_STAT => t('Aggressive. This setting skips the normal verification of changes to source CSS files, potentially reducing disk load on high traffic website.'), + ), + '#default_value' => $is_writable ? variable_get('preprocess_css', DRUPAL_AGGREGATION_ENABLED) : 0, '#disabled' => $disabled, ); $form['bandwidth_optimization']['preprocess_js'] = array( - '#type' => 'checkbox', + '#type' => 'radios', '#title' => t('Aggregate JavaScript files into one file.'), - '#default_value' => intval(variable_get('preprocess_js', 0) && $is_writable), + '#options' => array( + DRUPAL_AGGREGATION_DISABLED => t('Disabled. This can be useful during development, but slows down page download and display.'), + DRUPAL_AGGREGATION_ENABLED => t('Normal (recommended)'), + DRUPAL_AGGREGATION_ENABLED_WITHOUT_STAT => t('Aggressive. This setting skips the normal verification of changes to source JavaScript files, potentially reducing disk load on high traffic website.'), + ), + '#default_value' => $is_writable ? variable_get('preprocess_js', DRUPAL_AGGREGATION_ENABLED) : 0, '#disabled' => $disabled, );