diff --git a/cdn.basic.css.inc b/cdn.basic.css.inc index 30c1e50..e20b0a6 100644 --- a/cdn.basic.css.inc +++ b/cdn.basic.css.inc @@ -12,6 +12,12 @@ * Mostly based on drupal_get_css(). */ function _cdn_css_aggregate(&$vars) { + // Don't override Drupal core's aggregation if this page is not going to use + // a CDN anyway. + if (!cdn_check_protocol() && !cdn_check_drupal_path($_GET['q'])) { + return; + } + $output = ''; $prefix = $suffix = '' . "\n"; @@ -20,6 +26,7 @@ function _cdn_css_aggregate(&$vars) { $no_module_preprocess = ''; $no_theme_preprocess = ''; + $query_string = variable_get('css_js_query_string', '0'); $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); @@ -46,18 +53,24 @@ function _cdn_css_aggregate(&$vars) { // Only include the stylesheet if it exists. if (file_exists($file)) { if (!$preprocess || !($is_writable && $preprocess_css)) { + // Also build CSS cache files for other files; so that the file + // URLs in them are altered to point to the CDN. + $fake_types = array($type => array($file => TRUE)); + $filename = 'cdn_css_' . md5(serialize($types) . $query_string . 'cdn') .'_'. basename($file); + $file = _cdn_build_css_cache($fake_types, $filename); + // 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 .= ''."\n"; + $no_module_preprocess .= ''."\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 .= ''."\n"; + $no_theme_preprocess .= ''."\n"; } else { - $output .= ''."\n"; + $output .= ''."\n"; } } } @@ -67,10 +80,9 @@ function _cdn_css_aggregate(&$vars) { if ($is_writable && $preprocess_css) { // Prefix filename to prevent blocking by firewalls which reject files // starting with "ad*". - $query_string = variable_get('css_js_query_string', '0'); - $filename = 'css_'. md5(serialize($types) . $query_string . 'cdn') .'.css'; + $filename = 'cdn_css_'. md5(serialize($types) . $query_string . 'cdn') .'.css'; $preprocess_file = _cdn_build_css_cache($types, $filename); - $output .= ''."\n"; + $output .= ''."\n"; } } @@ -127,5 +139,27 @@ function _cdn_build_css_path($matches, $base = NULL) { $path = preg_replace('`(^|/)(?!\.\./)([^/]+)/\.\./`', '$1', $path); } - return 'url('. file_create_url($path) .')'; + return 'url('. _cdn_css_file_create_url($path) .')'; +} + +/** + * Generate CDN file URL without file_create_url() if the core patch has not + * been applied. + */ +function _cdn_css_file_create_url($path) { + if (variable_get(CDN_THEME_LAYER_FALLBACK_VARIABLE, FALSE) == TRUE) { + // Store the current path as the old path, then let cdn_file_url_alter() + // do its magic by invoking all file_url_alter hooks. When the path hasn't + // changed and is not already root-relative or protocol-relative, then + // generate a file URL as Drupal core would: prepend the base path. + $old_path = $path; + drupal_alter('file_url', $path); + if ($path == $old_path && drupal_substr($path, 0, 1) != '/' && drupal_substr($path, 0, 2) != '//') { + $path = base_path() . $path; + } + return $path; + } + else { + return file_create_url($path); + } } diff --git a/cdn.module b/cdn.module index 8e66b7a..ab256cc 100644 --- a/cdn.module +++ b/cdn.module @@ -70,15 +70,9 @@ function cdn_file_url_alter(&$path) { $mode = variable_get(CDN_MODE_VARIABLE, CDN_MODE_BASIC); $farfuture = variable_get(CDN_BASIC_FARFUTURE_VARIABLE, CDN_BASIC_FARFUTURE_DEFAULT); $stats = variable_get(CDN_STATS_VARIABLE, FALSE) && user_access(CDN_PERM_ACCESS_STATS); - $file_path_blacklist = variable_get(CDN_EXCEPTION_FILE_PATH_BLACKLIST_VARIABLE, CDN_EXCEPTION_FILE_PATH_BLACKLIST_DEFAULT); - $file_path_whitelist = variable_get(CDN_EXCEPTION_FILE_PATH_WHITELIST_VARIABLE, CDN_EXCEPTION_FILE_PATH_WHITELIST_DEFAULT); - $drupal_path_blacklist = variable_get(CDN_EXCEPTION_DRUPAL_PATH_BLACKLIST_VARIABLE, CDN_EXCEPTION_DRUPAL_PATH_BLACKLIST_DEFAULT); - $auth_users_blacklist = variable_get(CDN_EXCEPTION_AUTH_USERS_BLACKLIST_VARIABLE, CDN_EXCEPTION_AUTH_USERS_BLACKLIST_DEFAULT); $https_support = variable_get(CDN_HTTPS_SUPPORT_VARIABLE, FALSE); $maintenance_mode = variable_get('maintenance_mode', FALSE); $is_https_page = cdn_request_is_https(); - $module_blacklist = cdn_get_blacklist(); - global $user; // Don't alter file URLs when running update.php. if (defined('MAINTENANCE_MODE')) { @@ -92,35 +86,13 @@ function cdn_file_url_alter(&$path) { return; } - // If the current page is being served via HTTPS, and the CDN does not - // support HTTPS, then don't rewrite the file URL, because it would make the - // visit insecure. - if ($is_https_page && !$https_support) { + if (!cdn_check_protocol()) { return; } - - // If the current file path matches one of the blacklisted file paths, - // return immediately, except when the current file path also matches one - // of the whitelisted file paths. - if (( - drupal_match_path($path, $file_path_blacklist) - || - drupal_match_path($path, $module_blacklist) - ) - && !drupal_match_path($path, $file_path_whitelist) - ) - { - return; - } - - // If the current Drupal path matches one of the blacklisted Drupal paths, - // return immediately. - if (drupal_match_path($_GET['q'], $drupal_path_blacklist)) { + if (!cdn_check_drupal_path($_GET['q'])) { return; } - - // If logged in user, apply a secondary blacklist. - if ($user->uid > 0 && drupal_match_path($_GET['q'], $auth_users_blacklist)) { + if (!cdn_check_file($path)) { return; } @@ -690,6 +662,76 @@ function cdn_request_is_https() { } /** + * Check if the current protocol is supported by the CDN. + * + * Note: currently only checks HTTPS, in the future possibly also SPDY. + */ +function cdn_check_protocol() { + $https_support = variable_get(CDN_HTTPS_SUPPORT_VARIABLE, FALSE); + + // If the current page is being served via HTTPS, and the CDN does not + // support HTTPS, then don't rewrite the file URL, because it would make the + // visit insecure. + if (cdn_request_is_https() && !$https_support) { + return FALSE; + } + + return TRUE; +} + +/** + * Check if a Drupal path should serve files from the CDN (i.e.: is the Drupal + * path blacklisted?). + * + * @param $path + * A Drupal path. + */ +function cdn_check_drupal_path($path) { + global $user; + $blacklist = variable_get(CDN_EXCEPTION_DRUPAL_PATH_BLACKLIST_VARIABLE, CDN_EXCEPTION_DRUPAL_PATH_BLACKLIST_DEFAULT); + $auth_blacklist = variable_get(CDN_EXCEPTION_AUTH_USERS_BLACKLIST_VARIABLE, CDN_EXCEPTION_AUTH_USERS_BLACKLIST_DEFAULT); + + // Check if the Drupal path matches one of the blacklisted Drupal paths. + if (drupal_match_path($path, $blacklist)) { + return FALSE; + } + + // If logged in user, apply a secondary blacklist. + if ($user->uid > 0 && drupal_match_path($path, $auth_blacklist)) { + return FALSE; + } + + return TRUE; +} + +/** + * Check if a file should be served from the CDN. + * + * @param $path + * Path to a file; relative to the Drupal root directory. + */ +function cdn_check_file($path) { + $file_path_blacklist = variable_get(CDN_EXCEPTION_FILE_PATH_BLACKLIST_VARIABLE, CDN_EXCEPTION_FILE_PATH_BLACKLIST_DEFAULT); + $file_path_whitelist = variable_get(CDN_EXCEPTION_FILE_PATH_WHITELIST_VARIABLE, CDN_EXCEPTION_FILE_PATH_WHITELIST_DEFAULT); + $module_blacklist = cdn_get_blacklist(); + + // A file should not be served from a CDN when it matches one of the + // blacklists, except when it matches the whitelist. + if (( + drupal_match_path($path, $file_path_blacklist) + || + drupal_match_path($path, $module_blacklist) + ) + && !drupal_match_path($path, $file_path_whitelist) + ) + { + return FALSE; + } + + return TRUE; +} + +/** * Helper function to efficiently load include files for this module. */ function cdn_load_include($basename) {