diff -ruaN admin_menu.orig/admin_menu.api.php admin_menu/admin_menu.api.php --- admin_menu.orig/admin_menu.api.php 2014-12-19 23:59:50.000000000 +0200 +++ admin_menu/admin_menu.api.php 2015-07-26 13:30:39.751637608 +0300 @@ -162,3 +162,21 @@ ); return $caches; } + +/** + * Allows other modules to alter the configuration used to integrate with JS. + * + * Only is invoked if the JS module is available and enabled. + * + * @deprecated Please use JS 2.x if possible. + * + * @param array $config + * The configuration array as used in hook_js(). + * + * @see hook_js() + * @link https://www.drupal.org/project/js + */ +function hook_admin_menu_hook_js_config_alter(&$config) { + // Add our own module to the dependencies. + $config['cache']['dependencies']['my_module'] = 'my_module'; +} diff -ruaN admin_menu.orig/admin_menu.install admin_menu/admin_menu.install --- admin_menu.orig/admin_menu.install 2014-12-19 23:59:50.000000000 +0200 +++ admin_menu/admin_menu.install 2015-07-26 13:30:39.755637589 +0300 @@ -45,6 +45,31 @@ variable_del('admin_menu_devel_modules'); variable_del('admin_menu_devel_modules_enabled'); variable_del('admin_menu_devel_modules_skip'); + variable_del('admin_menu_js_dependencies'); +} + +/** + * Implements hook_requirements(). + */ +function admin_menu_requirements($phase) { + $requirements = array(); + $t = get_t(); + switch ($phase) { + case 'runtime': + // Warn about 1.x version of JS. + if (module_exists('js')) { + $js_info = drupal_parse_info_file(drupal_get_path('module', 'js') . '/js.info'); + if (!version_compare($js_info['version'], '7.x-2', '>=')) { + $requirements['admin_menu_js'] = array( + 'title' => $t(' Administration menu'), + 'description' => $t('Top avoid runtime issues you should upgrade to version 2.x of the High-performance JavaScript callback handler.'), + 'severity' => REQUIREMENT_INFO, + 'value' => $t('Please update to version 2.x of High-performance JavaScript callback handler'), + ); + } + } + } + return $requirements; } /** diff -ruaN admin_menu.orig/admin_menu.js admin_menu/admin_menu.js --- admin_menu.orig/admin_menu.js 2014-12-19 23:59:50.000000000 +0200 +++ admin_menu/admin_menu.js 2015-07-26 13:30:39.756637584 +0300 @@ -123,10 +123,15 @@ } $.ajax({ cache: true, - type: 'GET', + type: 'POST', dataType: 'text', // Prevent auto-evaluation of response. global: false, // Do not trigger global AJAX events. url: Drupal.settings.admin_menu.basePath.replace(/admin_menu/, 'js/admin_menu/cache/' + hash), + data: { + js_module: 'admin_menu', + js_callback: 'cache', + js_token: Drupal.settings.js && Drupal.settings.js.tokens && Drupal.settings.js.tokens['admin_menu-cache'] || '' + }, success: onSuccess, complete: function (XMLHttpRequest, status) { Drupal.admin.hashes.hash = status; diff -ruaN admin_menu.orig/admin_menu.module admin_menu/admin_menu.module --- admin_menu.orig/admin_menu.module 2014-12-19 23:59:50.000000000 +0200 +++ admin_menu/admin_menu.module 2015-07-26 13:32:11.546193261 +0300 @@ -64,7 +64,7 @@ // AJAX callback. // @see http://drupal.org/project/js $items['js/admin_menu/cache'] = array( - 'page callback' => 'admin_menu_js_cache', + 'page callback' => 'admin_menu_js_callback_cache', 'delivery callback' => 'admin_menu_deliver', 'access arguments' => array('access administration menu'), 'type' => MENU_CALLBACK, @@ -161,7 +161,7 @@ '#attached' => array(), ); $attached = &$page['page_bottom']['admin_menu']['#attached']; - $options = array('every_page' => TRUE); + $options = array('every_page' => TRUE, 'defer' => TRUE); $attached['css'][$path . '/admin_menu.css'] = $options; if ($user->uid == 1) { @@ -186,6 +186,11 @@ // user, only output the hash for the client-side HTTP cache callback URL. $cid = 'admin_menu:' . $user->uid . ':' . session_id() . ':' . $language->language; if (!$complete && !empty($_COOKIE['has_js']) && ($hash = admin_menu_cache_get($cid))) { + // If the JS module is installed, invoke the function to generate a token + // so it is added via settings. + if (module_exists('js')) { + js_get_token('admin_menu', 'cache'); + } $settings['hash'] = $hash; // The base path to use for cache requests depends on whether clean URLs // are enabled, whether Drupal runs in a sub-directory, and on the language @@ -264,18 +269,121 @@ /** * Implements hook_js(). + * + * For JS version 1.x + * + * @deprecated Please use JS 2.x if possible. + * + * @see admin_menu_hook_js_config() + * @see admin_menu_js_info() */ function admin_menu_js() { + $config = array(); + // If we've the dependencies in the cache we might can skip a boot-level here. + // Not significantly faster unless used with in-memory caching e.g. redis. + if (($cache = cache_get('admin_menu_hook_js_config')) && !empty($cache->data)) { + $config = $cache->data; + } + // If not available from cache it has to be in the variables. + if (empty($config)) { + // Make sure we've variable support. + drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES); + $config = variable_get('admin_menu_hook_js_config', array()); + // Store in cache. + cache_set('admin_menu_hook_js_config', $config); + } + return $config; +} + +/** + * Implements hook_js_info(). + * + * For JS version 2.x + * + * @see admin_menu_js() + */ +function admin_menu_js_info() { + // Build dependency list. + $module_data = system_rebuild_module_data(); + $dependencies = array('devel' => 'devel', 'filter' => 'filter', 'user' => 'user'); + // Ensure the "on behalf of" hooks are available too. + module_load_include('inc', 'admin_menu', 'admin_menu.map'); + foreach (module_implements('admin_menu_map') as $module) { + if (!isset($dependencies[$module])) { + $dependencies[$module] = $module; + if (isset($module_data[$module]->requires)) { + $dependencies += array_combine(array_keys($module_data[$module]->requires), array_keys($module_data[$module]->requires)); + } + } + } + return array( 'cache' => array( - 'callback' => 'admin_menu_js_cache', + 'delivery callback' => 'admin_menu_deliver', + 'dependencies' => $dependencies, 'includes' => array('common', 'theme', 'unicode'), - 'dependencies' => array('devel', 'filter', 'user'), + 'methods' => array('POST'), ), ); } /** + * Builds the config for the hook_js() implementation. + * + * Allows other modules to alter the configuration by using + * hook_admin_menu__hook_js_config_alter(&$config) + * + * @deprecated Please use JS 2.x if possible. + */ +function admin_menu_hook_js_config() { + if (module_exists('js')) { + // Build dependency list. + $module_data = system_rebuild_module_data(); + $dependencies = array(); + // Ensure the "on behalf of" hooks are available too. + module_load_include('inc', 'admin_menu', 'admin_menu.map'); + foreach (module_implements('admin_menu_map') as $module) { + if (!isset($dependencies[$module])) { + $dependencies[$module] = $module; + if (isset($module_data[$module]->requires)) { + $dependencies += array_combine(array_keys($module_data[$module]->requires), array_keys($module_data[$module]->requires)); + } + } + } + // Basic configuration. + $config = array( + 'cache' => array( + 'callback' => 'admin_menu_js_callback_cache', + 'includes' => array('path', 'theme', 'language', 'menu'), + 'dependencies' => $dependencies, + 'access arguments' => array('access administration menu'), + ), + ); + drupal_alter('admin_menu_hook_js_config', $config); + variable_set('admin_menu_hook_js_config', $config); + // Ensure the config is available in the cache. Not significantly faster + // unless used with in-memory caching e.g. redis. + cache_set('admin_menu_hook_js_config', $config); + } +} + +/** + * Implements hook_modules_enabled(). + */ +function admin_menu_modules_enabled($modules) { + // Rebuild the JS menu configuration. + admin_menu_hook_js_config(); +} + +/** + * Implements hook_modules_disabled(). + */ +function admin_menu_modules_disabled($modules) { + // Rebuild the JS menu configuration. + admin_menu_hook_js_config(); +} + +/** * Retrieve a client-side cache hash from cache. * * The hash cache is consulted more than once per request; we therefore cache @@ -323,7 +431,7 @@ * * @see admin_menu_deliver() */ -function admin_menu_js_cache() { +function admin_menu_js_callback_cache() { global $conf; // Suppress Devel module. @@ -359,14 +467,15 @@ drupal_add_http_header('Expires', gmdate(DATE_RFC1123, REQUEST_TIME + $max_age)); drupal_add_http_header('Cache-Control', 'private, max-age=' . $max_age); - // Retrieve and return the rendered menu. - return admin_menu_output(); + // Retrieve and output the rendered menu. + print admin_menu_output(); + exit; } /** * Delivery callback for client-side HTTP caching. * - * @see admin_menu_js_cache() + * @see admin_menu_js_callback_cache() */ function admin_menu_deliver($page_callback_result) { drupal_add_http_header('Content-Type', 'text/html; charset=utf-8'); @@ -375,9 +484,10 @@ global $language; drupal_add_http_header('Content-Language', $language->language); - // The page callback is always admin_menu_js_cache(), which always returns a - // string, and is only accessed when the user actually has access to it. - // Therefore, we do not care for the other possible page callback results. + // The page callback is always admin_menu_js_callback_cache(), which always + // returns a string, and is only accessed when the user actually has access + // to it. Therefore, we do not care for the other possible page callback + // results. print $page_callback_result; // Perform end-of-request tasks. The page cache is created here. @@ -473,6 +583,10 @@ // Rebuild the output. if (!isset($content)) { + // Note: 'admin_menu' uses theme() function to render own links through 'theme_admin_menu_links()' + // and icon through 'theme_admin_menu_icon()'. It requires to use 'DRUPAL_BOOTSTRAP_FULL'. + drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); + // Retrieve enabled components to display and make them available for others. $components = variable_get('admin_menu_components', array()); $components += array( @@ -799,6 +913,9 @@ if (db_table_exists('cache_admin_menu')) { cache_clear_all(isset($uid) ? $cid : '*', 'cache_admin_menu', TRUE); } + + // Rebuild the JS menu configuration. + admin_menu_hook_js_config(); } /**