summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGábor Hojtsy <gabor@hojtsy.hu>2008-01-07 19:43:29 +0000
committerGábor Hojtsy <gabor@hojtsy.hu>2008-01-07 19:43:29 +0000
commit66b7a5cb74d45c2e94b021aff1a24d3b83ca8501 (patch)
treee61ce5e2d9253e18d2835b34d4d35d45dc4587e2
parent8ea4186e71c7e50046a7f20effdec6c68125da9d (diff)
downloadbrdo-66b7a5cb74d45c2e94b021aff1a24d3b83ca8501.tar.gz
brdo-66b7a5cb74d45c2e94b021aff1a24d3b83ca8501.tar.bz2
#199946 by JirkaRybka: append a short query string to CSS and JS files, changing on upgrades, so on core/module/theme upgrades, browser caches will 'flush'
-rw-r--r--includes/common.inc61
-rw-r--r--install.php4
-rw-r--r--update.php20
3 files changed, 61 insertions, 24 deletions
diff --git a/includes/common.inc b/includes/common.inc
index 48f27bd29..fbd88e993 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -294,7 +294,6 @@ function drupal_get_destination() {
* @see drupal_get_destination()
*/
function drupal_goto($path = '', $query = NULL, $fragment = NULL, $http_response_code = 302) {
- global $update_mode;
if (isset($_REQUEST['destination'])) {
extract(parse_url(urldecode($_REQUEST['destination'])));
@@ -309,7 +308,7 @@ function drupal_goto($path = '', $query = NULL, $fragment = NULL, $http_response
// Allow modules to react to the end of the page request before redirecting.
// We do not want this while running update.php.
- if (empty($update_mode)) {
+ if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
module_invoke_all('exit', $url);
}
@@ -1707,10 +1706,16 @@ function drupal_get_css($css = NULL) {
$no_module_preprocess = '';
$no_theme_preprocess = '';
- $preprocess_css = variable_get('preprocess_css', FALSE);
+ $preprocess_css = (variable_get('preprocess_css', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update'));
$directory = file_directory_path();
$is_writable = is_dir($directory) && is_writable($directory) && (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC);
+ // A dummy query-string is added to filenames, to gain control over
+ // browser-caching. The string changes on every update or full cache
+ // flush, forcing browsers to load a new copy of the files, as the
+ // URL changed.
+ $query_string = '?'. substr(variable_get('css_js_query_string', '0'), 0, 1);
+
foreach ($css as $media => $types) {
// If CSS preprocessing is off, we still need to output the styles.
// Additionally, go through any remaining styles if CSS preprocessing is on and output the non-cached ones.
@@ -1720,22 +1725,22 @@ function drupal_get_css($css = NULL) {
// If a CSS file is not to be preprocessed and it's a module CSS file, it needs to *always* appear at the *top*,
// regardless of whether preprocessing is on or off.
if (!$preprocess && $type == 'module') {
- $no_module_preprocess .= '<link type="text/css" rel="stylesheet" media="'. $media .'" href="'. base_path() . $file .'" />'."\n";
+ $no_module_preprocess .= '<link type="text/css" rel="stylesheet" media="'. $media .'" href="'. base_path() . $file . $query_string .'" />'."\n";
}
// If a CSS file is not to be preprocessed and it's a theme CSS file, it needs to *always* appear at the *bottom*,
// regardless of whether preprocessing is on or off.
else if (!$preprocess && $type == 'theme') {
- $no_theme_preprocess .= '<link type="text/css" rel="stylesheet" media="'. $media .'" href="'. base_path() . $file .'" />'."\n";
+ $no_theme_preprocess .= '<link type="text/css" rel="stylesheet" media="'. $media .'" href="'. base_path() . $file . $query_string .'" />'."\n";
}
else {
- $output .= '<link type="text/css" rel="stylesheet" media="'. $media .'" href="'. base_path() . $file .'" />'."\n";
+ $output .= '<link type="text/css" rel="stylesheet" media="'. $media .'" href="'. base_path() . $file . $query_string .'" />'."\n";
}
}
}
}
if ($is_writable && $preprocess_css) {
- $filename = md5(serialize($types)) .'.css';
+ $filename = md5(serialize($types) . $query_string) .'.css';
$preprocess_file = drupal_build_css_cache($types, $filename);
$output .= '<link type="text/css" rel="stylesheet" media="'. $media .'" href="'. base_path() . $preprocess_file .'" />'."\n";
}
@@ -2020,8 +2025,7 @@ function drupal_add_js($data = NULL, $type = 'module', $scope = 'header', $defer
* All JavaScript code segments and includes for the scope as HTML tags.
*/
function drupal_get_js($scope = 'header', $javascript = NULL) {
- global $update_mode;
- if (empty($update_mode) && function_exists('locale_update_js_files')) {
+ if ((!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') && function_exists('locale_update_js_files')) {
locale_update_js_files();
}
@@ -2037,10 +2041,18 @@ function drupal_get_js($scope = 'header', $javascript = NULL) {
$preprocessed = '';
$no_preprocess = array('core' => '', 'module' => '', 'theme' => '');
$files = array();
- $preprocess_js = (variable_get('preprocess_js', FALSE) && empty($update_mode));
+ $preprocess_js = (variable_get('preprocess_js', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update'));
$directory = file_directory_path();
$is_writable = is_dir($directory) && is_writable($directory) && (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC);
+ // A dummy query-string is added to filenames, to gain control over
+ // browser-caching. The string changes on every update or full cache
+ // flush, forcing browsers to load a new copy of the files, as the
+ // URL changed. Files that should not be cached (see drupal_add_js())
+ // get time() as query-string instead, to enforce reload on every
+ // page request.
+ $query_string = '?'. substr(variable_get('css_js_query_string', '0'), 0, 1);
+
foreach ($javascript as $type => $data) {
if (!$data) continue;
@@ -2059,7 +2071,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL) {
// Additionally, go through any remaining scripts if JS preprocessing is on and output the non-cached ones.
foreach ($data as $path => $info) {
if (!$info['preprocess'] || !$is_writable || !$preprocess_js) {
- $no_preprocess[$type] .= '<script type="text/javascript"'. ($info['defer'] ? ' defer="defer"' : '') .' src="'. base_path() . $path . ($info['cache'] ? '' : '?'. time()) ."\"></script>\n";
+ $no_preprocess[$type] .= '<script type="text/javascript"'. ($info['defer'] ? ' defer="defer"' : '') .' src="'. base_path() . $path . ($info['cache'] ? $query_string : '?'. time()) ."\"></script>\n";
}
else {
$files[$path] = $info;
@@ -2070,7 +2082,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL) {
// Aggregate any remaining JS files that haven't already been output.
if ($is_writable && $preprocess_js && count($files) > 0) {
- $filename = md5(serialize($files)) .'.js';
+ $filename = md5(serialize($files) . $query_string) .'.js';
$preprocess_file = drupal_build_js_cache($files, $filename);
$preprocessed .= '<script type="text/javascript" src="'. base_path() . $preprocess_file .'"></script>'."\n";
}
@@ -2412,7 +2424,6 @@ function xmlrpc($url) {
function _drupal_bootstrap_full() {
static $called;
- global $update_mode;
if ($called) {
return;
@@ -2440,7 +2451,7 @@ function _drupal_bootstrap_full() {
module_load_all();
// Let all modules take action before menu system handles the request
// We do not want this while running update.php.
- if (empty($update_mode)) {
+ if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
module_invoke_all('init');
}
}
@@ -3506,6 +3517,9 @@ function drupal_implode_tags($tags) {
* exposes a hook for other modules to clear their own cache data as well.
*/
function drupal_flush_all_caches() {
+ // Change query-strings on css/js files to enforce reload for all users.
+ _drupal_flush_css_js();
+
drupal_clear_css_cache();
drupal_clear_js_cache();
drupal_rebuild_theme_registry();
@@ -3519,3 +3533,22 @@ function drupal_flush_all_caches() {
cache_clear_all('*', $table, TRUE);
}
}
+
+/**
+ * Helper function to change query-strings on css/js files.
+ *
+ * Changes the character added to all css/js files as dummy query-string,
+ * so that all browsers are forced to reload fresh files. We keep
+ * 20 characters history (FIFO) to avoid repeats, but only the first
+ * (newest) character is actually used on urls, to keep them short.
+ * This is also called from update.php.
+ */
+function _drupal_flush_css_js() {
+ $string_history = variable_get('css_js_query_string', '00000000000000000000');
+ $new_character = $string_history[0];
+ $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+ while (strpos($string_history, $new_character) !== FALSE) {
+ $new_character = $characters[mt_rand(0, strlen($characters) - 1)];
+ }
+ variable_set('css_js_query_string', $new_character . substr($string_history, 0, 19));
+}
diff --git a/install.php b/install.php
index 426493150..2b83a3c52 100644
--- a/install.php
+++ b/install.php
@@ -792,6 +792,10 @@ if (Drupal.jsEnabled) {
// Register actions declared by any modules.
actions_synchronize();
+ // Randomize query-strings on css/js files, to hide the fact that
+ // this is a new install, not upgraded yet.
+ _drupal_flush_css_js();
+
variable_set('install_profile', $profile);
}
diff --git a/update.php b/update.php
index 235eed624..3ddf10435 100644
--- a/update.php
+++ b/update.php
@@ -13,6 +13,12 @@
* be sure to open settings.php again, and change it back to its original state!
*/
+/**
+ * Global flag to identify update.php run, and so avoid various unwanted
+ * operations, such as hook_init() and hook_exit() invokes, css/js preprocessing
+ * and translation, and solve some theming issues. This flag is checked on several
+ * places in Drupal code (not just update.php).
+ */
define('MAINTENANCE_MODE', 'update');
/**
@@ -264,11 +270,7 @@ function update_batch() {
function update_finished($success, $results, $operations) {
// clear the caches in case the data has been updated.
- cache_clear_all('*', 'cache', TRUE);
- cache_clear_all('*', 'cache_page', TRUE);
- cache_clear_all('*', 'cache_filter', TRUE);
- drupal_clear_css_cache();
- drupal_clear_js_cache();
+ drupal_flush_all_caches();
$_SESSION['update_results'] = $results;
$_SESSION['update_success'] = $success;
@@ -340,6 +342,9 @@ function update_results_page() {
}
function update_info_page() {
+ // Change query-strings on css/js files to enforce reload for all users.
+ _drupal_flush_css_js();
+
update_task_list('info');
drupal_set_title('Drupal database update');
$output = '<p>Use this utility to update your database whenever a new release of Drupal or a module is installed.</p><p>For more detailed information, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.</p>';
@@ -516,11 +521,6 @@ ini_set('display_errors', FALSE);
include_once './includes/bootstrap.inc';
-// Bootstrap Drupal in a safe way, without calling hook_init() and hook_exit(),
-// to avoid possible warnings. We need to set the global variable after
-// DRUPAL_BOOTSTRAP_CONFIGURATION, which unsets globals, but before the rest.
-drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
-$update_mode = TRUE;
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
drupal_maintenance_theme();