'advagg_missing_css',
'type' => MENU_CALLBACK,
'access callback' => TRUE,
'file path' => $file_path,
'file' => 'advagg.missing.inc',
);
$items[$js_path . '/%'] = array(
'page callback' => 'advagg_missing_js',
'type' => MENU_CALLBACK,
'access callback' => TRUE,
'file path' => $file_path,
'file' => 'advagg.missing.inc',
);
$items['admin/settings/advagg'] = array(
'title' => 'Advanced CSS/JS Aggregation',
'description' => 'Configuration for Advanced CSS/JS Aggregation.',
'page callback' => 'advagg_admin_page',
'type' => MENU_NORMAL_ITEM,
'access arguments' => array('administer site configuration'),
'file path' => $file_path,
'file' => 'advagg.admin.inc',
);
$items['admin/settings/advagg/config'] = array(
'title' => 'Configuration',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => 0,
);
$items['admin/settings/advagg/info'] = array(
'title' => 'Information',
'description' => 'More detailed information about advagg.',
'page callback' => 'advagg_admin_info_page',
'type' => MENU_LOCAL_TASK,
'access arguments' => array('administer site configuration'),
'file path' => $file_path,
'file' => 'advagg.admin.inc',
);
$items['admin_menu/flush-cache/advagg'] = array(
'page callback' => 'advagg_admin_flush_cache',
'type' => MENU_CALLBACK,
'access arguments' => array('administer site configuration'),
'file path' => $file_path,
'file' => 'advagg.admin.inc',
);
return $items;
}
/**
* Implementation of hook_admin_menu().
*
* Add in a cache flush for advagg.
*/
function advagg_admin_menu(&$deleted) {
$links = array();
$links[] = array(
'title' => 'Adv CSS/JS Agg',
'path' => 'admin_menu/flush-cache/advagg',
'query' => 'destination',
'parent_path' => 'admin_menu/flush-cache',
);
return $links;
}
/**
* Implementation of hook_admin_menu_output_alter().
*
* Add in a cache flush for advagg.
*/
function advagg_admin_menu_output_alter(&$content) {
if (!empty($content['icon']['icon']['flush-cache']['#access']) && !empty($content['icon']['icon']['flush-cache']['requisites']) && empty($content['icon']['icon']['flush-cache']['advagg'])) {
$content['icon']['icon']['flush-cache']['advagg'] = $content['icon']['icon']['flush-cache']['requisites'];
$content['icon']['icon']['flush-cache']['advagg']['#title'] = t('Adv CSS/JS Agg');
$content['icon']['icon']['flush-cache']['advagg']['#href'] = 'admin_menu/flush-cache/advagg';
}
}
/**
* Implementation of hook_cron().
*/
function advagg_cron() {
if (!variable_get('advagg_prune_on_cron', ADVAGG_PRUNE_ON_CRON)) {
return;
}
// Set the oldest file/bundle to keep at 2 weeks.
$max_time = module_exists('advagg_bundler') ? variable_get('advagg_bundler_outdated', ADVAGG_BUNDLER_OUTDATED) : 1209600;
$max_file_time = time() - $max_time;
$max_bundle_time = time() - ($max_time*3);
$bundles_removed = 0;
$files_removed = array();
// Prune old files
$results = db_query("SELECT filename, filename_md5 FROM {advagg_files}");
while ($row = db_fetch_array($results)) {
// If the file exists, do nothing
if (file_exists($row['filename'])) {
continue;
}
// Remove bundles referencing missing files, if they are older than 2 weeks.
$bundles = db_query("SELECT bundle_md5 FROM {advagg_bundles} WHERE filename_md5 = '%s' AND timestamp > %d", $row['filename_md5'], $max_file_time);
while ($bundle_md5 = db_result($bundles)) {
$bundles_removed++;
db_query("DELETE FROM {advagg_bundles} WHERE bundle_md5 = '%s'", $bundle_md5);
}
$count = db_result(db_query("SELECT COUNT(*) FROM {advagg_bundles} WHERE filename_md5 = '%s'", $row['filename_md5']));
// If no more bundles reference the missing file then remove the file.
if (empty($count)) {
db_query("DELETE FROM {advagg_files} WHERE filename_md5 = '%s'", $row['filename_md5']);
$files_removed[] = $row['filename'];
}
}
// Prune old bundles
$bundles_removed += db_result(db_query("
SELECT COUNT(*)
FROM (
SELECT *
FROM {advagg_bundles}
WHERE timestamp < %d
GROUP BY bundle_md5
) AS advagg_count", $max_bundle_time));
$results = db_query("DELETE FROM {advagg_bundles} WHERE timestamp < %d", $max_bundle_time);
// Report to watchdog if anything was done.
if (!empty($bundles_removed) || !empty($files_removed)) {
watchdog('advagg', 'Cron ran and the following files where removed from the database: %files
%count old bundles where also removed from the database.', array(
'%files' => implode(', ', $files_removed),
'%count' => $bundles_removed,
));
}
}
/**
* Implementation of hook_init().
*/
function advagg_init() {
global $base_path, $conf, $_advagg;
// Disable advagg if requested.
if (isset($_GET['advagg']) && $_GET['advagg'] == -1 && user_access('bypass advanced aggregation')) {
$conf['advagg_enabled'] = FALSE;
}
// Enable debugging if requested.
if (isset($_GET['advagg-debug']) && $_GET['advagg-debug'] == 1 && user_access('bypass advanced aggregation')) {
$conf['advagg_debug'] = TRUE;
}
// Enable core preprocessing if requested.
if (isset($_GET['advagg-core']) && $_GET['advagg-core'] == 1 && user_access('bypass advanced aggregation')) {
$conf['preprocess_css'] = TRUE;
$conf['preprocess_js'] = TRUE;
}
// Disable ctools_ajax_page_preprocess() if this functionality is available.
if (variable_get('advagg_enabled', ADVAGG_ENABLED) && function_exists('ctools_ajax_run_page_preprocess')) {
ctools_ajax_run_page_preprocess(FALSE);
$_advagg['ctools_patched'] = TRUE;
}
// Create a closure function that does not add JavaScript.
if (variable_get('advagg_closure', ADVAGG_CLOSURE)) {
if (!function_exists('phptemplate_closure')) {
$_advagg['closure'] = TRUE;
/**
* Execute hook_footer() which is run at the end of the page right before
* the close of the body tag.
*
* @param $main (optional)
* Whether the current page is the front page of the site.
* @return
* A string containing the results of the hook_footer() calls.
*/
function phptemplate_closure($main = 0) {
$footer = implode("\n", module_invoke_all('footer', $main));
// If advagg is disabled, then include footer JS here.
if (!variable_get('advagg_enabled', ADVAGG_ENABLED)) {
$footer .= drupal_get_js('footer');
}
return $footer;
}
}
else {
$_advagg['closure'] = FALSE;
}
}
}
/**
* Implementation of hook_theme_registry_alter().
*
* Make sure our preprocess function runs last for page.
*
* @param $theme_registry
* The existing theme registry data structure.
*/
function advagg_theme_registry_alter(&$theme_registry) {
global $_advagg;
if (isset($theme_registry['page'])) {
// If jquery_update's preprocess function is there already, remove it.
if (module_exists('jquery_update') && $key = array_search('jquery_update_preprocess_page', $theme_registry['page']['preprocess functions'])) {
unset($theme_registry['page']['preprocess functions'][$key]);
}
// If ctools hasn't been patched remove it from getting pre-processed.
if ( !empty($_advagg['ctools_patched'])
&& module_exists('ctools')
&& $key = array_search('ctools_ajax_page_preprocess', $theme_registry['page']['preprocess functions'])
) {
unset($theme_registry['page']['preprocess functions'][$key]);
}
// Add our own preprocessing function to the end of the array.
$theme_registry['page']['preprocess functions'][] = 'advagg_processor';
// If labjs's is enabled, move it to the bottom.
if (module_exists('labjs') && $key = array_search('labjs_preprocess_page', $theme_registry['page']['preprocess functions'])) {
$theme_registry['page']['preprocess functions'][] = $theme_registry['page']['preprocess functions'][$key];
unset($theme_registry['page']['preprocess functions'][$key]);
}
// If designkit is enabled, move it to the bottom.
if (module_exists('designkit') && $key = array_search('designkit_preprocess_page', $theme_registry['page']['preprocess functions'])) {
$theme_registry['page']['preprocess functions'][] = $theme_registry['page']['preprocess functions'][$key];
unset($theme_registry['page']['preprocess functions'][$key]);
}
// If conditional styles is enabled, move it to the bottom.
if (module_exists('conditional_styles') && $key = array_search('conditional_styles_preprocess_page', $theme_registry['page']['preprocess functions'])) {
$theme_registry['page']['preprocess functions'][] = $theme_registry['page']['preprocess functions'][$key];
unset($theme_registry['page']['preprocess functions'][$key]);
}
}
}
/**
* Get the CSS & JS path for advagg.
*
* @param $reset
* reset the static variables.
* @return
* array($css_path, $js_path)
*/
function advagg_get_root_files_dir($reset = FALSE) {
static $css_path = '';
static $js_path = '';
if ($reset) {
$css_path = '';
$js_path = '';
}
if (!empty($css_path) && !empty($js_path)) {
return array($css_path, $js_path);
}
$public_downloads = (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC);
if (!$public_downloads) {
$custom_path = variable_get('advagg_custom_files_dir', ADVAGG_CUSTOM_FILES_DIR);
}
if (empty($custom_path)) {
$css_path = file_create_path('advagg_css');
$js_path = file_create_path('advagg_js');
return array($css_path, $js_path);
}
file_check_directory($custom_path, FILE_CREATE_DIRECTORY);
// Get path name
$conf_path = conf_path();
if (is_link($conf_path)) {
$path = readlink($conf_path);
}
$conf_path = str_replace("\\", '/', $conf_path);
$conf_path = explode('/', $conf_path);
$conf_path = array_pop($conf_path);
$custom_path = $custom_path . '/' . $conf_path;
file_check_directory($custom_path, FILE_CREATE_DIRECTORY);
$css_path = $custom_path . '/advagg_css';
$js_path = $custom_path . '/advagg_js';
file_check_directory($css_path, FILE_CREATE_DIRECTORY);
file_check_directory($js_path, FILE_CREATE_DIRECTORY);
return array($css_path, $js_path);
}
/**
* Merge 2 css arrays together.
*
* @param $array1
* first array
* @param $array2
* second array
* @return
* combined array
*/
function advagg_merge_css($array1, $array2) {
foreach ($array2 as $media => $types) {
foreach ($types as $type => $files) {
foreach ($files as $file => $preprocess) {
$array1[$media][$type][$file] = $preprocess;
}
}
}
return $array1;
}
/**
* Merge 2 css arrays together.
*
* @param $array1
* first array
* @param $array2
* second array
* @return
* combined array
*/
function advagg_merge_inline_css($array1, $array2) {
foreach ($array2 as $media => $types) {
foreach ($types as $type => $blobs) {
foreach ($blobs as $prefix => $data) {
foreach ($data as $suffix => $blob) {
$array1[$media][$type][$prefix][$suffix] = $blob;
}
}
}
}
return $array1;
}
/**
* Remove .less files from the array.
*
* @param $css_func
* Drupal CSS array.
*/
function advagg_css_array_fixer(&$css_func) {
if (!module_exists('less')) {
return;
}
// Remove '.css.less' files from the stack.
foreach ($css_func as $k => $v) {
foreach ($v as $ke => $va) {
foreach ($va as $file => $preprocess) {
if (advagg_string_ends_with($file, '.css.less')) {
unset($css_func[$k][$ke][$file]);
}
}
}
}
}
/**
* See if a string ends with a substring.
*
* @param $haystack
* The main string being compared.
* @param $needle
* The secondary string being compared.
* @return
* bool
*/
function advagg_string_ends_with($haystack, $needle) {
// Define substr_compare if it doesn't exist (PHP 4 fix).
if (!function_exists('substr_compare')) {
/**
* Binary safe comparison of two strings from an offset, up to length
* characters.
*
* Compares main_str from position offset with str up to length characters.
* @see http://php.net/substr-compare#53084
*
* @param $main_str
* The main string being compared.
* @param $str
* The secondary string being compared.
* @param $offset
* The start position for the comparison. If negative, it starts counting
* from the end of the string.
* @param $length
* The length of the comparison. The default value is the largest of the
* length of the str compared to the length of main_str less the offset.
* @param $case_insensitivity
* If TRUE, comparison is case insensitive.
* @return
* Returns < 0 if main_str from position offset is less than str, > 0 if
* it is greater than str, and 0 if they are equal. If offset is equal to
* or greater than the length of main_str or length is set and is less than
* 1, substr_compare() prints a warning and returns FALSE.
*/
function substr_compare($main_str, $str, $offset, $length = NULL, $case_insensitivity = FALSE) {
$offset = (int) $offset;
// Throw a warning because the offset is invalid
if ($offset >= strlen($main_str)) {
trigger_error('The start position cannot exceed initial string length.', E_USER_WARNING);
return FALSE;
}
// We are comparing the first n-characters of each string, so let's use the PHP function to do it
if ($offset == 0 && is_int($length) && $case_insensitivity === TRUE) {
return strncasecmp($main_str, $str, $length);
}
// Get the substring that we are comparing
if (is_int($length)) {
$main_substr = substr($main_str, $offset, $length);
$str_substr = substr($str, 0, $length);
}
else {
$main_substr = substr($main_str, $offset);
$str_substr = $str;
}
// Return a case-insensitive comparison of the two strings
if ($case_insensitivity === TRUE) {
return strcasecmp($main_substr, $str_substr);
}
// Return a case-sensitive comparison of the two strings
return strcmp($main_substr, $str_substr);
}
}
$haystack_len = strlen($haystack);
$needle_len = strlen($needle);
if ($needle_len > $haystack_len) {
return FALSE;
}
return substr_compare($haystack, $needle, $haystack_len-$needle_len, $needle_len, TRUE) === 0;
}
/**
* Implementation of hook_advagg_disable_processor().
*/
function advagg_advagg_disable_processor() {
// Disable advagg on the configuration page; in case something bad happened.
if (isset($_GET['q']) &&
( $_GET['q'] == 'admin/settings/advagg'
|| $_GET['q'] == 'admin/settings/advagg/config'
|| $_GET['q'] == 'batch'
)
) {
return TRUE;
}
}
/**
* Process variables for page.tpl.php
*
* @param $variables
* The existing theme data structure.
*/
function advagg_processor(&$variables) {
global $_advagg;
// Invoke hook_advagg_disable_processor
$disabled = module_invoke_all('advagg_disable_processor');
// If disabled, skip
if (!variable_get('advagg_enabled', ADVAGG_ENABLED) || in_array(TRUE, $disabled, TRUE)) {
if (module_exists('jquery_update')) {
return jquery_update_preprocess_page($variables);
}
else {
return;
}
}
// CSS
$css_var = $variables['css'];
$css_orig = $css_var;
$css_func = drupal_add_css();
advagg_css_array_fixer($css_func);
$css = advagg_merge_css($css_func, $css_var);
$css_func_inline = advagg_add_css_inline();
if (!empty($css_func_inline)) {
$css = advagg_merge_inline_css($css, $css_func_inline);
}
$css_conditional_styles = !empty($variables['conditional_styles']) ? $variables['conditional_styles'] : '';
$css_styles = $variables['styles'];
// Build HTML code.
$processed_css = advagg_process_css($css);
if (!empty($processed_css)) {
$variables['styles'] = $processed_css;
}
$variables['styles'] .= "\n". $css_conditional_styles;
// JS
$js_code = array();
$js_code['header'] = drupal_add_js(NULL, NULL, 'header');
if (variable_get('advagg_closure', ADVAGG_CLOSURE) && !empty($_advagg['closure'])) {
$js_code['footer'] = drupal_add_js(NULL, NULL, 'footer');
}
$skip_keys = variable_get('advagg_region_skip_keys', array('styles', 'scripts', 'zebra', 'id', 'directory', 'layout', 'head_title', 'base_path', 'front_page', 'head', 'body_classes', 'header', 'footer', 'closure'));
foreach ($variables as $key => $value) {
if (!in_array($key, $skip_keys) && is_string($value) && !empty($value) && !isset($js_code[$key])) {
$js_code[$key] = drupal_add_js(NULL, NULL, $key);
}
}
$js_code_orig = $js_code;
// Build HTML code.
advagg_jquery_updater($js_code['header']);
$js_code = advagg_process_js($js_code);
foreach ($js_code as $key => $value) {
if ($key == 'header') {
$variables['scripts'] = $value;
}
elseif ($key == 'footer' && variable_get('advagg_closure', ADVAGG_CLOSURE) && !empty($_advagg['closure'])) {
$variables['closure'] .= $value;
}
else {
$variables[$key] .= $value;
}
}
// Send requests to server if async enabled.
advagg_async_send_http_request();
// Write debug info to watchdog if debugging enabled.
if (variable_get('advagg_debug', ADVAGG_DEBUG)) {
$data = array(
'css_before_vars' => $css_orig,
'css_before_function' => $css_func,
'css_before_styles' => $css_styles,
'css_before_inline' => $css_func_inline,
'css_before_conditional_styles' => $css_conditional_styles,
'css_merged' => $css,
'css_after' => $processed_css,
'js_before' => $js_code_orig,
'js_after' => $js_code,
);
$data['runtime'] = isset($_advagg['debug']) ? $_advagg['debug'] : FALSE;
$data = str_replace(' ', ' ', nl2br(htmlentities(iconv('utf-8', 'utf-8//IGNORE', print_r($data, TRUE)), ENT_QUOTES, 'UTF-8')));
watchdog('advagg', 'Debug info: !data', array('!data' => $data), WATCHDOG_DEBUG);
}
}
/**
* Special handling for jquery update.
*
* @param $js
* List of files in the header
*/
function advagg_jquery_updater(&$js) {
if (!module_exists('jquery_update') || !variable_get('jquery_update_replace', TRUE) || empty($js)) {
return;
}
// Replace jquery.js first.
$new_jquery = array(jquery_update_jquery_path() => $js['core']['misc/jquery.js']);
$js['core'] = array_merge($new_jquery, $js['core']);
unset($js['core']['misc/jquery.js']);
// Loop through each of the required replacements.
foreach (jquery_update_get_replacements() as $type => $replacements) {
foreach ($replacements as $find => $replace) {
// If the file to replace is loaded on this page...
if (isset($js[$type][$find])) {
// Create a new entry for the replacement file, and unset the original one.
$replace = JQUERY_UPDATE_REPLACE_PATH .'/'. $replace;
$js[$type][$replace] = $js[$type][$find];
unset($js[$type][$find]);
}
}
}
}
/**
* Given a list of files; return back the aggregated filename.
*
* @param $files
* List of files in the proposed bundle.
* @param $filetype
* css or js.
* @param $counter
* (optional) Counter value.
* @param $bundle_md5
* (optional) Bundle's machine name.
* @return
* Aggregated filename.
*/
function advagg_get_filename($files, $filetype, $counter = FALSE, $bundle_md5 = '') {
if (empty($files) || empty($filetype)) {
return FALSE;
}
global $_advagg;
$filenames = array();
$run_alter = FALSE;
if (empty($bundle_md5)) {
// Create bundle md5
$bundle_md5 = md5(implode('', $files));
$run_alter = TRUE;
// Record root request in db.
// Get counter if there.
if (empty($counter)) {
$counter = db_result(db_query("SELECT counter FROM {advagg_bundles} WHERE bundle_md5 = '%s'", $bundle_md5));
}
// If this is a brand new bundle then insert file/bundle info into database.
if ($counter === FALSE) {
$counter = 0;
advagg_insert_bundle_db($files, $filetype, $bundle_md5, TRUE);
}
// If bundle should be root and is not, then make it root.
// Refresh timestamp if older then 12 hours.
$row = db_fetch_array(db_query("SELECT root, timestamp FROM {advagg_bundles} WHERE bundle_md5 = '%s'", $bundle_md5));
if ($row['root'] === 0 || time() - $row['timestamp'] > variable_get('advagg_file_last_used_interval', ADVAGG_FILE_LAST_USED_INTERVAL)) {
db_query("UPDATE {advagg_bundles} SET root = '1', timestamp = %d WHERE bundle_md5 = '%s'", time(), $bundle_md5);
}
}
// Set original array.
$filenames[] = array(
'filetype' => $filetype,
'files' => $files,
'counter' => $counter,
'bundle_md5' => $bundle_md5,
);
// Invoke hook_advagg_filenames_alter() to give installed modules a chance to
// alter filenames. One to many relationships come to mind.
// Do not run alter if MD5 was given, we want to generate that file only in
// this special case.
if ($run_alter) {
// Force counter to be looked up later.
$filenames[0]['counter'] = FALSE;
drupal_alter('advagg_filenames', $filenames);
}
// Write to DB if needed and create filenames.
$output = array();
$used_md5 = array();
if (variable_get('advagg_debug', ADVAGG_DEBUG)) {
$_advagg['debug']['get_filename_post_alter'][] = array(
'key' => $bundle_md5,
'filenames' => $filenames,
);
}
// Get all counters at once
$counters = array();
foreach ($filenames as $key => $values) {
if (empty($values['counter'])) {
$counters[$key] = $values['bundle_md5'];
}
}
$result = advagg_db_multi_select_in('advagg_bundles', 'bundle_md5', "'%s'", $counters, array('counter', 'bundle_md5'), 'GROUP BY bundle_md5');
while ($row = db_fetch_array($result)) {
$key = array_search($row['bundle_md5'], $counters);
if (empty($filenames[$key]['counter']) && $filenames[$key]['counter'] !== 0) {
$filenames[$key]['counter'] = intval($row['counter']);
}
}
foreach ($filenames as $values) {
// Get info from array.
$filetype = $values['filetype'];
$files = $values['files'];
$counter = $values['counter'];
$bundle_md5 = $values['bundle_md5'];
// See if a JS bundle exists that already has the same files in it, just in a
// different order.
// if ($filetype == 'js' && $run_alter) {
// advagg_find_existing_bundle($files, $bundle_md5);
// }
// Do not add the same bundle twice.
if (isset($used_md5[$bundle_md5])) {
continue;
}
$used_md5[$bundle_md5] = TRUE;
// If this is a brand new bundle then insert file/bundle info into database.
if (empty($counter) && $counter !== 0) {
$counter = 0;
advagg_insert_bundle_db($files, $filetype, $bundle_md5, FALSE);
}
// Prefix filename to prevent blocking by firewalls which reject files
// starting with "ad*".
$output[] = array(
'filename' => advagg_build_filename($filetype, $bundle_md5, $counter),
'files' => $files,
'bundle_md5' => $bundle_md5,
);
}
return $output;
}
/**
* Get a bundle from the cache & verify it is good.
*
* @param $cached_data_key
* cache key for the cache_advagg_bundle_reuse table.
* @param $debug_name
* Name to output in the array if debugging is enabled.
* @return
* data from the cache.
*/
function advagg_cached_bundle_get($cached_data_key, $debug_name) {
global $_advagg;
$data = cache_get($cached_data_key, 'cache_advagg_bundle_reuse');
if (!empty($data->data)) {
$data = $data->data;
$bundle_contents = array();
$good = TRUE;
// Verify cached data is good.
foreach ($data as $filename => $extra) {
if (is_numeric($filename)) {
continue;
}
// Get md5 from aggregated filename.
$b_md5 = explode('/', $filename);
$b_md5 = explode('_', array_pop($b_md5));
$b_md5 = $b_md5[1];
// Lookup bundle and make sure it is valid.
if (!empty($b_md5)) {
list($b_filetype, $b_files) = advagg_get_files_in_bundle($b_md5);
$bundle_contents[$filename] = $b_files;
if (empty($b_files)) {
$good = FALSE;
}
}
}
// Debugging.
if (variable_get('advagg_debug', ADVAGG_DEBUG)) {
$_advagg['debug'][$debug_name][] = array(
'key' => $cached_data_key,
'files' => $files,
'cache' => $data,
'bundle_contents' => $bundle_contents,
);
}
if ($good) {
return $data;
}
}
return FALSE;
}
/**
* Given a list of files, see if a bundle already exists containing all of those
* files. If in strict mode then the file count has to be the same.
*
* @param $files
* List of files in the proposed bundle.
* @param $bundle_md5
* Bundle's machine name.
*/
function advagg_find_existing_bundle(&$files, &$bundle_md5) {
// Sort files for better cache hits.
$temp_files = $files;
sort($temp_files);
$cached_data_key = 'advagg_existing_' . md5(implode('', $temp_files));
// Try cache first; cache table is cache_advagg_bundle_reuse. string is debug name.
$cached_data = advagg_cached_bundle_get($cached_data_key, 'advagg_find_existing_bundle');
if (!empty($cached_data)) {
$files = $cached_data[0]['files'];
$bundle_md5 = $cached_data[0]['bundle_md5'];
return;
}
// Build union query.
$query = 'SELECT root.bundle_md5 FROM {advagg_bundles} AS root';
$joins = array();
$wheres = array();
$args = array();
$counter = 0;
foreach ($files as $filename) {
// Use alpha for table aliases; numerics do not work.
$key = strtr($counter, '01234567890', 'abcdefghij');
$joins[$key] = "\nINNER JOIN {advagg_bundles} AS $key USING(bundle_md5)\n";
if ($counter == 0) {
$wheres[$key] = "WHERE $key.filename_md5 = '%s'";
}
else {
$wheres[$key] = "AND $key.filename_md5 = '%s'";
}
$args[$key] = md5($filename);
$counter++;
}
$query .= implode("\n", $joins);
$query .= implode("\n", $wheres);
$query .= ' GROUP BY bundle_md5';
// Find matching bundles and select first good one.
$files_count = count($files);
$results = db_query($query, $args);
while ($new_md5 = db_result($results)) {
$count = db_result(db_query("SELECT count(*) FROM {advagg_bundles} WHERE bundle_md5 = '%s'", $new_md5));
// Make sure bundle has the same number of files if using strict matching.
if (!empty($count) && $count == $files_count) {
$bundle_md5 = $new_md5;
$data = array(array('files' => $files, 'bundle_md5' => $bundle_md5));
cache_set($cached_data_key, $data, 'cache_advagg_bundle_reuse', CACHE_TEMPORARY);
return;
}
}
}
/**
* Build the filename.
*
* @param $filetype
* css or js.
* @param $counter
* Counter value.
* @param $bundle_md5
* Bundle's machine name.
*/
function advagg_build_filename($filetype, $bundle_md5, $counter) {
return $filetype . '_' . $bundle_md5 . '_' . $counter . '.' . $filetype;
}
/**
* Insert info into the advagg_files and advagg_bundles database.
*
* @param $files
* List of files in the proposed bundle.
* @param $filetype
* css or js.
* @param $bundle_md5
* Bundle's machine name.
* @param $root
* Is this a root bundle.
*/
function advagg_insert_bundle_db($files, $filetype, $bundle_md5, $root) {
$lock_name = 'advagg_insert_bundle_db' . $bundle_md5;
if (!lock_acquire($lock_name)) {
lock_wait($lock_name);
return;
}
foreach ($files as $order => $filename) {
$filename_md5 = md5($filename);
// Insert file into the advagg_files table if it doesn't exist.
$checksum = db_result(db_query("SELECT checksum FROM {advagg_files} WHERE filename_md5 = '%s'", $filename_md5));
if (empty($checksum)) {
$checksum = advagg_checksum($filename);
db_query("INSERT INTO {advagg_files} (filename, filename_md5, checksum, filetype, filesize) VALUES ('%s', '%s', '%s', '%s', %d)", $filename, $filename_md5, $checksum, $filetype, @filesize($filename));
}
// Create the entries in the advagg_bundles table.
db_query("INSERT INTO {advagg_bundles} (bundle_md5, filename_md5, counter, porder, root) VALUES ('%s', '%s', '%d', '%d', '%d')", $bundle_md5, $filename_md5, 0, $order, $root, time());
}
lock_release($lock_name);
}
/**
* Save a string to the specified destination. Verify that file size is not zero.
*
* @param $data
* A string containing the contents of the file.
* @param $dest
* A string containing the destination location.
* @return
* Boolean indicating if the file save was successful.
*/
function advagg_file_saver($data, $dest, $force, $type) {
// Create the JS file.
$file_save_data = 'file_save_data';
$custom_path = variable_get('advagg_custom_files_dir', ADVAGG_CUSTOM_FILES_DIR);
if (!empty($custom_path)) {
$file_save_data = 'advagg_file_save_data';
}
if (!$file_save_data($data, $dest, FILE_EXISTS_REPLACE)) {
return FALSE;
}
// Make sure filesize is not zero.
advagg_clearstatcache(TRUE, $dest);
if (@filesize($dest) == 0 && !empty($data)) {
if (!$file_save_data($data, $dest, FILE_EXISTS_REPLACE)) {
return FALSE;
}
advagg_clearstatcache(TRUE, $dest);
if (@filesize($dest) == 0 && !empty($data)) {
// Filename is bad, create a new one next time.
file_delete($dest);
return FALSE;
}
}
if (variable_get('advagg_gzip_compression', ADVAGG_GZIP_COMPRESSION) && extension_loaded('zlib')) {
$gzip_dest = $dest . '.gz';
advagg_clearstatcache(TRUE, $gzip_dest);
if (!file_exists($gzip_dest) || $force) {
$gzip_data = gzencode($data, 9, FORCE_GZIP);
if (!$file_save_data($gzip_data, $gzip_dest, FILE_EXISTS_REPLACE)) {
return FALSE;
}
// Make sure filesize is not zero.
advagg_clearstatcache(TRUE, $gzip_dest);
if (@filesize($gzip_dest) == 0 && !empty($gzip_data)) {
if (!$file_save_data($gzip_data, $gzip_dest, FILE_EXISTS_REPLACE)) {
return FALSE;
}
advagg_clearstatcache(TRUE, $gzip_dest);
if (@filesize($gzip_dest) == 0 && !empty($gzip_data)) {
// Filename is bad, create a new one next time.
file_delete($gzip_dest);
return FALSE;
}
}
}
}
// Make sure .htaccess file exists.
advagg_htaccess_check_generate($dest);
cache_set($dest, time(), 'cache_advagg', CACHE_PERMANENT);
return TRUE;
}
/**
* ***MODIFIED CORE FUNCTIONS BELOW***
*
* @see file_save_data()
* @see file_move()
* @see file_copy()
*/
/**
* Save a string to the specified destination.
*
* @see file_save_data()
*
* @param $data A string containing the contents of the file.
* @param $dest A string containing the destination location.
* @param $replace Replace behavior when the destination file already exists.
* - FILE_EXISTS_REPLACE - Replace the existing file
* - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is unique
* - FILE_EXISTS_ERROR - Do nothing and return FALSE.
*
* @return A string containing the resulting filename or 0 on error
*/
function advagg_file_save_data($data, $dest, $replace = FILE_EXISTS_RENAME) {
$temp = file_directory_temp();
// On Windows, tempnam() requires an absolute path, so we use realpath().
$file = tempnam(realpath($temp), 'file');
if (!$fp = fopen($file, 'wb')) {
drupal_set_message(t('The file could not be created.'), 'error');
return 0;
}
fwrite($fp, $data);
fclose($fp);
if (!advagg_file_move($file, $dest, $replace)) {
return 0;
}
return $file;
}
/**
* Moves a file to a new location.
*
* @see file_move()
*
* - Checks if $source and $dest are valid and readable/writable.
* - Performs a file move if $source is not equal to $dest.
* - If file already exists in $dest either the call will error out, replace the
* file or rename the file based on the $replace parameter.
*
* @param $source
* Either a string specifying the file location of the original file or an
* object containing a 'filepath' property. This parameter is passed by
* reference and will contain the resulting destination filename in case of
* success.
* @param $dest
* A string containing the directory $source should be copied to. If this
* value is omitted, Drupal's 'files' directory will be used.
* @param $replace
* Replace behavior when the destination file already exists.
* - FILE_EXISTS_REPLACE: Replace the existing file.
* - FILE_EXISTS_RENAME: Append _{incrementing number} until the filename is
* unique.
* - FILE_EXISTS_ERROR: Do nothing and return FALSE.
* @return
* TRUE for success, FALSE for failure.
*/
function advagg_file_move(&$source, $dest = 0, $replace = FILE_EXISTS_RENAME) {
$path_original = is_object($source) ? $source->filepath : $source;
if (advagg_file_copy($source, $dest, $replace)) {
$path_current = is_object($source) ? $source->filepath : $source;
if ($path_original == $path_current || file_delete($path_original)) {
return 1;
}
drupal_set_message(t('The removal of the original file %file has failed.', array('%file' => $path_original)), 'error');
}
return 0;
}
/**
* Copies a file to a new location.
*
* @see file_copy()
*
* This is a powerful function that in many ways performs like an advanced
* version of copy().
* - Checks if $source and $dest are valid and readable/writable.
* - Performs a file copy if $source is not equal to $dest.
* - If file already exists in $dest either the call will error out, replace the
* file or rename the file based on the $replace parameter.
*
* @param $source
* Either a string specifying the file location of the original file or an
* object containing a 'filepath' property. This parameter is passed by
* reference and will contain the resulting destination filename in case of
* success.
* @param $dest
* A string containing the directory $source should be copied to. If this
* value is omitted, Drupal's 'files' directory will be used.
* @param $replace
* Replace behavior when the destination file already exists.
* - FILE_EXISTS_REPLACE: Replace the existing file.
* - FILE_EXISTS_RENAME: Append _{incrementing number} until the filename is
* unique.
* - FILE_EXISTS_ERROR: Do nothing and return FALSE.
* @return
* TRUE for success, FALSE for failure.
*/
function advagg_file_copy(&$source, $dest = 0, $replace = FILE_EXISTS_RENAME) {
$directory = dirname($dest);
// Process a file upload object.
if (is_object($source)) {
$file = $source;
$source = $file->filepath;
if (!$basename) {
$basename = $file->filename;
}
}
$source = realpath($source);
advagg_clearstatcache(TRUE, $source);
if (!file_exists($source)) {
drupal_set_message(t('The selected file %file could not be copied, because no file by that name exists. Please check that you supplied the correct filename.', array('%file' => $source)), 'error');
return 0;
}
// If the destination file is not specified then use the filename of the source file.
$basename = basename($dest);
$basename = $basename ? $basename : basename($source);
$dest = $directory . '/' . $basename;
// Make sure source and destination filenames are not the same, makes no sense
// to copy it if they are. In fact copying the file will most likely result in
// a 0 byte file. Which is bad. Real bad.
if ($source != realpath($dest)) {
if (!$dest = file_destination($dest, $replace)) {
drupal_set_message(t('The selected file %file could not be copied, because a file by that name already exists in the destination.', array('%file' => $source)), 'error');
return FALSE;
}
if (!@copy($source, $dest)) {
drupal_set_message(t('The selected file %file could not be copied. ' . $dest, array('%file' => $source)), 'error');
return 0;
}
// Give everyone read access so that FTP'd users or
// non-webserver users can see/read these files,
// and give group write permissions so group members
// can alter files uploaded by the webserver.
@chmod($dest, 0664);
}
if (isset($file) && is_object($file)) {
$file->filename = $basename;
$file->filepath = $dest;
$source = $file;
}
else {
$source = $dest;
}
return 1; // Everything went ok.
}
/**
* Generate a checksum for a given filename.
*
* @param $filename
* filename
* @return
* Checksum value.
*/
function advagg_checksum($filename) {
advagg_clearstatcache(TRUE, $filename);
if (file_exists($filename)) {
$mode = variable_get('advagg_checksum_mode', ADVAGG_CHECKSUM_MODE);
if ($mode == 'mtime') {
$checksum = @filemtime($filename);
if ($checksum === FALSE) {
touch($filename);
advagg_clearstatcache(TRUE, $filename);
$checksum = @filemtime($filename);
// Use md5 as a last option.
if ($checksum === FALSE) {
$checksum = md5(file_get_contents($filename));
}
}
}
elseif ($mode = 'md5') {
$checksum = md5(file_get_contents($filename));
}
}
else {
$checksum = '-1';
}
return $checksum;
}
/**
* See if this bundle has been built.
*
* @param $filepath
* filename
* @return
* Boolean indicating if the bundle already exists.
*/
function advagg_bundle_built($filepath) {
// Don't use the cache if not selected.
if (!variable_get('advagg_bundle_built_mode', ADVAGG_BUNDLE_BUILT_MODE)) {
advagg_clearstatcache(TRUE, $filepath);
return file_exists($filepath);
}
$data = advagg_get_bundle_from_filename(basename($filepath));
if (is_array($data)) {
list($type, $md5, $counter) = $data;
}
else {
return FALSE;
}
$data = cache_get($filepath, 'cache_advagg');
if (isset($data->data)) {
// Refresh timestamp if older then 12 hours.
if (time() - $data->data > variable_get('advagg_file_last_used_interval', ADVAGG_FILE_LAST_USED_INTERVAL)) {
cache_set($filepath, time(), 'cache_advagg', CACHE_PERMANENT);
db_query("UPDATE {advagg_bundles} SET timestamp = %d WHERE bundle_md5 = '%s'", time(), $md5);
}
return TRUE;
}
// If not in cache check disk.
advagg_clearstatcache(TRUE, $filepath);
if (file_exists($filepath)) {
if (@filesize($filepath) == 0) {
return FALSE;
}
}
else {
return FALSE;
}
// File existed on disk; place in cache.
cache_set($filepath, time(), 'cache_advagg', CACHE_PERMANENT);
db_query("UPDATE {advagg_bundles} SET timestamp = %d WHERE bundle_md5 = '%s'", time(), $md5);
return TRUE;
}
function advagg_get_bundle_from_filename($filename) {
// Verify requested filename has the correct pattern.
if (!preg_match('/^(j|cs)s_[0-9a-f]{32}_\d+\.(j|cs)s$/', $filename)) {
return t('Wrong Pattern.');
}
// Get type
$type = substr($filename, 0, strpos($filename, '_'));
// Get extension
$ext = substr($filename, strpos($filename, '.', 37)+1);
// Make sure extension is the same as the type.
if ($ext != $type) {
return t('Type does not match extension.');
}
// Extract info from wanted filename.
if ($type == 'css') {
$md5 = substr($filename, 4, 32);
$counter = substr($filename, 37, strpos($filename, '.', 38)-37);
}
elseif ($type == 'js') {
$md5 = substr($filename, 3, 32);
$counter = substr($filename, 36, strpos($filename, '.', 37)-36);
}
else {
return t('Wrong file type.');
}
return array($type, $md5, $counter);
}
/**
* Implementation of hook_flush_caches().
*/
function advagg_flush_caches() {
// Try to allocate enough time to flush the cache
if (function_exists('set_time_limit')) {
@set_time_limit(240);
}
global $_advagg;
// Only one advagg cache flusher can run at a time.
if (!lock_acquire('advagg_flush_caches')) {
return;
}
// Only run code below if the advagg db tables exist.
if (!db_table_exists('advagg_files')) {
return array('cache_advagg_bundle_reuse');
}
// Find files that have changed.
$needs_refreshing = array();
$results = db_query("SELECT * FROM {advagg_files}");
while ($row = db_fetch_array($results)) {
$checksum = advagg_checksum($row['filename']);
// Let other modules see if the bundles needs to be rebuilt.
// hook_advagg_files_table
// Return TRUE in order to increment the counter.
$hook_results = module_invoke_all('advagg_files_table', $row, $checksum);
// Check each return value; see if an update is needed.
$update = FALSE;
if (!empty($hook_results)) {
foreach ($hook_results as $update) {
if ($update === TRUE) {
break;
}
}
}
// Increment the counter if needed and mark file for bundle refreshment.
if ($checksum != $row['checksum'] || $update == TRUE) {
$needs_refreshing[$row['filename_md5']] = $row['filename'];
// Update checksum; increment counter.
db_query("UPDATE {advagg_files} SET checksum = '%s', counter = counter + 1 WHERE filename_md5 = '%s'", $checksum, $row['filename_md5']);
}
}
// Get the bundles.
$bundles = array();
foreach ($needs_refreshing as $filename_md5 => $filename) {
$results = db_query("SELECT bundle_md5 FROM {advagg_bundles} WHERE filename_md5 = '%s'", $filename_md5);
while ($row = db_fetch_array($results)) {
$bundles[$row['bundle_md5']] = $row['bundle_md5'];
}
}
foreach ($bundles as $bundle_md5) {
// Increment Counter
db_query("UPDATE {advagg_bundles} SET counter = counter + 1, timestamp = %d WHERE bundle_md5 = '%s'", time(), $bundle_md5);
if (variable_get('advagg_rebuild_on_flush', ADVAGG_REBUILD_ON_FLUSH)) {
// Rebuild bundles on shutdown in the background. This is needed so that
// the cache_advagg_bundle_reuse table has been cleared.
register_shutdown_function('advagg_rebuild_bundle', $bundle_md5, '', TRUE);
}
}
$_advagg['bundles'] = $bundles;
$_advagg['files'] = $needs_refreshing;
// Garbage collection
list($css_path, $js_path) = advagg_get_root_files_dir();
file_scan_directory($css_path, '.*', array('.', '..', 'CVS'), 'advagg_delete_file_if_stale', TRUE);
file_scan_directory($js_path, '.*', array('.', '..', 'CVS'), 'advagg_delete_file_if_stale', TRUE);
lock_release('advagg_flush_caches');
return array('cache_advagg_bundle_reuse');
}
/**
* Rebuild a bundle.
*
* @param $bundle_md5
* Bundle's machine name.
* @param $counter
* Counter value.
* @param $force
* Rebuild even if file already exists.
*/
function advagg_rebuild_bundle($bundle_md5, $counter = '', $force = FALSE) {
global $conf, $_advagg;
list($filetype, $files) = advagg_get_files_in_bundle($bundle_md5);
$conf['advagg_async_generation'] = FALSE;
$good = advagg_css_js_file_builder($filetype, $files, '', $counter, $force, $bundle_md5);
if (!$good) {
watchdog('advagg', 'This bundle could not be generated correctly. Bundle MD5: %md5', array('%md5' => $bundle_md5));
}
else {
$_advagg['rebuilt'][] = $bundle_md5;
}
return $good;
}
/**
* Get list of files and the filetype given a bundle md5.
*
* @param $bundle_md5
* Bundle's machine name.
* @return
* array ($filetype, $files)
*/
function advagg_get_files_in_bundle($bundle_md5) {
$files = array();
$filetype = NULL;
$results = db_query("SELECT filename, filetype FROM {advagg_files} AS af INNER JOIN {advagg_bundles} AS ab USING ( filename_md5 ) WHERE bundle_md5 = '%s' ORDER BY porder ASC", $bundle_md5);
while ($row = db_fetch_array($results)) {
$files[] = $row['filename'];
$filetype = $row['filetype'];
}
return array($filetype, $files);
}
/**
* Callback to delete files modified more than a set time ago.
*
* @param $filename
* name of a file to check how old it is.
*/
function advagg_delete_file_if_stale($filename) {
// Do not process .gz files
if (strpos($filename, '.gz') !== FALSE) {
return;
}
$now = time();
$file_last_mod = variable_get('advagg_stale_file_threshold', ADVAGG_STALE_FILE_THRESHOLD);
$file_last_used = variable_get('advagg_stale_file_last_used_threshold', ADVAGG_STALE_FILE_LAST_USED_THRESHOLD);
// Default stale file threshold is 30 days.
advagg_clearstatcache(TRUE, $filename);
if ($now - filemtime($filename) <= $file_last_mod) {
return;
}
// Check to see if this file is still in use.
$data = cache_get($filename, 'cache_advagg');
if (!empty($data->data)) {
advagg_clearstatcache(TRUE, $filename);
$file_last_a = @fileatime($filename);
$file_last_agz = @fileatime($filename . '.gz');
$file_last_a = max($file_last_a, $file_last_agz);
if ($now - $data->data > $file_last_used && $now - $file_last_a > $file_last_used) {
// Delete file if it hasn't been used in the last 15 days.
file_delete($filename);
file_delete($filename . '.gz');
}
else {
// Touch file so we don't check again for another 30 days
touch($filename);
}
}
else {
// Delete file if it is not in the cache.
file_delete($filename);
file_delete($filename . '.gz');
}
}
/**
* Get data about a file.
*
* @param $filename_md5
* md5 of filename.
* @return
* data array from database.
*/
function advagg_get_file_data($filename_md5) {
$data = cache_get($filename_md5, 'cache_advagg_files_data');
if (empty($data->data)) {
return FALSE;
}
return $data->data;
}
/**
* Set data about a file.
*
* @param $filename_md5
* md5 of filename.
* @param $data
* data to store.
*/
function advagg_set_file_data($filename_md5, $data) {
cache_set($filename_md5, $data, 'cache_advagg_files_data', CACHE_PERMANENT);
}
/**
* Given path output uri to that file
*
* @param $filename_md5
* md5 of filename.
* @param $data
* data to store.
*/
function advagg_build_uri($path) {
$original_path = $path;
// CDN Support.
if (module_exists('cdn')) {
$status = variable_get(CDN_STATUS_VARIABLE, CDN_DISABLED);
if ($status == CDN_ENABLED || ($status == CDN_TESTING && user_access(CDN_PERM_ACCESS_TESTING))) {
// Alter URL when the file_create_url() patch is not there.
if (variable_get(CDN_THEME_LAYER_FALLBACK_VARIABLE, FALSE)) {
cdn_file_url_alter($path);
}
// Use the patched version of file_create_url().
else {
$path = file_create_url($path);
}
if (strcmp($original_path, $path) != 0) {
return $path;
}
}
}
return base_path() . $path;
}
/**
* ***MODIFIED CORE FUNCTIONS BELOW***
*
* @see drupal_get_css()
* @see drupal_build_css_cache()
* @see drupal_get_js()
* @see drupal_build_js_cache()
*/
/**
* Returns an array of values needed for aggregation
*
* @param $noagg
* (optional) Bool indicating that aggregation should be disabled if TRUE.
* @return
* array of values to be imported via list() function.
*/
function advagg_process_css_js_prep($noagg = FALSE) {
$preprocess = (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update');
if ($noagg || (isset($_GET['advagg']) && $_GET['advagg'] == 0 && user_access('bypass advanced aggregation'))) {
$preprocess = FALSE;
}
$public_downloads = (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC);
if (!$public_downloads) {
$custom_path = variable_get('advagg_custom_files_dir', ADVAGG_CUSTOM_FILES_DIR);
if (!empty($custom_path)) {
$public_downloads = TRUE;
}
}
// 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);
return array($preprocess, $public_downloads, $query_string);
}
/**
* Returns a themed representation of all stylesheets that should be attached to
* the page.
*
* @see drupal_get_css()
*
* It loads the CSS in order, with 'module' first, then 'theme' afterwards.
* This ensures proper cascading of styles so themes can easily override
* module styles through CSS selectors.
*
* Themes may replace module-defined CSS files by adding a stylesheet with the
* same filename. For example, themes/garland/system-menus.css would replace
* modules/system/system-menus.css. This allows themes to override complete
* CSS files, rather than specific selectors, when necessary.
*
* If the original CSS file is being overridden by a theme, the theme is
* responsible for supplying an accompanying RTL CSS file to replace the
* module's.
*
* @param $css
* (optional) An array of CSS files. If no array is provided, the default
* stylesheets array is used instead.
* @param $noagg
* (optional) Bool indicating that aggregation should be disabled if TRUE.
* @return
* A string of XHTML CSS tags.
*/
function advagg_process_css($css = NULL, $noagg = FALSE) {
global $conf;
$original_css = $css;
if (!isset($css)) {
$css = drupal_add_css();
}
if (empty($css)) {
return FALSE;
}
// Get useful info.
list($preprocess_css, $public_downloads, $query_string) = advagg_process_css_js_prep($noagg);
// Invoke hook_advagg_css_pre_alter() to give installed modules a chance to
// modify the data in the $javascript array if necessary.
drupal_alter('advagg_css_pre', $css, $preprocess_css, $public_downloads);
// Set variables.
$external_no_preprocess = array();
$module_no_preprocess = array();
$output_no_preprocess = array();
$output_preprocess = array();
$theme_no_preprocess = array();
$inline_no_preprocess = array();
$files_included = array();
$files_aggregates_included = array();
$inline_included = array();
// Process input.
foreach ($css as $media => $types) {
// Setup some variables
$files_included[$media] = array();
$files_aggregates_included[$media] = array();
$inline_included[$media] = array();
// 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.
foreach ($types as $type => $files) {
if ($type == 'module') {
// Setup theme overrides for module styles.
$theme_styles = array();
foreach (array_keys($css[$media]['theme']) as $theme_style) {
$theme_styles[] = basename($theme_style);
}
}
foreach ($types[$type] as $file => $preprocess) {
// If the theme supplies its own style using the name of the module
// style, skip its inclusion. This includes any RTL styles associated
// with its main LTR counterpart.
if ($type == 'module' && in_array(str_replace('-rtl.css', '.css', basename($file)), $theme_styles)) {
// Unset the file to prevent its inclusion when CSS aggregation is enabled.
unset($types[$type][$file]);
continue;
}
// If a CSS file is not to be preprocessed and it's an external
// CSS file, it needs to *always* appear at the *very top*,
// regardless of whether preprocessing is on or off.
if ($type == 'external') {
$external_no_preprocess[] = array(
'media' => $media,
'href' => $file,
'prefix' => '',
'suffix' => '',
);
$files_included[$media][$file] = TRUE;
// Unset the file to prevent its inclusion.
unset($types[$type][$file]);
continue;
}
// If a CSS file is not to be preprocessed and it's an inline CSS blob
// it needs to *always* appear at the *very bottom*.
if ($type == 'inline') {
if (is_array($preprocess)) {
foreach ($preprocess as $suffix => $blob) {
$blob = advagg_drupal_load_stylesheet_content($blob, $preprocess);
// Invoke hook_advagg_css_inline_alter() to give installed modules
// a chance to modify the contents of $blob if necessary.
drupal_alter('advagg_css_inline', $blob);
$inline_no_preprocess[] = array(
'media' => $media,
'data' => $blob,
'prefix' => $file,
'suffix' => $suffix,
);
$inline_included[$media][] = $blob;
}
}
else {
$file = advagg_drupal_load_stylesheet_content($file, $preprocess);
// Invoke hook_advagg_css_inline_alter() to give installed modules a
// chance to modify the contents of $file if necessary.
drupal_alter('advagg_css_inline', $file);
$inline_no_preprocess[] = array(
'media' => $media,
'data' => $file,
'prefix' => '',
'suffix' => '',
);
$inline_included[$media][] = $file;
}
// Unset to prevent its inclusion.
unset($types[$type][$file]);
continue;
}
// Only include the stylesheet if it exists.
if (advagg_file_exists($file)) {
if (!$preprocess || !($public_downloads && $preprocess_css)) {
// Create URI for file.
$file_uri = advagg_build_uri($file) . $query_string;
$files_included[$media][$file] = $preprocess;
// 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') {
$module_no_preprocess[] = array(
'media' => $media,
'href' => $file_uri,
'prefix' => '',
'suffix' => '',
);
}
// 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.
elseif (!$preprocess && $type == 'theme') {
$theme_no_preprocess[] = array(
'media' => $media,
'href' => $file_uri,
'prefix' => '',
'suffix' => '',
);
}
else {
$output_no_preprocess[] = array(
'media' => $media,
'href' => $file_uri,
'prefix' => '',
'suffix' => '',
);
}
}
}
}
}
if ($public_downloads && $preprocess_css) {
$files_aggregates_included[$media] = $files_included[$media];
$files = array();
foreach ($types as $type) {
foreach ($type as $file => $cache) {
if ($cache) {
$files[] = $file;
$files_included[$media][$file] = TRUE;
unset($files_aggregates_included[$file]);
}
}
}
$preprocess_files = advagg_css_js_file_builder('css', $files, $query_string);
$good = TRUE;
foreach ($preprocess_files as $preprocess_file => $extra) {
// Empty aggregate, skip
if (empty($preprocess_file)) {
continue;
}
if ($extra !== FALSE && is_array($extra)) {
$prefix = $extra['prefix'];
$suffix = $extra['suffix'];
$output_preprocess[] = array(
'media' => $media,
'href' => advagg_build_uri($preprocess_file),
'prefix' => $prefix,
'suffix' => $suffix,
);
$files_aggregates_included[$media][$preprocess_file] = $extra;
}
else {
$good = FALSE;
break;
}
}
if (!$good) {
// Redo with aggregation turned off and return the new value.
watchdog('advagg', 'CSS aggregation failed. %filename could not be saved correctly.', array('%filename' => $preprocess_file), WATCHDOG_ERROR);
$data = advagg_process_css($original_css, TRUE);
return $data;
}
}
}
// Default function called: advagg_unlimited_css_builder
$function = variable_get('advagg_css_render_function', ADVAGG_CSS_RENDER_FUNCTION);
return $function($external_no_preprocess, $module_no_preprocess, $output_no_preprocess, $output_preprocess, $theme_no_preprocess, $inline_no_preprocess, $inline_included, $files_included, $files_aggregates_included);
}
/**
* Logic to figure out what kind of css tags to use.
*
* @param $external_no_preprocess
* array of css files ($media, $href)
* @param $module_no_preprocess
* array of css files ($media, $href)
* @param $output_no_preprocess
* array of css files ($media, $href)
* @param $output_preprocess
* array of css files ($media, $href, $prefix, $suffix)
* @param $theme_no_preprocess
* array of css files ($media, $href)
* @param $inline_no_preprocess
* array of css data to inline ($media, $data)
* @param $inline_included
* array of inline css included. $a[$media][] = $datablob;
* @param $files_included
* array of css files included. $a[$media][] = $filename
* @param $files_aggregates_included
* array of css files & aggregates included. $a[$media][] = $filename
* @return
* html for loading the css. html for the head.
*/
function advagg_unlimited_css_builder($external_no_preprocess, $module_no_preprocess, $output_no_preprocess, $output_preprocess, $theme_no_preprocess, $inline_no_preprocess, $files_included, $files_aggregates_included, $inline_included) {
global $user;
$styles = '';
$files = array_merge($external_no_preprocess, $module_no_preprocess, $output_no_preprocess, $output_preprocess, $theme_no_preprocess, $inline_no_preprocess);
// Select method for css html output
if (count($files) < variable_get('advagg_css_count_threshold', ADVAGG_CSS_COUNT_THRESHOLD)) {
advagg_unlimited_css_traditional($files, $styles);
}
elseif (variable_get('advagg_css_logged_in_ie_detect', ADVAGG_CSS_LOGGED_IN_IE_DETECT) && $user->uid != 0) {
// Detect IE browsers here
$is_ie = FALSE;
if (isset($_SERVER['HTTP_USER_AGENT'])) {
// Strings for testing found via
// http://chrisschuld.com/projects/browser-php-detecting-a-users-browser-from-php/
// Test for v1 - v1.5 IE
// Test for versions > 1.5
// Test for Pocket IE
if ( stristr($_SERVER['HTTP_USER_AGENT'], 'microsoft internet explorer')
|| stristr($_SERVER['HTTP_USER_AGENT'], 'msie')
|| stristr($_SERVER['HTTP_USER_AGENT'], 'mspie')
) {
$is_ie = TRUE;
}
}
// Play Safe and treat as IE if user agent is not set
else {
$is_ie = TRUE;
}
if ($is_ie) {
advagg_unlimited_css_import(array_merge($external_no_preprocess, $module_no_preprocess, $output_no_preprocess), $styles);
advagg_unlimited_css_import($output_preprocess, $styles);
advagg_unlimited_css_import($theme_no_preprocess, $styles);
advagg_unlimited_css_traditional($inline_no_preprocess, $styles);
}
else {
advagg_unlimited_css_traditional($files, $styles);
}
}
else {
advagg_unlimited_css_import(array_merge($external_no_preprocess, $module_no_preprocess, $output_no_preprocess), $styles);
advagg_unlimited_css_import($output_preprocess, $styles);
advagg_unlimited_css_import($theme_no_preprocess, $styles);
advagg_unlimited_css_traditional($inline_no_preprocess, $styles);
}
return $styles;
}
/**
* Use link tags for CSS
*
* @param $files
* array of css files ($media, $href, $prefix, $suffix)
* @param &$styles
* html string
*/
function advagg_unlimited_css_traditional($files, &$styles) {
$last_prefix = '';
$last_suffix = '';
foreach ($files as $css_file) {
$media = $css_file['media'];
$prefix = empty($css_file['prefix']) ? '' : $css_file['prefix'] . "\n";
$suffix = empty($css_file['suffix']) ? '' : $css_file['suffix'];
// Group prefixes and suffixes.
if (isset($css_file['href'])) {
$href = $css_file['href'];
if ($prefix != $last_prefix) {
$styles .= $last_suffix . "\n" . $prefix . '' . "\n";
}
else {
$styles .= '' . "\n";
}
}
else {
$data = $css_file['data'];
if ($prefix != $last_prefix) {
$styles .= $last_suffix . "\n" . $prefix . '' . "\n";
}
else {
$styles .= '' . "\n";
}
}
$last_prefix = $prefix;
$last_suffix = $suffix;
}
$styles .= $last_suffix . "\n";
}
/**
* Use import tags for CSS
*
* @param $files
* array of css files ($media, $href)
* @param &$styles
* html string
*/
function advagg_unlimited_css_import($files, &$styles) {
$counter = 0;
$media = NULL;
$import = '';
foreach ($files as $css_file) {
$media_new = $css_file['media'];
$href = $css_file['href'];
if ($media_new != $media || $counter > variable_get('advagg_css_count_threshold', ADVAGG_CSS_COUNT_THRESHOLD)) {
if ($media && !empty($import)) {
$styles .= "\n" . '';
$import = '';
}
$counter = 0;
$media = $media_new;
}
$import .= '@import "'. $href .'";'."\n";
$counter++;
}
if ($media && !empty($import)) {
$styles .= "\n".'';
}
}
/**
* Returns a themed presentation of all JavaScript code for the current page.
*
* @see drupal_get_js()
*
* References to JavaScript files are placed in a certain order: first, all
* 'core' files, then all 'module' and finally all 'theme' JavaScript files
* are added to the page. Then, all settings are output, followed by 'inline'
* JavaScript code. If running update.php, all preprocessing is disabled.
*
* @param $js_code
* An array with all JavaScript code. Key it the region
* @param $noagg
* (optional) Bool indicating that aggregation should be disabled if TRUE.
* @return
* All JavaScript code segments and includes for the scope as HTML tags.
*/
function advagg_process_js($master_set, $noagg = FALSE) {
global $conf;
if ((!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') && function_exists('locale_update_js_files')) {
locale_update_js_files();
}
// Get useful info.
list($preprocess_js, $public_downloads, $query_string) = advagg_process_css_js_prep($noagg);
$output = array();
foreach ($master_set as $scope => $javascript) {
if ($scope != 'header' && $scope != 'footer' && empty($javascript)) {
continue;
}
// Invoke hook_advagg_js_pre_alter() to give installed modules a chance to
// modify the data in the $javascript array if necessary.
drupal_alter('advagg_js_pre', $javascript, $preprocess_js, $public_downloads, $scope);
$master_set[$scope] = $javascript;
}
// Invoke hook_advagg_js_header_footer_alter() to give installed modules a chance to
// modify the data in the header and footer JS if necessary.
drupal_alter('advagg_js_header_footer', $master_set, $preprocess_js, $public_downloads);
foreach ($master_set as $scope => $javascript) {
if (empty($javascript)) {
continue;
}
// Set variables.
$setting_no_preprocess = array();
$inline_no_preprocess = array();
$external_no_preprocess = array();
$output_no_preprocess = array('core' => array(), 'module' => array(), 'theme' => array());
$output_preprocess = array();
$preprocess_list = array();
$js_settings_array = array();
$inline_included = array();
$files_included = array();
$files_aggregates_included = array();
// Process input.
foreach ($javascript as $type => $data) {
if (empty($data)) {
continue;
}
switch ($type) {
case 'setting':
$data = call_user_func_array('array_merge_recursive', $data);
$js_settings_array[] = $data;
$js_settings = advagg_drupal_to_js($data);
$js_settings = preg_replace(array('/"DRUPAL_JS_RAW\:/', '/\:DRUPAL_JS_RAW"/'), array('', ''), $js_settings);
$setting_no_preprocess[] = 'jQuery.extend(Drupal.settings, ' . $js_settings . ");";
break;
case 'inline':
foreach ($data as $info) {
// Invoke hook_advagg_js_inline_alter() to give installed modules a
// chance to modify the contents of $info['code'] if necessary.
drupal_alter('advagg_js_inline', $info['code']);
$inline_no_preprocess[] = array($info['code'], $info['defer']);
$inline_included[] = $info['code'];
}
break;
case 'external':
foreach ($data as $path => $info) {
$external_no_preprocess[] = array($path, $info['defer']);
$files_included[$path] = TRUE;
}
break;
default:
// If JS preprocessing is off, we still need to output the scripts.
// 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'] || !$public_downloads || !$preprocess_js) {
$output_no_preprocess[$type][] = array(advagg_build_uri($path) . ($info['cache'] ? $query_string : '?'. time()), $info['defer']);
$files_included[$path] = $info['preprocess'];
}
else {
$preprocess_list[$path] = $info;
}
}
}
}
// Aggregate any remaining JS files that haven't already been output.
if ($public_downloads && $preprocess_js && count($preprocess_list) > 0) {
$files_aggregates_included = $files_included;
$files = array();
foreach ($preprocess_list as $path => $info) {
if ($info['preprocess']) {
$files[] = $path;
$files_included[$path] = TRUE;
}
}
$preprocess_files = advagg_css_js_file_builder('js', $files, $query_string);
$good = TRUE;
foreach ($preprocess_files as $preprocess_file => $extra) {
// Empty aggregate, skip
if (empty($preprocess_file)) {
continue;
}
if ($extra !== FALSE && is_array($extra)) {
$prefix = $extra['prefix'];
$suffix = $extra['suffix'];
$output_preprocess[] = array(advagg_build_uri($preprocess_file), $prefix, $suffix);
$files_aggregates_included[$preprocess_file] = $extra;
}
else {
$good = FALSE;
break;
}
}
if (!$good) {
// Redo with aggregation turned off and return the new value.
watchdog('advagg', 'JS aggregation failed. %filename could not be saved correctly.', array('%filename' => $preprocess_file), WATCHDOG_ERROR);
$data = advagg_process_js($master_set, TRUE);
return $data;
}
}
// Default function called: advagg_js_builder
$function = variable_get('advagg_js_render_function', ADVAGG_JS_RENDER_FUNCTION);
$output[$scope] = $function($external_no_preprocess, $output_preprocess, $output_no_preprocess, $setting_no_preprocess, $inline_no_preprocess, $scope, $js_settings_array, $inline_included, $files_included, $files_aggregates_included);
}
return $output;
}
/**
* Build and theme JS output for header.
*
* @param $external_no_preprocess
* array(array($src, $defer))
* @param $output_preprocess
* array(array($src, $prefix, $suffix))
* @param $output_no_preprocess
* array(array(array($src, $defer)))
* @param $setting_no_preprocess
* array(array($code))
* @param $inline_no_preprocess
* array(array($code, $defer))
* @param $scope
* header or footer.
* @param $js_settings_array
* array of settings used.
* @param $inline_included
* array of inline scripts used.
* @param $files_included
* array of files used.
* @param $files_aggregates_included
* array of files and aggregates used.
* @return
* String of themed JavaScript.
*/
function advagg_js_builder($external_no_preprocess, $output_preprocess, $output_no_preprocess, $setting_no_preprocess, $inline_no_preprocess, $js_settings_array, $inline_included, $files_included, $files_aggregates_included) {
$output = '';
// For inline Javascript to validate as XHTML, all Javascript containing
// XHTML needs to be wrapped in CDATA. To make that backwards compatible
// with HTML 4, we need to comment out the CDATA-tag.
$embed_prefix = "\n\n";
// Keep the order of JS files consistent as some are preprocessed and others are not.
// Make sure any inline or JS setting variables appear last after libraries have loaded.
if (!empty($external_no_preprocess)) {
foreach ($external_no_preprocess as $values) {
list ($src, $defer) = $values;
$output .= '\n";
}
}
if (!empty($output_preprocess)) {
foreach ($output_preprocess as $values) {
list ($src, $prefix, $suffix) = $values;
$output .= $prefix . '' . $suffix . "\n";
}
}
foreach ($output_no_preprocess as $type => $list) {
if (!empty($list)) {
foreach ($list as $values) {
list ($src, $defer) = $values;
$output .= '\n";
}
}
}
if (!empty($setting_no_preprocess)) {
foreach ($setting_no_preprocess as $code) {
$output .= '\n";
}
}
if (!empty($inline_no_preprocess)) {
foreach ($inline_no_preprocess as $values) {
list ($code, $defer) = $values;
$output .= '\n";
}
}
return $output;
}
/**
* Always return TRUE, used for array_map in advagg_css_js_file_builder().
*/
function advagg_return_true() {
return TRUE;
}
/**
* Disable the page cache if the aggregate is not in the bundle.
*/
function advagg_disable_page_cache() {
global $conf;
if (variable_get('advagg_page_cache_mode', ADVAGG_PAGE_CACHE_MODE)) {
$conf['cache'] = CACHE_DISABLED;
// Invoke hook_advagg_disable_page_cache(). Allows 3rd party page cache
// plugins like boost or varnish to not cache this page.
module_invoke_all('advagg_disable_page_cache');
}
}
/**
* Aggregate CSS/JS files, putting them in the files directory.
*
* @see drupal_build_js_cache()
* @see drupal_build_css_cache()
*
* @param $type
* js or css
* @param $files
* An array of JS files to aggregate and compress into one file.
* @param $query_string
* (optional) Query string to add on to the file if bundle isn't ready.
* @param $counter
* (optional) Counter value.
* @param $force
* (optional) Rebuild even if file already exists.
* @param $md5
* (optional) Bundle's machine name.
* @return
* array with the filepath as the key and prefix and suffix in another array.
*/
function advagg_css_js_file_builder($type, $files, $query_string = '', $counter = FALSE, $force = FALSE, $md5 = '') {
global $_advagg, $base_path;
$data = '';
// Try cache first. When ever the counter changes this cache gets reset.
$cached_data_key = 'advagg_file_builder_' . md5(implode('', array_filter(array_unique($files))));
if (!$force) {
// Try cache first; cache table is cache_advagg_bundle_reuse.
$cached_data = advagg_cached_bundle_get($cached_data_key, 'file_builder_cache_object');
if (!empty($cached_data)) {
foreach ($cached_data as $filepath => $values) {
// Ping cache.
advagg_bundle_built($filepath);
}
return $cached_data;
}
}
list($css_path, $js_path) = advagg_get_root_files_dir();
if ($type == 'js') {
$file_type_path = $js_path;
}
if ($type == 'css') {
$file_type_path = $css_path;
}
// Send $files, get filename back
$filenames = advagg_get_filename($files, $type, $counter, $md5);
// Debugging.
if (variable_get('advagg_debug', ADVAGG_DEBUG)) {
$_advagg['debug']['file_builder_get_filenames'][] = array(
'key' => $cached_data_key,
'filenames' => $filenames,
);
}
$output = array();
$locks = array();
$cacheable = TRUE;
$files_used = array();
foreach ($filenames as $info) {
$filename = $info['filename'];
$files = $info['files'];
$bundle_md5 = $info['bundle_md5'];
$prefix = '';
$suffix = '';
$filepath = $file_type_path .'/'. $filename;
// Invoke hook_advagg_js_extra_alter() or hook_advagg_css_extra_alter to
// give installed modules a chance to modify the prefix or suffix for a
// given filename.
$values = array($filename, $bundle_md5, $prefix, $suffix);
drupal_alter('advagg_' . $type . '_extra', $values);
list($filename, $bundle_md5, $prefix, $suffix) = $values;
// Check that the file exists & filesize is not zero
$built = advagg_bundle_built($filepath);
if (!$built || $force) {
// Generate on request?
if (variable_get('advagg_async_generation', ADVAGG_ASYNC_GENERATION) && !$force) {
// Build request.
$url = _advagg_build_url($filepath . '?generator=1');
$headers = array(
'Host' => $_SERVER['HTTP_HOST'],
);
// Request file.
if (function_exists('stream_socket_client') && function_exists('stream_select')) {
advagg_async_connect_http_request($url, array('headers' => $headers));
}
else {
// Set timeout.
$socket_timeout = ini_set('default_socket_timeout', variable_get('advagg_socket_timeout', ADVAGG_SOCKET_TIMEOUT));
drupal_http_request($url, $headers, 'GET');
ini_set('default_socket_timeout', $socket_timeout);
}
// Return filepath if we are going to wait for the bundle to be
// generated or if the bundle already exists.
if (variable_get('advagg_aggregate_mode', ADVAGG_AGGREGATE_MODE) < 2 || advagg_bundle_built($filepath)) {
$output[$filepath] = array('prefix' => $prefix, 'suffix' => $suffix, 'files' => array_map('advagg_return_true', array_flip($files)));
}
else {
// Aggregate isn't built yet, send back the files that where going to
// be in it.
foreach ($files as $file) {
$output[$file . $query_string] = array('prefix' => '', 'suffix' => '', 'files' => array($file . $query_string => TRUE));
}
$cacheable = FALSE;
advagg_disable_page_cache();
}
continue;
}
// Only generate once.
$lock_name = 'advagg_' . $filename;
if (!lock_acquire($lock_name)) {
if (variable_get('advagg_aggregate_mode', ADVAGG_AGGREGATE_MODE) == 0 ) {
$locks[] = array($lock_name => $filepath);
$output[$filepath] = array('prefix' => $prefix, 'suffix' => $suffix, 'files' => array_map('advagg_return_true', array_flip($files)));
}
else {
// Aggregate isn't built yet, send back the files that where going
// to be in it.
foreach ($files as $file) {
$output[$file . $query_string] = array('prefix' => '', 'suffix' => '', 'files' => array($file . $query_string => TRUE));
}
$cacheable = FALSE;
advagg_disable_page_cache();
}
continue;
}
if ($type == 'css') {
$data = advagg_build_css_bundle($files);
}
elseif ($type == 'js') {
$data = advagg_build_js_bundle($files);
}
// Invoke hook_advagg_js_alter() or hook_advagg_css_alter to give
// installed modules a chance to modify the data in the bundle if
// necessary.
drupal_alter('advagg_' . $type, $data, $files, $bundle_md5);
$files_used = array_merge($files_used, $files);
// If data is empty then do not include this bundle in the final output.
if (empty($data) && !$force) {
lock_release($lock_name);
continue;
}
// Create the advagg_$type/ within the files folder.
file_check_directory($file_type_path, FILE_CREATE_DIRECTORY);
// Write file. default function called: advagg_file_saver
$function = variable_get('advagg_file_save_function', ADVAGG_FILE_SAVE_FUNCTION);
$good = $function($data, $filepath, $force, $type);
// Release lock.
lock_release($lock_name);
// If file save was not good then downgrade to non aggregated mode.
if (!$good) {
$output[$filepath] = FALSE;
$cacheable = FALSE;
continue;
}
}
else {
$files_used = array_merge($files_used, $files);
}
$output[$filepath] = array('prefix' => $prefix, 'suffix' => $suffix, 'files' => array_map('advagg_return_true', array_flip($files)));
}
// Wait for all locks before returning.
if (!empty($locks)) {
foreach ($locks as $lock_name => $filepath) {
lock_wait($lock_name);
if (!advagg_bundle_built($filepath)) {
$output[$filepath] = FALSE;
}
}
}
if (empty($output)) {
$output[] = FALSE;
return $output;
}
// Cache the output
if (!$force && $cacheable) {
$new_cached_data_key = 'advagg_file_builder_' . md5(implode('', array_filter(array_unique($files_used))));
// Verify the files in equals the files out.
if ($new_cached_data_key == $cached_data_key) {
cache_set($cached_data_key, $output, 'cache_advagg_bundle_reuse', CACHE_TEMPORARY);
}
}
return $output;
}
/**
* Given a list of files, grab their contents and glue it into one big string.
*
* @param $files
* array of filenames.
* @return
* string containing all the files.
*/
function advagg_build_css_bundle($files) {
$data = '';
// Build aggregate CSS file.
foreach ($files as $file) {
$contents = drupal_load_stylesheet($file, TRUE);
// Return the path to where this CSS file originated from.
$base = base_path() . dirname($file) .'/';
_drupal_build_css_path(NULL, $base);
// Prefix all paths within this CSS file, ignoring external and absolute paths.
$data .= preg_replace_callback('/url\([\'"]?(?![a-z]+:|\/+)([^\'")]+)[\'"]?\)/i', '_drupal_build_css_path', $contents);
}
// Per the W3C specification at http://www.w3.org/TR/REC-CSS2/cascade.html#at-import,
// @import rules must proceed any other style, so we move those to the top.
$regexp = '/@import[^;]+;/i';
preg_match_all($regexp, $data, $matches);
$data = preg_replace($regexp, '', $data);
$data = implode('', $matches[0]) . $data;
return $data;
}
/**
* Given a list of files, grab their contents and glue it into one big string.
*
* @param $files
* array of filenames.
* @return
* string containing all the files.
*/
function advagg_build_js_bundle($files) {
if (empty($files)) {
return '';
}
$data = '';
// Build aggregate JS file.
foreach ($files as $file) {
// Append a ';' and a newline after each JS file to prevent them from running together.
if (advagg_file_exists($file)) {
$data .= file_get_contents($file) .";\n";
}
}
return $data;
}
/**
* Use a cache table to see if a file exists.
*
* @param $filename
* name of file
* @return
* TRUE or FALSE
*/
function advagg_file_exists($filename) {
static $files = array();
if (empty($files)) {
$data = cache_get('advagg_file_checksum', 'cache');
if (empty($data->data)) {
$result = db_query("SELECT filename, checksum FROM {advagg_files}");
while ($row = db_fetch_array($result)) {
$files[$row['filename']] = $row['checksum'];
}
cache_set('advagg_file_checksum', $files, 'cache', CACHE_TEMPORARY);
}
else {
$files = $data->data;
}
}
if (!empty($files[$filename]) && $files[$filename] != -1) {
return TRUE;
}
else {
advagg_clearstatcache(TRUE, $filename);
return file_exists($filename);
}
}
/**
* Send out a fast 404 and exit.
*/
function advagg_missing_fast404($msg = '') {
global $base_path;
if (!headers_sent()) {
header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found');
header('X-AdvAgg: Failed Validation. ' . $msg);
}
print '' . "\n";
print '';
print '
The requested URL was not found on this server.
'; print ''; print ''; print ''; exit(); } /** * Generate .htaccess rules and place them in advagg dir * * @param $dest * destination of the file that just got saved. * @param $force * force recreate the .htaccess file. */ function advagg_htaccess_check_generate($dest, $force = FALSE) { global $base_path; if (!$force && !variable_get('advagg_dir_htaccess', ADVAGG_DIR_HTACCESS)) { return TRUE; } $dir = dirname($dest); $htaccess_file = $dir . '/.htaccess'; advagg_clearstatcache(TRUE, $htaccess_file); if (!$force && file_exists($htaccess_file)) { return TRUE; } list($css_path, $js_path) = advagg_get_root_files_dir(); $type = ''; if ($dir == $js_path) { $ext = 'js'; $path = $js_path; $type = 'text/javascript'; } elseif ($dir == $css_path) { $ext = 'css'; $path = $css_path; $type = 'text/css'; } else { return FALSE; } $data = "\n"; if (variable_get('advagg_gzip_compression', ADVAGG_GZIP_COMPRESSION)) { $data .= "