' . $readme . ''; } } else { $output = '
' . $readme . ''; } return $output; } } /** * Implements hook_block_view_alter(). */ function advagg_block_view_alter(&$data, $block) { // Do not run hook if AdvAgg is disabled. if (!advagg_enabled()) { return; } // Do not run hook if setting is disabled. if (!variable_get('advagg_scripts_scope_anywhere', ADVAGG_SCRIPTS_SCOPE_ANYWHERE)) { return; } if (empty($data) || empty($data['content'])) { return; } $block_info = $block->module . ':' . $block->delta; $prefix = ""; $suffix = ""; if (is_string($data['content'])) { $data['content'] = $prefix . $data['content'] . $suffix; } else { if (!isset($data['content']['#prefix'])) { $data['content']['#prefix'] = ''; } $data['content']['#prefix'] .= $prefix; if (!isset($data['content']['#suffix'])) { $data['content']['#suffix'] = ''; } $data['content']['#suffix'] .= $suffix; } } /** * Implements hook_views_pre_render(). */ function advagg_views_pre_render(&$view) { // Do not run hook if AdvAgg is disabled. if (!advagg_enabled()) { return; } // Do not run hook if setting is disabled. if (!variable_get('advagg_scripts_scope_anywhere', ADVAGG_SCRIPTS_SCOPE_ANYWHERE)) { return; } $info = "{$view->name}:{$view->current_display}"; $prefix = ""; $suffix = ""; if (!isset($view->attachment_before)) { $view->attachment_before = ''; } $view->attachment_before .= $prefix; if (!isset($view->attachment_after)) { $view->attachment_after = ''; } $view->attachment_after .= $suffix; } /** * Implements hook_panels_pre_render(). */ function advagg_panels_pre_render($panels_display, &$renderer) { // Do not run hook if AdvAgg is disabled. if (!advagg_enabled()) { return; } // Do not run hook if setting is disabled. if (!variable_get('advagg_scripts_scope_anywhere', ADVAGG_SCRIPTS_SCOPE_ANYWHERE)) { return; } $info = "{$panels_display->layout}:{$panels_display->css_id}"; $prefix = ""; $suffix = ""; if (!isset($renderer->prefix)) { $renderer->prefix = ''; } $renderer->prefix .= $prefix; if (!isset($renderer->suffix)) { $renderer->suffix = ''; } $renderer->suffix .= $suffix; } /** * Implements hook_url_inbound_alter(). * * Inbound URL rewrite helper. If host includes subdomain, rewrite URI and * internal path if necessary. */ function advagg_url_inbound_alter(&$path, $original_path, $path_language) { // Do nothing if this has been disabled. if (!variable_get('advagg_url_inbound_alter', ADVAGG_URL_INBOUND_ALTER)) { return; } // Setup static so we only need to run the logic once. $already_ran = &drupal_static(__FUNCTION__); if (!isset($already_ran)) { $already_ran = array(); } $request_path = request_path(); // Set the path again if we already did this alter. if (array_key_exists($request_path, $already_ran)) { $path = $already_ran[$request_path]; return; } // If requested path was for an advagg file but now it is something else // switch is back to the advagg file. if (!empty($path) && $path != $request_path && advagg_match_file_pattern($request_path) ) { // Get the advagg paths. $advagg_path = advagg_get_root_files_dir(); // Get the top level path. $top_level = substr($advagg_path[0][1], 0, strpos($advagg_path[0][1], 'advagg_css')); // Only change if it's an exact match. $start = strpos($request_path, $top_level . 'advagg_'); if ($start === 0) { // Set path to correct advagg path. $path = substr($request_path, $start); $already_ran[$request_path] = $path; } else { // Put all languages prefixes into an array. $language_list = language_list(); $prefixes = array(); foreach ($language_list as $lang) { if ($lang->enabled && !empty($lang->prefix) && strpos($request_path, $lang->prefix) !== FALSE) { $prefixes[$lang->prefix] = $lang->prefix; } } if (!empty($prefixes)) { // Remove all enabled languages prefixes from the beginning of the path. $substr_to_shrink = substr($request_path, 0, $start); foreach ($prefixes as $prefix) { $substr_to_shrink = str_replace($prefix . '/', '', $substr_to_shrink); } // Set path to correct advagg path. $path = $substr_to_shrink . substr($request_path, $start); $already_ran[$request_path] = $path; } } } } /** * Implements hook_hook_info(). */ function advagg_hook_info() { // List of hooks that can be inside of *.advagg.inc files. // All advagg hooks except for: // advagg_current_hooks_hash_array_alter // advagg_hooks_implemented_alter // advagg_get_root_files_dir_alter // because these 3 hooks are used on most requests. $advagg_hooks = array( 'advagg_get_css_file_contents_pre_alter', 'advagg_get_css_file_contents_alter', 'advagg_get_js_file_contents_alter', 'advagg_get_css_aggregate_contents_alter', 'advagg_get_js_aggregate_contents_alter', 'advagg_save_aggregate_pre_alter', 'advagg_save_aggregate_alter', 'advagg_build_aggregate_plans_alter', 'advagg_build_aggregate_plans_post_alter', 'advagg_css_groups_alter', 'advagg_js_groups_alter', 'advagg_modify_css_pre_render_alter', 'advagg_modify_js_pre_render_alter', 'advagg_changed_files', 'advagg_removed_aggregates', 'advagg_scan_for_changes', 'advagg_get_info_on_files_alter', 'advagg_context_alter', 'advagg_missing_root_file', ); $hooks = array(); foreach ($advagg_hooks as $hook) { $hooks[$hook] = array('group' => 'advagg'); } return $hooks; } /** * Implements hook_module_implements_alter(). */ function advagg_module_implements_alter(&$implementations, $hook) { // Move advagg_theme_registry_alter to the top. if ($hook === 'theme_registry_alter' && array_key_exists('advagg', $implementations)) { $item = array('advagg' => $implementations['advagg']); unset($implementations['advagg']); $implementations = array_merge($item, $implementations); } // Move advagg_ajax_render_alter to the top. if ($hook === 'ajax_render_alter' && array_key_exists('advagg', $implementations)) { $item = array('advagg' => $implementations['advagg']); unset($implementations['advagg']); $implementations = array_merge($item, $implementations); } // Move advagg_element_info_alter to the bottom. if ($hook === 'element_info_alter' && array_key_exists('advagg', $implementations)) { $item = $implementations['advagg']; unset($implementations['advagg']); $implementations['advagg'] = $item; } // Replace locale_js_alter with _advagg_locale_js_alter. if ($hook === 'js_alter' && array_key_exists('locale', $implementations)) { unset($implementations['locale']); $implementations['_advagg_locale'] = FALSE; } // Move advagg_file_url_alter to the bottom. if ($hook === 'file_url_alter' && array_key_exists('advagg', $implementations)) { $item = $implementations['advagg']; unset($implementations['advagg']); $implementations['advagg'] = $item; } if ($hook === 'requirements') { // Move advagg_requirements to the bottom. if (array_key_exists('advagg', $implementations)) { $item = $implementations['advagg']; unset($implementations['advagg']); $implementations['advagg'] = $item; } // Move advagg_css_cdn to the bottom. if (array_key_exists('advagg_css_cdn', $implementations)) { $item = $implementations['advagg_css_cdn']; unset($implementations['advagg_css_cdn']); $implementations['advagg_css_cdn'] = $item; } // Move advagg_css_compress to the bottom. if (array_key_exists('advagg_css_compress', $implementations)) { $item = $implementations['advagg_css_compress']; unset($implementations['advagg_css_compress']); $implementations['advagg_css_compress'] = $item; } // Move advagg_js_cdn to the bottom. if (array_key_exists('advagg_js_cdn', $implementations)) { $item = $implementations['advagg_js_cdn']; unset($implementations['advagg_js_cdn']); $implementations['advagg_js_cdn'] = $item; } // Move advagg_js_compress to the bottom. if (array_key_exists('advagg_js_compress', $implementations)) { $item = $implementations['advagg_js_compress']; unset($implementations['advagg_js_compress']); $implementations['advagg_js_compress'] = $item; } } // Move advagg_cron to the bottom. if ($hook === 'cron' && array_key_exists('advagg', $implementations)) { $item = $implementations['advagg']; unset($implementations['advagg']); $implementations['advagg'] = $item; } } /** * Implements hook_js_alter(). * * This is a locking wrapper for locale_js_alter(). */ function _advagg_locale_js_alter(&$js) { // If the variable is empty then get the latest variable from the database. $name = 'javascript_parsed'; $parsed = variable_get($name, array()); if (empty($parsed)) { $variables = array_map('unserialize', db_query('SELECT name, value FROM {variable} WHERE name = :name', array(':name' => $name))->fetchAllKeyed()); if (!empty($variables[$name])) { $GLOBALS['conf'][$name] = $variables[$name]; } } // See if locale_js_alter() needs to do anything. $dir = 'public://' . variable_get('locale_js_directory', 'languages'); $new_files = FALSE; // See if a rebuild of the translation file for the current language is // needed. if (!empty($parsed['refresh:' . $GLOBALS['language']->language])) { $new_files = TRUE; } // Check for new js source files. if (empty($new_files)) { foreach ($js as $item) { if ($item['type'] === 'file' && !in_array($item['data'], $parsed) && substr($item['data'], 0, strlen($dir)) != $dir ) { $new_files = TRUE; break; } } } if (empty($new_files)) { // No new files to manage, just add in available i18n files. advagg_locale_js_add_translations($js, $dir); // Exit function. return; } $count = 0; while (!lock_acquire('locale_js_alter', 10)) { ++$count; // If we've waited over 3 times then skip. if ($count > 3) { lock_release('locale_js_alter'); // Add in available i18n files. advagg_locale_js_add_translations($js, $dir); // Disable saving to the cache as translations might be missing. drupal_page_is_cacheable(FALSE); if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) > 1) { $GLOBALS['conf']['advagg_cache_level'] = 0; } return; } // Wait for the lock to be available. lock_wait('locale_js_alter'); } try { // Run the alter. locale_js_alter($js); } catch (PDOException $e) { // If it fails we don't care, javascript_parsed is either already written or // it will happen again on the next request. // Still log it if in development mode. if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) < 0) { watchdog('advagg', 'Development Mode - Caught PDO Exception:
@info
', array('@info' => $e));
}
}
lock_release('locale_js_alter');
}
/**
* Implements hook_system_info_alter().
*/
function advagg_system_info_alter(&$info, $file, $type) {
$config_path = &drupal_static(__FUNCTION__);
// Get advagg config path.
if (empty($config_path)) {
$config_path = advagg_admin_config_root_path();
}
// Replace advagg path.
if (!empty($info['configure'])
&& strpos($info['configure'], '/advagg') !== FALSE
&& ((!empty($info['dependencies'])
&& is_array($info['dependencies'])
&& in_array('advagg', $info['dependencies'])
) || $file->name === 'advagg')
) {
$pos = strpos($info['configure'], '/advagg') + 7;
$substr = substr($info['configure'], 0, $pos);
$info['configure'] = str_replace($substr, $config_path . '/advagg', $info['configure']);
}
}
/**
* Implements hook_permission().
*/
function advagg_permission() {
return array(
'bypass advanced aggregation' => array(
'title' => t('bypass advanced aggregation'),
'description' => t('User can use URL query strings to bypass AdvAgg.'),
),
);
}
/**
* Implements hook_file_url_alter().
*/
function advagg_file_url_alter(&$original_uri) {
// Do nothing if URI does not contain /advagg_
// OR file does not have the correct pattern.
if (strpos($original_uri, '/advagg_') === FALSE || !advagg_match_file_pattern($original_uri)) {
return;
}
// CDN fix.
// Do nothing if
// in maintenance_mode
// CDN module does not exist
// CDN far future is disabled
// CDN mode is not basic
// URI does not contain cdn/farfuture/.
if (variable_get('maintenance_mode', FALSE)
|| !module_exists('cdn')
|| !variable_get(CDN_BASIC_FARFUTURE_VARIABLE, CDN_BASIC_FARFUTURE_DEFAULT)
|| variable_get(CDN_MODE_VARIABLE, CDN_MODE_BASIC) != CDN_MODE_BASIC
|| strpos($original_uri, 'cdn/farfuture/') === FALSE
) {
return;
}
// Remove cdn/farfuture/BASE64/prefix:value/ from the URI.
$original_uri = preg_replace('/cdn\/farfuture\/[A-Za-z0-9-_]{43}\/[A-Za-z]+\:[A-Za-z0-9-_]+\//', '', $original_uri);
}
/**
* Implements hook_menu().
*/
function advagg_menu() {
list($css_path, $js_path) = advagg_get_root_files_dir();
$file_path = drupal_get_path('module', 'advagg');
$config_path = advagg_admin_config_root_path();
$path_defined = FALSE;
if (module_exists('s3fs') && is_callable('_s3fs_get_config')) {
$s3fs_config = _s3fs_get_config();
if (empty($s3fs_config['no_rewrite_cssjs'])) {
$external_css = trim(parse_url(str_replace('/test.css', '/%', file_create_url($css_path[0] . '/test.css')), PHP_URL_PATH));
if (strpos($external_css, $GLOBALS['base_path']) === 0) {
$external_css = substr($external_css, strlen($GLOBALS['base_path']));
}
$external_js = trim(parse_url(str_replace('/test.js', '/%', file_create_url($js_path[0] . '/test.js')), PHP_URL_PATH));
if (strpos($external_js, $GLOBALS['base_path']) === 0) {
$external_js = substr($external_js, strlen($GLOBALS['base_path']));
}
$items[$external_css] = array(
'title' => "Generate CSS Aggregate",
'page callback' => 'advagg_missing_aggregate',
'type' => MENU_CALLBACK,
// Allow anyone to access these public css files.
'access callback' => TRUE,
'file path' => $file_path,
'file' => 'advagg.missing.inc',
);
$items[$external_js] = array(
'title' => "Generate CSS Aggregate",
'page callback' => 'advagg_missing_aggregate',
'type' => MENU_CALLBACK,
// Allow anyone to access these public css files.
'access callback' => TRUE,
'file path' => $file_path,
'file' => 'advagg.missing.inc',
);
$path_defined = TRUE;
}
}
if (!$path_defined) {
$items[$css_path[1] . '/%'] = array(
'title' => "Generate CSS Aggregate",
'page callback' => 'advagg_missing_aggregate',
'type' => MENU_CALLBACK,
// Allow anyone to access these public css files.
'access callback' => TRUE,
'file path' => $file_path,
'file' => 'advagg.missing.inc',
);
$items[$js_path[1] . '/%'] = array(
'title' => "Generate JS Aggregate",
'page callback' => 'advagg_missing_aggregate',
'type' => MENU_CALLBACK,
// Allow anyone to access these public js files.
'access callback' => TRUE,
'file path' => $file_path,
'file' => 'advagg.missing.inc',
);
}
$items[$config_path . '/default'] = array(
'title' => 'Performance',
'type' => MENU_DEFAULT_LOCAL_TASK,
'file path' => drupal_get_path('module', 'system'),
'weight' => -10,
);
$items[$config_path . '/advagg'] = array(
'title' => 'Advanced CSS/JS Aggregation',
'description' => 'Configuration for Advanced CSS/JS Aggregation.',
'page callback' => 'drupal_get_form',
'page arguments' => array('advagg_admin_settings_form'),
'type' => MENU_LOCAL_TASK,
'access arguments' => array('administer site configuration'),
'file path' => $file_path,
'file' => 'advagg.admin.inc',
'weight' => 1,
);
$items[$config_path . '/advagg/config'] = array(
'title' => 'Configuration',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items[$config_path . '/advagg/info'] = array(
'title' => 'Information',
'description' => 'More detailed information about advagg.',
'page callback' => 'drupal_get_form',
'page arguments' => array('advagg_admin_info_form'),
'type' => MENU_LOCAL_TASK,
'access arguments' => array('administer site configuration'),
'file path' => $file_path,
'file' => 'advagg.admin.inc',
'weight' => 18,
);
$items[$config_path . '/advagg/operations'] = array(
'title' => 'Operations',
'description' => 'Flush caches, set the bypass cookie, take drastic actions.',
'page callback' => 'drupal_get_form',
'page arguments' => array('advagg_admin_operations_form'),
'type' => MENU_LOCAL_TASK,
'access arguments' => array('administer site configuration'),
'file path' => $file_path,
'file' => 'advagg.admin.inc',
'weight' => 20,
);
return $items;
}
/**
* Implements hook_cron().
*
* This will be ran once a day at most.
*/
function advagg_cron($bypass_time_check = FALSE) {
// @param bool $bypass_time_check
// Set to TRUE to skip the 24 hour check.
//
// Execute once a day (24 hours).
if (!$bypass_time_check && variable_get('advagg_cron_timestamp', 0) > (REQUEST_TIME - variable_get('advagg_cron_frequency', ADVAGG_CRON_FREQUENCY))) {
return array();
}
variable_set('advagg_cron_timestamp', REQUEST_TIME);
// Flush the cache_advagg_info cache bin.
cache_clear_all(NULL, 'cache_advagg_info');
$return = array();
// Clear out all stale advagg aggregated files.
module_load_include('inc', 'advagg', 'advagg.cache');
$return[] = advagg_delete_stale_aggregates();
// Delete all empty aggregated files.
$return[] = advagg_delete_empty_aggregates();
// Delete orphaned aggregates.
$return[] = advagg_delete_orphaned_aggregates();
// Remove aggregates that include missing files.
$return[] = advagg_remove_missing_files_from_db();
// Remove unused aggregates.
$return[] = advagg_remove_old_unused_aggregates();
// Remove expired locks from the semaphore database table.
$return[] = advagg_cleanup_semaphore_table();
// Remove old temp files.
$return[] = advagg_remove_temp_files();
// Refresh all locale files.
$return[] = advagg_refresh_all_locale_files();
// Update libraries data.
advagg_get_remote_libraries_versions(TRUE);
return $return;
}
/**
* Implements hook_flush_caches().
*/
function advagg_flush_caches($all_bins = FALSE, $push_new_changes = TRUE) {
// * @param bool $all_bins
// * TRUE: Get all advagg cache bins.
// * @param bool $push_new_changes
// * FALSE: Do not scan for changes.
//
// Send back a blank array if aav table doesn't exist.
if (!db_table_exists('advagg_aggregates_versions')) {
return array();
}
// Scan for and push new changes.
module_load_include('inc', 'advagg', 'advagg.cache');
if ($push_new_changes) {
advagg_push_new_changes();
}
// Get list of cache bins to clear.
$bins = array('cache_advagg_aggregates');
if ($all_bins) {
$bins[] = 'cache_advagg_info';
}
return $bins;
}
/**
* Implements hook_element_info_alter().
*/
function advagg_element_info_alter(&$type) {
// Replace drupal_pre_render_styles with advagg_pre_render_styles.
$type['styles']['#items'] = array();
if (!isset($type['styles']['#pre_render'])) {
$type['styles']['#pre_render'] = array();
}
$key = array_search('drupal_pre_render_styles', $type['styles']['#pre_render']);
if ($key !== FALSE) {
$type['styles']['#pre_render'][$key] = 'advagg_pre_render_styles';
}
else {
$type['styles']['#pre_render'][] = 'advagg_pre_render_styles';
}
// Allow for other code to easily change the render with alter hooks.
$type['styles']['#pre_render'][] = 'advagg_modify_css_pre_render';
$type['styles']['#group_callback'] = 'drupal_group_css';
// Swap in our own aggregation callback.
$type['styles']['#aggregate_callback'] = '_advagg_aggregate_css';
$type['styles']['#type'] = 'styles';
// Replace drupal_pre_render_scripts with advagg_pre_render_scripts.
$type['scripts']['#items'] = array();
if (!isset($type['scripts']['#pre_render'])) {
$type['scripts']['#pre_render'] = array();
}
$key_drupal = array_search('drupal_pre_render_scripts', $type['scripts']['#pre_render']);
$key_omega = array_search('omega_pre_render_scripts', $type['scripts']['#pre_render']);
$key_aurora = array_search('aurora_pre_render_scripts', $type['scripts']['#pre_render']);
if ($key_drupal !== FALSE) {
$type['scripts']['#pre_render'][$key_drupal] = 'advagg_pre_render_scripts';
}
elseif ($key_omega !== FALSE) {
$type['scripts']['#pre_render'][$key_omega] = 'advagg_pre_render_scripts';
}
elseif ($key_aurora !== FALSE) {
$type['scripts']['#pre_render'][$key_aurora] = 'advagg_pre_render_scripts';
}
else {
$type['scripts']['#pre_render'][] = 'advagg_pre_render_scripts';
}
// Allow for other code to easily change the render with alter hooks.
$type['scripts']['#pre_render'][] = 'advagg_modify_js_pre_render';
$type['scripts']['#group_callback'] = 'advagg_group_js';
// Swap in our own aggregation callback.
$type['scripts']['#aggregate_callback'] = '_advagg_aggregate_js';
$type['scripts']['#type'] = 'scripts';
// Copy html_tag to html_script_tag.
$type['html_script_tag'] = $type['html_tag'];
$type['html_script_tag']['#theme'] = 'html_script_tag';
$type['html_script_tag']['#type'] = 'html_script_tag';
}
/**
* Implements hook_theme_registry_alter().
*
* Replace template_process_html with _advagg_process_html.
*/
function advagg_theme_registry_alter(&$theme_registry) {
if (!isset($theme_registry['html'])) {
return;
}
// Replace core's process function with our own.
$index = array_search('template_process_html', $theme_registry['html']['process functions']);
if ($index !== FALSE) {
$theme_registry['html']['process functions'][$index] = '_advagg_process_html';
}
else {
// Put AdvAgg at the bottom if we can't find the replacement.
$theme_registry['html']['process functions'][] = '_advagg_process_html';
}
// Copy html_tag to html_script_tag.
$theme_registry['html_script_tag'] = $theme_registry['html_tag'];
$theme_registry['html_script_tag']['function'] = 'theme_html_script_tag';
// Fix imce_page.
if (isset($theme_registry['imce_page'])) {
$advagg_path = drupal_get_path('module', 'advagg');
$imce_path = drupal_get_path('module', 'imce');
if (strpos($theme_registry['imce_page']['path'], $imce_path) !== FALSE) {
$theme_registry['imce_page']['path'] = $advagg_path . '/tpl';
}
}
}
/**
* Implements hook_ajax_render_alter().
*/
function advagg_ajax_render_alter(&$commands) {
// Do not run hook if AdvAgg is disabled.
if (!advagg_enabled()) {
return;
}
// Do not run hook if advagg_ajax_render_alter is FALSE.
if (!variable_get('advagg_ajax_render_alter', ADVAGG_AJAX_RENDER_ALTER)) {
return;
}
// Conditionally adds the default Drupal/jQuery libraries to the page.
// @see http://drupal.org/node/1279226
if (function_exists('drupal_add_js_page_defaults')) {
drupal_add_js_page_defaults();
}
// Get Core JS.
list(, $core_scripts_header, $core_scripts_footer, $items, $settings) = advagg_build_ajax_js_css();
// Get AdvAgg JS.
$scripts_header = $scripts_footer = '';
if (!empty($items['js'])) {
$scripts_footer_array = advagg_get_js('footer', $items['js'], TRUE);
// Function advagg_pre_render_scripts() gets called here.
$scripts_footer = drupal_render($scripts_footer_array);
$scripts_header_array = advagg_get_js('header', $items['js'], TRUE);
// Function advagg_pre_render_scripts() gets called here.
$scripts_header = drupal_render($scripts_header_array);
}
// Remove core JS.
foreach ($commands as $key => $values) {
// Skip if not an array or not a command.
if (!is_array($values) || empty($values['command'])) {
continue;
}
if ($values['command'] === 'settings'
&& is_array($values['settings'])
&& !empty($values['merge'])
) {
// Remove JS settings.
unset($commands[$key]);
continue;
}
if ($values['command'] === 'insert'
&& is_null($values['settings'])
&& $values['method'] === 'prepend'
&& $values['data'] == $core_scripts_header
) {
// Remove JS header.
unset($commands[$key]);
continue;
}
if ($values['command'] === 'insert'
&& is_null($values['settings'])
&& $values['method'] === 'append'
&& $values['data'] == $core_scripts_footer
) {
// Remove JS footer.
unset($commands[$key]);
continue;
}
}
// Add in AdvAgg JS.
$extra_commands = array();
if (!empty($scripts_header)) {
$extra_commands[] = ajax_command_prepend('head', $scripts_header);
}
if (!empty($scripts_footer)) {
$extra_commands[] = ajax_command_append('body', $scripts_footer);
}
if (!empty($extra_commands)) {
$commands = array_merge($extra_commands, $commands);
}
if (!empty($settings)) {
array_unshift($commands, ajax_command_settings(advagg_cleanup_settings_array(drupal_array_merge_deep_array(array_filter($settings['data'], 'is_array'))), TRUE));
}
}
/**
* Implements hook_preprocess_page().
*/
function advagg_preprocess_page() {
// Scan for changes to any CSS/JS files if in development mode.
advagg_scan_filesystem_for_changes_live();
}
/**
* Implements hook_preprocess_html().
*
* Add in rendering IE meta tag if "combine CSS" is enabled.
*/
function advagg_preprocess_html() {
// http://www.phpied.com/conditional-comments-block-downloads/#update
// Prevent conditional comments from stalling css downloads.
$fix_blocking_css_ie = array(
'#weight' => '-999999',
'#type' => 'markup',
'#markup' => "\n",
);
// Add markup for IE conditional comments to head.
drupal_add_html_head($fix_blocking_css_ie, 'fix_blocking_css_ie');
// Do not force IE rendering mode if "combine CSS" is disabled.
if (!variable_get('advagg_combine_css_media', ADVAGG_COMBINE_CSS_MEDIA)) {
return;
}
// Send IE meta tag to force IE rendering mode header.
$x_ua_compatible = 'IE=edge';
if (variable_get('advagg_chrome_header_enabled', ADVAGG_CHROME_HEADER_ENABLED)) {
$x_ua_compatible .= ',chrome=1';
}
drupal_add_http_header('X-UA-Compatible', $x_ua_compatible);
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Give advice on how to temporarily disable css/js aggregation.
*/
function advagg_form_system_performance_settings_alter(&$form, &$form_state) {
module_load_include('admin.inc', 'advagg');
advagg_admin_system_performance_settings_form($form, $form_state);
}
/**
* Implements hook_js_alter().
*/
function advagg_js_alter(&$js) {
if (module_exists('admin_menu')) {
// Fix for admin menu; put JS in footer.
$path = drupal_get_path('module', 'admin_menu');
$filename = $path . '/admin_menu.js';
if (isset($js[$filename])) {
$js[$filename]['scope'] = 'footer';
}
}
}
/**
* @} End of "addtogroup hooks".
*/
/**
* @defgroup 3rd_party_hooks 3rd party hook implementations
* @{
* Hooks that are not apart of core or AdvAgg.
*/
/**
* Implements hook_cron_alter().
*/
function advagg_cron_alter(&$data) {
// Run this cron job every 2 minutes.
if (isset($data['advagg_js_compress_cron'])) {
$data['advagg_js_compress_cron']['rule'] = '*/2 * * * *';
}
// Run this cron job every 5 minutes.
if (isset($data['advagg_relocate_cron'])) {
$data['advagg_relocate_cron']['rule'] = '*/5 * * * *';
}
// Run this cron job every day.
if (isset($data['advagg_cron'])) {
$data['advagg_cron']['rule'] = '0 0 * * *';
}
}
/**
* Implements hook_password_policy_force_change_allowed_paths_alter().
*/
function advagg_password_policy_force_change_allowed_paths_alter(&$allowed_paths) {
$advagg_items = advagg_menu();
foreach ($advagg_items as $path => $attributes) {
if (!empty($attributes['page callback']) && $attributes['page callback'] === 'advagg_missing_aggregate') {
$allowed_paths[] = str_replace('/%', '/*', $path);
}
}
}
/**
* Implements hook_s3fs_upload_params_alter().
*
* Set headers for advagg files.
*/
function advagg_s3fs_upload_params_alter(&$upload_params) {
// Get advagg dir.
list($css_path, $js_path) = advagg_get_root_files_dir();
$scheme = file_uri_scheme($css_path[1]);
if ($scheme) {
$css_path_dir = parse_url($css_path[1]);
$css_path_dir = str_replace("$scheme://", '', $css_path[1]);
}
else {
$css_path_dir = ltrim($css_path[1], '/');
}
$scheme = file_uri_scheme($js_path[1]);
if ($scheme) {
$js_path_dir = parse_url($js_path[1]);
$js_path_dir = str_replace("$scheme://", '', $js_path_dir[1]);
}
else {
$js_path_dir = ltrim($js_path[1], '/');
}
// Get file type in advagg dir, css or js.
$type = '';
if (strpos($upload_params['Bucket'] . '/' . $upload_params['Key'], $css_path_dir) !== FALSE) {
$type = 'css';
}
if (strpos($upload_params['Bucket'] . '/' . $upload_params['Key'], $js_path_dir) !== FALSE) {
$type = 'js';
}
if ($js_path_dir === $css_path_dir && !empty($type)) {
$pathinfo = pathinfo($upload_params['Key']);
if ($pathinfo['extension'] === 'gz') {
$pathinfo = pathinfo($pathinfo['filename']);
}
$type = $pathinfo['extension'];
}
if (empty($type)) {
// Only change advagg files.
return;
}
// Cache control is 52 weeeks.
if (variable_get('advagg_resource_hints_use_immutable', ADVAGG_RESOURCE_HINTS_USE_IMMUTABLE)) {
$upload_params['CacheControl'] = 'max-age=31449600, no-transform, public, immutable';
}
else {
$upload_params['CacheControl'] = 'max-age=31449600, no-transform, public';
}
// Expires in 365 days.
$upload_params['Expires'] = gmdate('D, d M Y H:i:s \G\M\T', REQUEST_TIME + 365 * 24 * 60 * 60);
// The extension is .css or .js.
$pathinfo = pathinfo($upload_params['Key']);
if ($pathinfo['extension'] === $type) {
if (variable_get('advagg_gzip', ADVAGG_GZIP)) {
// Set gzip.
$upload_params['ContentEncoding'] = 'gzip';
}
elseif (variable_get('advagg_brotli', ADVAGG_BROTLI)) {
// Set br.
$upload_params['ContentEncoding'] = 'br';
}
}
}
/**
* Implements hook_admin_menu_cache_info().
*
* Add in a cache flush for advagg.
*/
function advagg_admin_menu_cache_info() {
if (variable_get('advagg_enabled', ADVAGG_ENABLED)) {
$caches['advagg'] = array(
'title' => t('Adv CSS/JS Agg'),
'callback' => 'advagg_admin_flush_cache',
);
return $caches;
}
}
/**
* Implements hook_admin_menu_output_alter().
*
* Add in a cache flush for advagg.
*/
function advagg_admin_menu_output_alter(array &$content) {
if (variable_get('advagg_enabled', ADVAGG_ENABLED)) {
// Remove default core aggregation link.
unset($content['icon']['icon']['flush-cache']['assets']);
}
}
/**
* Implements hook_anonymous_login_paths_alter().
*/
function advagg_anonymous_login_paths_alter(&$paths) {
// Exclude advagg css/js paths.
list($css_path, $js_path) = advagg_get_root_files_dir();
$paths['exclude'][] = $css_path[1] . '/*';
$paths['exclude'][] = $js_path[1] . '/*';
}
/**
* Implements hook_pre_flush_all_caches().
*/
function advagg_pre_flush_all_caches() {
static $run_once;
if (!isset($run_once)) {
$run_once = TRUE;
// Only invoked by registry_rebuild.
module_load_include('admin.inc', 'advagg');
// Truncate the advagg_files table.
advagg_admin_truncate_advagg_files();
}
}
/**
* @} End of "defgroup 3rd_party_hooks".
*/
/**
* Only the alter part of locale_js_alter(), not the parsing part.
*
* @param array $javascript
* An array with all JavaScript code. Defaults to the default
* JavaScript array for the given scope.
* @param string $dir
* String pointing to the public locale_js_directory.
*/
function advagg_locale_js_add_translations(array &$javascript, $dir) {
// Add the translation JavaScript file to the page.
if (!empty($GLOBALS['language']->javascript)) {
// Add the translation JavaScript file to the page.
$file = $dir . '/' . $GLOBALS['language']->language . '_' . $GLOBALS['language']->javascript . '.js';
$javascript[$file] = drupal_js_defaults($file);
}
}
/**
* Callback for pre_render so elements can be modified before they are rendered.
*
* @param array $elements
* A render array containing:
* - #items: The JavaScript items as returned by drupal_add_js() and
* altered by drupal_get_js().
* - #group_callback: A function to call to group #items. Following
* this function, #aggregate_callback is called to aggregate items within
* the same group into a single file.
* - #aggregate_callback: A function to call to aggregate the items within
* the groups arranged by the #group_callback function.
*
* @return array
* A render array that will render to a string of JavaScript tags.
*/
function advagg_modify_js_pre_render(array $elements) {
// Get the children elements.
$children = array_intersect_key($elements, array_flip(element_children($elements)));
// Allow other modules to modify $children and $elements before they are
// rendered.
// Call hook_advagg_modify_js_pre_render_alter()
drupal_alter('advagg_modify_js_pre_render', $children, $elements);
// Remove old children elements.
foreach ($children as $key => $value) {
if (isset($elements[$key])) {
unset($elements[$key]);
}
}
// Add in new children elements.
$elements += $children;
return $elements;
}
/**
* Callback for pre_render so elements can be modified before they are rendered.
*
* @param array $elements
* A render array containing:
* - #items: The CSS items as returned by drupal_add_css() and
* altered by drupal_get_css().
* - #group_callback: A function to call to group #items. Following
* this function, #aggregate_callback is called to aggregate items within
* the same group into a single file.
* - #aggregate_callback: A function to call to aggregate the items within
* the groups arranged by the #group_callback function.
*
* @return array
* A render array that will render to a string of JavaScript tags.
*/
function advagg_modify_css_pre_render(array $elements) {
if (!advagg_enabled()) {
return $elements;
}
// Put children elements into a reference array.
$children = array();
foreach ($elements as $key => &$value) {
if ($key !== '' && $key[0] === '#') {
continue;
}
$children[$key] = &$value;
}
unset($value);
// Allow other modules to modify $children and $elements before they are
// rendered.
// Call hook_advagg_modify_css_pre_render_alter()
drupal_alter('advagg_modify_css_pre_render', $children, $elements);
return $elements;
}
/**
* Default callback to aggregate CSS files and inline content.
*
* Having the browser load fewer CSS files results in much faster page loads
* than when it loads many small files. This function aggregates files within
* the same group into a single file unless the site-wide setting to do so is
* disabled (commonly the case during site development). To optimize download,
* it also compresses the aggregate files by removing comments, whitespace, and
* other unnecessary content. Additionally, this functions aggregates inline
* content together, regardless of the site-wide aggregation setting.
*
* @param array $css_groups
* An array of CSS groups as returned by drupal_group_css(). This function
* modifies the group's 'data' property for each group that is aggregated.
*
* @see drupal_aggregate_css()
* @see drupal_group_css()
* @see drupal_pre_render_styles()
* @see system_element_info()
*/
function _advagg_aggregate_css(array &$css_groups) {
if (!advagg_enabled()) {
return drupal_aggregate_css($css_groups);
}
if (variable_get('advagg_debug', ADVAGG_DEBUG)) {
$GLOBALS['_advagg']['debug']['css_groups_before'][] = $css_groups;
}
$preprocess_css = advagg_file_aggregation_enabled('css');
// Allow other modules to modify $css_groups right before it is processed.
// Call hook_advagg_css_groups_alter().
drupal_alter('advagg_css_groups', $css_groups, $preprocess_css);
// For each group that needs aggregation, aggregate its items.
$files_to_aggregate = array();
// Allow for inline CSS to be between aggregated files.
$gap_counter = 0;
foreach ($css_groups as $key => $group) {
switch ($group['type']) {
// If a file group can be aggregated into a single file, do so, and set
// the group's data property to the file path of the aggregate file.
case 'file':
if ($group['preprocess'] && $preprocess_css) {
$files_to_aggregate[$gap_counter][$key] = $group;
}
else {
++$gap_counter;
}
break;
// Aggregate all inline CSS content into the group's data property.
case 'inline':
++$gap_counter;
$css_groups[$key]['data'] = '';
foreach ($group['items'] as $item) {
$css_groups[$key]['data'] .= advagg_load_stylesheet_content($item['data'], $item['preprocess']);
}
break;
// Create a gap for external CSS.
case 'external':
++$gap_counter;
break;
}
}
if (!empty($files_to_aggregate)) {
$hooks_hash = advagg_get_current_hooks_hash();
$serialize_function = variable_get('advagg_serialize', ADVAGG_SERIALIZE);
$css_hash = drupal_hash_base64($serialize_function($files_to_aggregate));
$cache_id = 'advagg:css:' . $hooks_hash . ':' . $css_hash;
if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) >= 1 && $cache = cache_get($cache_id, 'cache_advagg_aggregates')) {
$plans = $cache->data;
}
else {
module_load_include('inc', 'advagg', 'advagg');
$plans = advagg_build_aggregate_plans($files_to_aggregate, 'css');
if (!empty($plans) && variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) >= 1) {
cache_set($cache_id, $plans, 'cache_advagg_aggregates', CACHE_TEMPORARY);
}
}
$css_groups = advagg_merge_plans($css_groups, $plans);
}
if (variable_get('advagg_debug', ADVAGG_DEBUG)) {
$GLOBALS['_advagg']['debug']['css_groups_after'][] = $css_groups;
}
}
/**
* Default callback to aggregate JavaScript files.
*
* Having the browser load fewer JavaScript files results in much faster page
* loads than when it loads many small files. This function aggregates files
* within the same group into a single file unless the site-wide setting to do
* so is disabled (commonly the case during site development). To optimize
* download, it also compresses the aggregate files by removing comments,
* whitespace, and other unnecessary content.
*
* @param array $js_groups
* An array of JavaScript groups as returned by drupal_group_js(). For each
* group that is aggregated, this function sets the value of the group's
* 'data' key to the URI of the aggregate file.
*
* @see drupal_group_js()
* @see drupal_pre_render_scripts()
*/
function _advagg_aggregate_js(array &$js_groups) {
if (!advagg_enabled()) {
if (function_exists('drupal_aggregate_js')) {
return drupal_aggregate_js($js_groups);
}
else {
return;
}
}
if (variable_get('advagg_debug', ADVAGG_DEBUG)) {
$GLOBALS['_advagg']['debug']['js_groups_before'][] = $js_groups;
}
$preprocess_js = advagg_file_aggregation_enabled('js');
// Allow other modules to modify $js_groups right before it is processed.
// Call hook_advagg_js_groups_alter().
drupal_alter('advagg_js_groups', $js_groups, $preprocess_js);
// For each group that needs aggregation, aggregate its items.
$files_to_aggregate = array();
// Only aggregate when the site is configured to do so, and not during an
// update.
$gap_counter = 0;
if ($preprocess_js) {
// Set boolean to TRUE if all JS in footer.
$all_in_footer = FALSE;
if (module_exists('advagg_mod') && variable_get('advagg_mod_js_footer', ADVAGG_MOD_JS_FOOTER) >= 2) {
$all_in_footer = TRUE;
}
foreach ($js_groups as $key => &$group) {
switch ($group['type']) {
// If a file group can be aggregated into a single file, do so, and set
// the group's data property to the file path of the aggregate file.
case 'file':
if (!empty($group['preprocess'])) {
// Special handing for when all JS is in the footer.
if ($all_in_footer && $group['scope'] === 'footer' && $group['group'] > 9000) {
++$gap_counter;
$all_in_footer = FALSE;
}
$files_to_aggregate[$gap_counter][$key] = $group;
}
else {
++$gap_counter;
}
break;
// Create a gap for inline JS.
case 'inline':
++$gap_counter;
break;
// Create a gap for external JS.
case 'external':
++$gap_counter;
break;
}
}
unset($group);
}
if (!empty($files_to_aggregate)) {
$hooks_hash = advagg_get_current_hooks_hash();
$serialize_function = variable_get('advagg_serialize', ADVAGG_SERIALIZE);
$js_hash = drupal_hash_base64($serialize_function($files_to_aggregate));
$cache_id = 'advagg:js:' . $hooks_hash . ':' . $js_hash;
if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) >= 1 && $cache = cache_get($cache_id, 'cache_advagg_aggregates')) {
$plans = $cache->data;
}
else {
module_load_include('inc', 'advagg', 'advagg');
$plans = advagg_build_aggregate_plans($files_to_aggregate, 'js');
if (!empty($plans) && variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) >= 1) {
cache_set($cache_id, $plans, 'cache_advagg_aggregates', CACHE_TEMPORARY);
}
}
$js_groups = advagg_merge_plans($js_groups, $plans);
}
if (variable_get('advagg_debug', ADVAGG_DEBUG)) {
$GLOBALS['_advagg']['debug']['js_groups_after'][] = $js_groups;
}
}
/**
* Builds the arrays needed for css rendering and caching.
*
* @param bool $skip_alter
* (Optional) If set to TRUE, this function skips calling drupal_alter() on
* css, useful for the aggressive cache.
*
* @return array
* Array contains the 2 arrays used for css.
*/
function _advagg_build_css_arrays_for_rendering($skip_alter = FALSE) {
// Get the raw CSS variable.
$raw_css = drupal_add_css();
// Process and Sort css.
$full_css = advagg_get_css($raw_css, $skip_alter);
// Add attached js to drupal_add_js() function.
if (!empty($full_css['#attached'])) {
drupal_process_attached($full_css);
// Remove #attached since it's been added to the javascript array now.
unset($full_css['#attached']);
}
return array($raw_css, $full_css);
}
/**
* Builds the arrays needed for js rendering and caching.
*
* @param bool $skip_alter
* (Optional) If set to TRUE, this function skips calling drupal_alter() on
* js, useful for the aggressive cache.
*
* @return array
* Array contains the 3 arrays used for javascript.
*/
function _advagg_build_js_arrays_for_rendering($skip_alter = FALSE) {
// Get the raw JS variable.
$javascript = drupal_add_js();
// Process and Sort JS.
$full_javascript = advagg_get_full_js($javascript, $skip_alter);
// Get scopes used in the js.
$scopes = advagg_get_js_scopes($full_javascript);
// Add JS to the header and footer of the page.
$js_scope_array = array();
$js_scope_settings_array = array();
foreach ($scopes as $scope => $use) {
if (!$use) {
// If the scope is not being used, skip it.
continue;
}
// advagg_get_js() will sort the JavaScript so that it appears in the
// correct order.
$scripts = advagg_get_js($scope, $full_javascript);
if (isset($scripts['#items']['settings'])) {
// Get the js settings.
$js_scope_settings_array[$scope]['settings'] = $scripts['#items']['settings'];
// Exclude JS Settings from the array; we'll add it back later.
$scripts['#items']['settings'] = array();
}
$js_scope_array[$scope] = $scripts;
}
// Fix settings; if more than 1 is set, use the largest one.
if (count($js_scope_settings_array) > 1) {
$max = -1;
$max_scope = '';
foreach ($js_scope_settings_array as $scope => $settings) {
$count = count($settings);
$max = max($max, $count);
if ($max == $count) {
$max_scope = $scope;
}
}
foreach ($js_scope_settings_array as $scope => $settings) {
if ($scope !== $max_scope) {
unset($js_scope_settings_array[$scope]);
}
}
}
return array($javascript, $js_scope_settings_array, $js_scope_array);
}
/**
* Returns TRUE if the CSS is being loaded via JavaScript.
*
* @param object $css_cache
* Cache object from cache_get().
*
* @return bool
* TRUE if CSS loaded via JS. FALSE if not.
*/
function advagg_css_in_js($css_cache = NULL) {
if (module_exists('advagg_mod')
&& variable_get('advagg_mod_css_defer', ADVAGG_MOD_CSS_DEFER)
) {
return TRUE;
}
if (module_exists('css_delivery')
&& css_delivery_enabled()
) {
return TRUE;
}
// Critical css added by another means.
if (!empty($css_cache->data[1]['#items'])) {
foreach ($css_cache->data[1]['#items'] as $values) {
if (!empty($values['critical-css'])) {
return TRUE;
}
}
}
return variable_get('advagg_css_in_js', ADVAGG_CSS_IN_JS);
}
/**
* Given the full css and js scope array return back the render cache.
*
* @param array $full_css
* Array from advagg_get_css() with #attached removed because it was built by
* _advagg_build_css_arrays_for_rendering().
* @param array $js_scope_array
* Array built from iterations of advagg_get_js() inside of
* _advagg_build_js_arrays_for_rendering().
*
* @return array
* Array containing the $css_cache, $js_cache, $css_cache_id, $js_cache_id.
*/
function advagg_get_render_cache(array $full_css, array $js_scope_array) {
$cids = array();
$css_cache_id = '';
$js_cache_id = '';
// Get advagg hash.
$hooks_hash = advagg_get_current_hooks_hash();
$serialize_function = variable_get('advagg_serialize', ADVAGG_SERIALIZE);
if (advagg_file_aggregation_enabled('css')) {
// Generate css cache id.
$cids[] = $css_cache_id = 'advagg:css:full:1.1:' . $hooks_hash . ':' . drupal_hash_base64($serialize_function($full_css));
}
if (advagg_file_aggregation_enabled('js')) {
// Generate js cache id.
$cids[] = $js_cache_id = 'advagg:js:full:1.1:' . $hooks_hash . ':' . drupal_hash_base64($serialize_function($js_scope_array));
}
if (!empty($cids)) {
// Get the cached data.
$cached_data = cache_get_multiple($cids, 'cache_advagg_aggregates');
// Set variables from the cache.
if (isset($cached_data[$css_cache_id])) {
$css_cache = $cached_data[$css_cache_id];
}
if (isset($cached_data[$js_cache_id])) {
$js_cache = $cached_data[$js_cache_id];
}
}
// Special handling if the css is loaded via JS.
if (!empty($css_cache)
&& empty($js_cache)
&& advagg_css_in_js($css_cache)
) {
// If CSS is being loaded via JavaScript and the css cache is set but the
// js cache is not set; then unset the css cache as well.
unset($css_cache);
}
// Set to empty arrays on a cache miss.
if (!isset($css_cache)) {
$css_cache = new stdClass();
}
if (!isset($js_cache)) {
$js_cache = new stdClass();
}
return array($css_cache, $js_cache, $css_cache_id, $js_cache_id);
}
/**
* Replacement for template_process_html().
*/
function _advagg_process_html(&$variables) {
// Don't fail even if the menu router failed.
if (drupal_get_http_header('status') === '404 Not Found') {
// See if the URI contains advagg.
$uri = request_uri();
if (stripos($uri, '/advagg_') !== FALSE) {
$advagg_items = advagg_menu();
// Check css.
$css = reset($advagg_items);
$css_path = key($advagg_items);
$css_path = substr($css_path, 0, strlen($css_path) - 1);
$css_start = strpos($uri, $css_path);
if ($css_start !== FALSE) {
$filename = substr($uri, $css_start + strlen($css_path));
}
// Check js.
if (empty($filename)) {
$js = next($advagg_items);
$js_path = key($advagg_items);
$js_path = substr($js_path, 0, strlen($js_path) - 1);
$js_start = strpos($uri, $js_path);
if ($js_start !== FALSE) {
$filename = substr($uri, $js_start + strlen($js_path));
}
}
// If we have a filename call the page callback.
if (!empty($filename)) {
$router_item = $css;
if (isset($js)) {
$router_item = $js;
}
// Include the file if needed.
if ($router_item['file']) {
$included = module_load_include($router_item['file'], 'advagg');
if (!$included && !function_exists($router_item['page callback'])) {
$file = DRUPAL_ROOT . '/' . drupal_get_path('module', 'advagg') . '/' . $router_item['file'];
if (is_file($file)) {
require_once $file;
}
}
}
// Call the function.
if (function_exists($router_item['page callback'])) {
// Strip query and fragment form the filename.
if ($pos = strpos($filename, '?')) {
$filename = substr($filename, 0, $pos);
}
if ($pos = strpos($filename, '#')) {
$filename = substr($filename, 0, $pos);
}
// Generate the file.
call_user_func_array($router_item['page callback'], array($filename));
}
else {
// Report the bigger issue to watchdog.
watchdog('advagg', 'You need to flush your menu cache. This can be done at the top of the performance page. The advagg callback failed while trying to generate this file: @uri', array(
'@performance' => url('admin/config/development/performance'),
'@uri' => $uri,
), WATCHDOG_CRITICAL);
}
}
}
}
if (!advagg_enabled()) {
template_process_html($variables);
return;
}
// Render page_top and page_bottom into top level variables.
if (isset($variables['page']) && is_array($variables['page']) && isset($variables['page']['page_top'])) {
$variables['page_top'] = drupal_render($variables['page']['page_top']);
}
elseif (!isset($variables['page_top'])) {
$variables['page_top'] = '';
}
if (isset($variables['page']) && is_array($variables['page']) && isset($variables['page']['page_bottom'])) {
$variables['page_bottom'] = drupal_render($variables['page']['page_bottom']);
}
elseif (!isset($variables['page_bottom'])) {
$variables['page_bottom'] = '';
}
// Place the rendered HTML for the page body into a top level variable.
if (isset($variables['page']) && is_array($variables['page']) && isset($variables['page']['#children'])) {
$variables['page'] = $variables['page']['#children'];
}
$advagg_script_alt_scope_scripts = array();
if (variable_get('advagg_scripts_scope_anywhere', ADVAGG_SCRIPTS_SCOPE_ANYWHERE)) {
$prefix = "";
$suffix = "";
$variables['page'] = $prefix . $variables['page'] . $suffix;
$prefix = "";
$suffix = "";
$variables['page_top'] = $prefix . $variables['page_top'] . $suffix;
$prefix = "";
$suffix = "";
$variables['page_bottom'] = $prefix . $variables['page_bottom'] . $suffix;
$matches = array();
preg_match_all('//', $variables['page_top'], $matches);
$advagg_script_alt_scope_scripts = array_merge($matches[1], $advagg_script_alt_scope_scripts);
preg_match_all('//', $variables['page'], $matches);
$advagg_script_alt_scope_scripts = array_merge($matches[1], $advagg_script_alt_scope_scripts);
preg_match_all('//', $variables['page_bottom'], $matches);
$advagg_script_alt_scope_scripts = array_merge($matches[1], $advagg_script_alt_scope_scripts);
}
// Parts of drupal_get_html_head().
$elements = drupal_add_html_head();
if (is_callable('advagg_mod_html_head_post_alter')) {
advagg_mod_html_head_post_alter($elements);
}
// Get default javascript.
// @see http://drupal.org/node/1279226
if (function_exists('drupal_add_js_page_defaults')) {
drupal_add_js_page_defaults();
}
$javascript = array();
// Try the render cache.
if (!variable_get('advagg_debug', ADVAGG_DEBUG)) {
// No Alter.
if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) >= 5 && !module_exists('advagg_relocate')) {
// Get all CSS and JS variables needed; running no alters.
list($variables['css'], $full_css) = _advagg_build_css_arrays_for_rendering(TRUE);
list($javascript, $js_scope_settings_array, $js_scope_array) = _advagg_build_js_arrays_for_rendering(TRUE);
// Get the render cache.
list($css_cache, $js_cache, $css_cache_id_no_alter, $js_cache_id_no_alter) = advagg_get_render_cache($full_css, $js_scope_array);
}
// With Alter.
if ((empty($css_cache->data) || empty($js_cache->data)) && variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) >= 3) {
// Get all CSS and JS variables needed; running alters.
list($variables['css'], $full_css) = _advagg_build_css_arrays_for_rendering();
list($javascript, $js_scope_settings_array, $js_scope_array) = _advagg_build_js_arrays_for_rendering();
// Get the render cache.
list($css_cache, $js_cache, $css_cache_id, $js_cache_id) = advagg_get_render_cache($full_css, $js_scope_array);
}
}
// CSS has nice hooks so we don't need to work around it.
if (!empty($css_cache->data)) {
// Use render cache.
list($variables['styles'], $full_css) = $css_cache->data;
}
else {
// Get the css if we have not done so.
if (empty($full_css)) {
list($variables['css'], $full_css) = _advagg_build_css_arrays_for_rendering();
}
// Render the CSS; advagg_pre_render_styles() gets called here.
$variables['styles'] = drupal_render($full_css);
if (!empty($css_cache_id) && variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) >= 3) {
// Save to the cache.
cache_set($css_cache_id, array($variables['styles'], $full_css), 'cache_advagg_aggregates', CACHE_TEMPORARY);
}
if (!empty($css_cache_id_no_alter) && variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) >= 5) {
// Save to the cache.
cache_set($css_cache_id_no_alter, array($variables['styles'], $full_css), 'cache_advagg_aggregates', CACHE_TEMPORARY);
}
}
if (module_exists('advagg_font') && variable_get('advagg_font_fontfaceobserver', ADVAGG_FONT_FONTFACEOBSERVER)) {
$fonts = array();
foreach ($full_css['#groups'] as $groups) {
if (isset($groups['items']['files'])) {
foreach ($groups['items']['files'] as $file) {
if (isset($file['advagg_font'])) {
foreach ($file['advagg_font'] as $class => $name) {
$fonts[$class] = $name;
}
}
}
}
}
if (!empty($fonts)) {
if (isset($js_scope_settings_array)) {
$key = key($js_scope_settings_array);
$js_scope_settings_array[$key]['settings']['data'][] = array('advagg_font' => $fonts);
}
drupal_add_js(array('advagg_font' => $fonts), array('type' => 'setting'));
}
}
if (variable_get('advagg_resource_hints_preload', ADVAGG_RESOURCE_HINTS_PRELOAD)) {
foreach ($full_css['#groups'] as $groups) {
if (empty($groups['data']) || $groups['type'] === 'inline') {
continue;
}
advagg_add_preload_header(advagg_convert_abs_to_rel(file_create_url($groups['data'])), 'style');
}
}
// JS needs hacks.
// Clear out all old scripts.
if (variable_get('advagg_clear_scripts', ADVAGG_CLEAR_SCRIPTS)) {
$variables['scripts'] = '';
}
if (!isset($variables['scripts'])) {
$variables['scripts'] = '';
}
if (!isset($variables['page_bottom']) || !is_string($variables['page_bottom'])) {
$variables['page_bottom'] = '';
}
$use_cache = FALSE;
if (!empty($js_cache->data) && !variable_get('advagg_debug', ADVAGG_DEBUG)) {
// Use render cache.
$use_cache = TRUE;
$add_to_variables = array();
// Replace cached settings with current ones.
$js_settings_used = array();
$js_scope_settings_array_copy = $js_scope_settings_array;
if (variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) >= 5) {
if (!empty($js_scope_settings_array_copy['header']) && empty($js_scope_settings_array_copy['footer'])) {
// Copy header settings into the footer.
$js_scope_settings_array_copy['footer'] = $js_scope_settings_array_copy['header'];
}
}
list($js_cache_data, $js_scope_array) = $js_cache->data;
foreach ($js_cache_data as $scope => $value) {
$scope_settings = $scope;
if ($scope_settings === 'scripts') {
$scope_settings = 'header';
}
if ($scope === 'page_bottom') {
$scope_settings = 'footer';
}
// Search $value for Drupal.settings.
$start = strpos($value, 'jQuery.extend(Drupal.settings,');
if ($start !== FALSE) {
// If the cache and current settings scope's do not match; do not use
// the cached version.
if (!isset($js_scope_settings_array_copy[$scope_settings]['settings'])) {
$use_cache = FALSE;
break;
}
// Replace cached Drupal.settings with current Drupal.settings for this
// page.
$merged = advagg_cleanup_settings_array(drupal_array_merge_deep_array(array_filter($js_scope_settings_array_copy[$scope_settings]['settings']['data'], 'is_array')));
$json_data = advagg_json_encode($merged);
if (!empty($json_data)) {
// Record that this is being used.
$js_settings_used[$scope_settings] = TRUE;
// Replace the drupal settings string.
$value = advagg_replace_drupal_settings_string($value, $json_data);
}
}
$add_to_variables[$scope] = $value;
}
if ($use_cache) {
$all_used = array_diff(array_keys($js_scope_settings_array_copy), array_keys($js_settings_used));
// Ignore this check if the cache level is less than 5.
if (!empty($all_used) && variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) < 5 && !empty($js_settings_used)) {
// Some js settings did not make it into the output. Skip cache.
$use_cache = FALSE;
}
}
if ($use_cache) {
// Using the cache; write to the $variables array.
foreach ($add_to_variables as $scope => $value) {
// Set the scope variable if not set.
if (!isset($variables[$scope]) || !is_string($variables[$scope])) {
$variables[$scope] = '';
}
// Append the js to the scope.
$variables[$scope] .= $value;
}
}
}
// If the cache isn't used.
if (!$use_cache) {
if (!empty($js_cache->data) && !empty($css_cache->data) && advagg_css_in_js($css_cache)) {
// Render the css so it will be added to the js array;
// advagg_pre_render_styles() gets called here.
$variables['styles'] = drupal_render($full_css);
}
// Check if the js has changed.
$new_js = drupal_add_js();
$diff = array_diff(array_keys($new_js), array_keys($javascript));
if (!empty($diff) || empty($javascript)) {
// Get all JS variables needed again because js changed; or because we
// never got them in the first place.
list($javascript, $js_scope_settings_array, $js_scope_array) = _advagg_build_js_arrays_for_rendering();
}
$js_cache = array();
$js_cache['scripts'] = '';
if (!empty($js_scope_array)) {
// Add JS to the header and footer of the page.
foreach ($js_scope_array as $scope => &$scripts_array) {
// Add js settings.
if (!empty($js_scope_settings_array[$scope]['settings'])) {
$scripts_array['#items']['settings'] = $js_scope_settings_array[$scope]['settings'];
}
// Render js; advagg_pre_render_scripts() gets called here.
$scripts = drupal_render($scripts_array);
if ($scope === 'header') {
// Add to the top of this section.
$variables['scripts'] = $scripts . $variables['scripts'];
$js_cache['scripts'] = $scripts . $js_cache['scripts'];
}
// Footer scripts.
elseif ($scope === 'footer') {
// Add to the bottom of this section.
$variables['page_bottom'] .= $scripts;
$js_cache['page_bottom'] = $scripts;
}
// Above css scripts.
elseif ($scope === 'above_css') {
// Put in this new section.
$variables['above_css'] = $scripts;
$js_cache['above_css'] = $scripts;
}
elseif (variable_get('advagg_scripts_scope_anywhere', ADVAGG_SCRIPTS_SCOPE_ANYWHERE)) {
// Scripts in other places.
if (isset($variables[$scope])
&& is_string($variables[$scope])
&& array_key_exists($scope, $GLOBALS['theme_info']->info['regions'])
) {
// Add to the bottom of this section.
$variables[$scope] .= $scripts;
$js_cache[$scope] = $scripts;
}
elseif (array_search($scope, $advagg_script_alt_scope_scripts, TRUE) !== FALSE) {
// Add to the inline html.
$pos_page_top = strpos($variables['page_top'], "");
$pos_page = strpos($variables['page'], "");
$pos_page_bottom = strpos($variables['page_bottom'], "");
if ($pos_page_top !== FALSE) {
$pos_page_top += strlen("");
$variables['page_top'] = substr_replace($variables['page_top'], "\n$scripts", $pos_page_top, 0);
$js_cache[$scope] = $scripts;
}
elseif ($pos_page !== FALSE) {
$pos_page += strlen("");
$variables['page'] = substr_replace($variables['page'], "\n$scripts", $pos_page, 0);
$js_cache[$scope] = $scripts;
}
elseif ($pos_page_bottom !== FALSE) {
$pos_page_bottom += strlen("");
$variables['page_bottom'] = substr_replace($variables['page_bottom'], "\n$scripts", $pos_page_bottom, 0);
$js_cache[$scope] = $scripts;
}
}
// Add javascript to scripts if we can't find the region in the theme.
elseif (strpos($scope, ':') === FALSE) {
// Add to the bottom of this section.
$variables['scripts'] .= $scripts;
$js_cache['scripts'] .= $scripts;
}
}
}
unset($scripts_array);
// Clear drupal settings so cache is smaller.
foreach ($js_cache as &$string) {
$string = advagg_replace_drupal_settings_string($string, '{}');
}
unset($string);
// Clear drupal settings and not needed items from render cache.
$js_scope_array = array_intersect_key($js_scope_array, array_flip(element_children($js_scope_array)));
foreach ($js_scope_array as $scope => &$scripts_array) {
// Clear element children.
$scripts_array = array_diff_key($scripts_array, array_flip(element_children($scripts_array)));
if (isset($scripts_array['#children'])) {
unset($scripts_array['#children']);
}
// Clear drupal settings.
if (isset($scripts_array['#items']['settings']['data']) && is_array($scripts_array['#items']['settings']['data'])) {
$scripts_array['#items']['settings']['data'] = array();
}
// Clear printed keys.
if (isset($scripts_array['#printed'])) {
unset($scripts_array['#printed']);
}
// Clear not used groups.
foreach ($scripts_array['#groups'] as $key => $groups) {
if (!isset($groups['items']['files'])) {
unset($scripts_array['#groups'][$key]);
}
}
}
unset($scripts_array);
if (!empty($js_cache_id) && !empty($js_cache) && variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) >= 3) {
cache_set($js_cache_id, array($js_cache, $js_scope_array), 'cache_advagg_aggregates', CACHE_TEMPORARY);
}
if (!empty($js_cache_id_no_alter) && !empty($js_cache) && variable_get('advagg_cache_level', ADVAGG_CACHE_LEVEL) >= 5) {
cache_set($js_cache_id_no_alter, array($js_cache, $js_scope_array), 'cache_advagg_aggregates', CACHE_TEMPORARY);
}
}
}
if (!empty($variables['above_css'])) {
$variables['styles'] = $variables['above_css'] . $variables['styles'];
}
if (variable_get('advagg_resource_hints_preload', ADVAGG_RESOURCE_HINTS_PRELOAD)) {
foreach ($js_scope_array as $scope => &$scripts_array) {
if ($scope !== 'header'
&& $scope !== 'footer'
&& $scope !== 'above_css'
&& !variable_get('advagg_scripts_scope_anywhere', ADVAGG_SCRIPTS_SCOPE_ANYWHERE)
) {
continue;
}
foreach ($scripts_array['#groups'] as $groups) {
if (empty($groups['data']) || $groups['type'] === 'inline') {
continue;
}
advagg_add_preload_header(advagg_convert_abs_to_rel(file_create_url($groups['data'])), 'script');
}
}
}
$head_elements_before = drupal_add_html_head();
if (variable_get('advagg_resource_hints_dns_prefetch', ADVAGG_RESOURCE_HINTS_DNS_PREFETCH)
|| variable_get('advagg_resource_hints_preconnect', ADVAGG_RESOURCE_HINTS_PRECONNECT)
|| variable_get('advagg_resource_hints_preload', ADVAGG_RESOURCE_HINTS_PRELOAD)
) {
// Prefetch css domains.
foreach ($full_css['#items'] as $file) {
advagg_add_resource_hints_array($file);
}
foreach ($full_css['#groups'] as $groups) {
if (isset($groups['items']['files'])) {
foreach ($groups['items']['files'] as $file) {
advagg_add_resource_hints_array($file);
}
}
}
// Prefetch js domains.
foreach ($js_scope_array as $scope_js) {
foreach ($scope_js['#items'] as $file) {
advagg_add_resource_hints_array($file);
}
if (isset($scope_js['#groups'])) {
foreach ($scope_js['#groups'] as $groups) {
if (isset($groups['items']['files'])) {
foreach ($groups['items']['files'] as $file) {
advagg_add_resource_hints_array($file);
}
}
}
}
}
}
// Add in preload link headers.
advagg_add_preload_header();
// Add in the headers added by advagg.
$head_elements_after = drupal_add_html_head();
$elements += array_diff_key($head_elements_after, $head_elements_before);
// Parts of drupal_get_html_head().
drupal_alter('html_head', $elements);
$head = drupal_render($elements);
if (variable_get('advagg_html_head_in_css_location', ADVAGG_HTML_HEAD_IN_CSS_LOCATION)) {
$variables['styles'] = $head . $variables['styles'];
$variables['head'] = '';
}
else {
$variables['head'] = $head;
}
// Remove AdvAgg comments.
if (variable_get('advagg_scripts_scope_anywhere', ADVAGG_SCRIPTS_SCOPE_ANYWHERE)
&& !empty($advagg_script_alt_scope_scripts)
&& !variable_get('theme_debug', FALSE)
) {
$variables['page_top'] = preg_replace('//', '', $variables['page_top']);
$variables['page'] = preg_replace('//', '', $variables['page']);
$variables['page_bottom'] = preg_replace('//', '', $variables['page_bottom']);
}
// Output debug info.
if (variable_get('advagg_debug', ADVAGG_DEBUG)) {
$debug = $GLOBALS['_advagg']['debug'];
if (is_callable('httprl_pr')) {
$output = ' ' . httprl_pr($debug);
}
else {
$output = '' . str_replace(array('<', '>'), array('<', '>'), print_r($debug, TRUE)) . ''; } watchdog('advagg_debug', $output, array(), WATCHDOG_DEBUG); } } /** * Replace inline drupal settings script. * * @param string $subject * Inline js. * @param string $replace * JS settings replacement. * * @return string * Returns the subject with the replacement in place if this is a drupal * settings json blob. */ function advagg_replace_drupal_settings_string($subject, $replace) { $start = strpos($subject, 'jQuery.extend(Drupal.settings,'); if ($start === FALSE) { return $subject; } // Find the end of the Drupal.settings. $script_end = stripos($subject, '', $start); $settings_substring = substr($subject, $start, $script_end - $start); $json_end = strripos($settings_substring, '});'); // Check if LABjs has added an additional wrapper around Drupal settings. $script_tag_start = strripos(substr($subject, 0, $start), '