Index: admin_menu.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/admin_menu/admin_menu.inc,v retrieving revision 1.47 diff -u -p -r1.47 admin_menu.inc --- admin_menu.inc 9 Jun 2009 18:21:03 -0000 1.47 +++ admin_menu.inc 11 Jun 2009 19:13:23 -0000 @@ -109,24 +109,6 @@ function admin_menu_admin_menu(&$deleted $links = array(); $icon_path = ''; - // Add link to manually run cron. - $links[] = array( - 'title' => 'Run cron', - 'path' => 'admin/reports/status/run-cron', - 'weight' => 50, - 'parent_path' => $icon_path, - ); - // Add link to run update.php. - $links[] = array( - 'title' => 'Run updates', - 'path' => 'update.php', - 'weight' => 50, - 'parent_path' => $icon_path, - 'options' => array( - 'external' => TRUE, - ), - ); - // Move 'By module' item into Site configuration. if (isset($deleted['admin/by-module'])) { $deleted['admin/by-module']['parent_path'] = 'admin/settings'; @@ -135,38 +117,6 @@ function admin_menu_admin_menu(&$deleted unset($deleted['admin/by-module']); } - // Add link to drupal.org. - $links[] = array( - 'title' => 'Drupal.org', - 'path' => 'http://drupal.org', - 'weight' => 100, - 'parent_path' => $icon_path, - ); - // Add links to project issue queues. - $links[] = array( - 'title' => 'Drupal issue queue', - 'path' => 'http://drupal.org/project/issues/drupal', - 'weight' => -10, - 'parent_path' => 'http://drupal.org', - ); - $projects = array(); - foreach (module_list(FALSE, TRUE) as $module) { - $info = drupal_parse_info_file(drupal_get_path('module', $module) . '/' . $module . '.info'); - if (!isset($info['project']) || (isset($info['project']) && ($info['project'] == 'drupal' || isset($projects[$info['project']])))) { - continue; - } - $projects[$info['project']] = 1; - $url = 'http://drupal.org/project/issues/' . $info['project']; - // Filtering project versions via query string is not yet supported. - // @see http://drupal.org/node/97569 - // $url .= !empty($info['version']) ? '/'. $info['version'] : ''; - $links[] = array( - 'title' => check_plain($info['name']) . ' issue queue', - 'path' => $url, - 'parent_path' => 'http://drupal.org', - ); - } - // Add 'Create ' items to Content management menu. if (isset($deleted['node/add'])) { $deleted['node/add']['parent_path'] = 'admin/content'; @@ -211,167 +161,150 @@ function admin_menu_admin_menu(&$deleted } } + return $links; +} + +/** + * Add some hard-coded features for better user experience. + * + * @param $menu_links + * An array containing the complete administration menu structure, passed by + * reference. + * + * @return + * An array of links that were removed from $menu_links. + */ +function admin_menu_adjust_items() { + // Add icon containing special links. + $links['icon'] = array( + '#title' => theme('admin_menu_icon'), + '#attributes' => array('class' => array('admin-menu-icon')), + '#href' => '', + '#options' => array( + 'html' => TRUE, + ), + ); + // Add link to manually run cron. + $links['icon']['cron'] = array( + '#title' => t('Run cron'), + '#weight' => 50, + '#href' => 'admin/reports/status/run-cron', + ); + // Add link to run update.php. + $links['icon']['update'] = array( + '#title' => t('Run updates'), + '#weight' => 50, + '#href' => 'update.php', + '#options' => array( + 'external' => TRUE, + ), + ); + // Add link to drupal.org. + $links['icon']['drupal.org'] = array( + '#title' => 'Drupal.org', + '#weight' => 100, + '#href' => 'http://drupal.org', + ); + // Add links to project issue queues. + $links['icon']['drupal.org']['drupal'] = array( + '#title' => t('@project issue queue', array('@project' => 'Drupal')), + '#weight' => -10, + '#href' => 'http://drupal.org/project/issues/drupal', + ); + $projects = array(); + foreach (module_list(FALSE, TRUE) as $module) { + $info = drupal_parse_info_file(drupal_get_path('module', $module) . '/' . $module . '.info'); + if (!isset($info['project']) || (isset($info['project']) && ($info['project'] == 'drupal' || isset($projects[$info['project']])))) { + continue; + } + $projects[$info['project']] = 1; + // @todo Add version filter. + $links['icon']['drupal.org'][$info['project']] = array( + '#title' => t('@project issue queue', array('@project' => 'Drupal')), + '#href' => 'http://drupal.org/project/issues/' . $info['project'], + ); + } // Add items to flush caches. - $links[] = array( - 'title' => 'Flush all caches', - 'path' => 'admin_menu/flush-cache', - 'options' => array( + $links['icon']['flush-cache'] = array( + '#title' => t('Flush all caches'), + '#weight' => 20, + '#href' => 'admin_menu/flush-cache', + '#options' => array( 'query' => drupal_get_destination(), ), - 'weight' => 20, - 'parent_path' => $icon_path, ); $caches = array( - 'admin_menu' => 'Administration menu', - 'cache' => 'Cache tables', - 'menu' => 'Menu', - 'requisites' => 'Page requisites', - 'registry' => 'Registry', - 'theme' => 'Theme registry', - ); - foreach ($caches as $name => $title) { - $links[] = array( - 'title' => $title, - 'path' => 'admin_menu/flush-cache/' . $name, - 'options' => array( + 'admin_menu' => t('Administration menu'), + 'cache' => t('Cache tables'), + 'menu' => t('Menu'), + 'requisites' => t('Page requisites'), + 'registry' => t('Registry'), + 'theme' => t('Theme registry'), + ); + foreach ($caches as $arg => $title) { + $links['icon']['flush-cache'][$arg] = array( + '#title' => $title, + '#href' => 'admin_menu/flush-cache/' . $arg, + '#options' => array( 'query' => drupal_get_destination(), ), - 'parent_path' => 'admin_menu/flush-cache', - ); - } - - // Add devel module links - if (module_exists('devel')) { - // Add variable editor. - $links[] = array( - 'title' => 'Variable editor', - 'path' => 'devel/variable', - 'weight' => 20, - 'parent_path' => $icon_path, ); - // Add switch_user items. - if ($devel_user_links = module_invoke('devel', 'switch_user_list')) { - foreach ($devel_user_links as $link) { - if (is_array($link)) { - $links[] = array( - 'title' => $link['title'], - 'description' => $link['attributes']['title'], - 'path' => $link['href'], - 'options' => array( - 'query' => $link['query'], - 'html' => !empty($link['html']), - ), - 'parent_path' => 'user/%', - ); - } - // @todo Remove when Devel 6.x-1.16 has been released. - elseif (preg_match('!href="' . base_path() . '([^\?]+)\?([^"]+)" title="([^"]+)">(()?[^<]+()?)!', $link, $match)) { - $links[] = array( - 'title' => $match[4], - 'description' => $match[3], - 'path' => urldecode($match[1]), - 'options' => array( - 'query' => drupal_get_destination(), - 'html' => TRUE, - ), - 'weight' => 20, - 'parent_path' => 'user/%', - ); - } - } - } } - // Add developer modules toggle link. + // Add link to toggle developer modules (performance). $saved_state = variable_get('admin_menu_devel_modules_enabled', NULL); - $links[] = array( - 'title' => isset($saved_state) ? t('Enable developer modules') : t('Disable developer modules'), - 'path' => 'admin_menu/toggle-modules', - 'weight' => 88, - 'parent_path' => $icon_path, - 'options' => array( + $links['icon']['toggle-modules'] = array( + '#title' => isset($saved_state) ? t('Enable developer modules') : t('Disable developer modules'), + '#weight' => 88, + '#href' => 'admin_menu/toggle-modules', + '#options' => array( 'query' => drupal_get_destination(), ), ); - return $links; -} + // Add link to show current authenticated/anonymous users. + $links['user-counter'] = array( + '#title' => admin_menu_get_user_count(), + '#description' => t('Current anonymous / authenticated users'), + '#weight' => -90, + '#attributes' => array('class' => array('admin-menu-action', 'admin-menu-users')), + '#href' => 'user', + ); + $links['account'] = array( + '#title' => $GLOBALS['user']->name, + '#weight' => -99, + '#attributes' => array('class' => array('admin-menu-action')), + '#href' => 'user/' . $GLOBALS['user']->uid, + ); + $links['logout'] = array( + '#title' => 'Log out', + '#weight' => -100, + '#attributes' => array('class' => array('admin-menu-action')), + '#href' => 'user/logout', + ); -/** - * Add some hard-coded features for better user experience. - * - * @param $menu_links - * An array containing the complete administration menu structure, passed by - * reference. - * @param $sort - * An array containing the # parts of each link - must be updated if a link - * is added. - * - * @return - * An array of links that were removed from $menu_links. - */ -function admin_menu_adjust_items(&$menu_links, &$sort) { - global $user; - $links = array(); - $deleted = array(); - - // Change or remove items, or add new top-level items. - $deleted['admin/by-module'] = $menu_links['admin/by-module']; - unset($menu_links['admin/by-module'], $sort['admin/by-module']); - $deleted['admin/by-task'] = $menu_links['admin/by-task']; - unset($menu_links['admin/by-task'], $sort['admin/by-task']); - - // Remove certain links to re-position them in admin_menu_admin_menu(). - foreach ($menu_links as $path => $link) { - // Remove links below - // - admin/build/node-type/* - // - node/add* - if (strpos($path, 'admin/build/node-type/') !== FALSE || strpos($path, 'node/add') !== FALSE) { - $deleted[$path] = $link; - unset($menu_links[$path], $sort[$path]); + // Add Devel module links. + if (module_exists('devel')) { + // Add variable editor. + $links['icon']['devel-variables'] = array( + '#title' => t('Variable editor'), + '#weight' => 20, + '#href' => 'devel/variable', + ); + // Add switch user links. + foreach (module_invoke('devel', 'switch_user_list') as $link) { + $links['account'][$link['title']] = array( + '#title' => $link['title'], + '#description' => $link['attributes']['title'], + '#href' => $link['href'], + '#options' => array( + 'query' => $link['query'], + 'html' => !empty($link['html']), + ), + ); } } - // Add the icon containing special links. - $links[] = array( - 'title' => theme('admin_menu_icon'), - 'path' => '', - 'weight' => -100, - 'parent_path' => '', - 'options' => array('extra class' => 'admin-menu-icon', 'html' => TRUE), - ); - // Add link to show current authenticated/anonymous users - we will add the - // data dynamically in the _alter hook. - $links[] = array( - 'title' => admin_menu_get_user_count(), - 'description' => t('Current anonymous / authenticated users'), - 'path' => 'user', - 'weight' => -90, - 'parent_path' => '', - 'options' => array('extra class' => 'admin-menu-action admin-menu-users'), - ); - $links[] = array( - 'title' => '@username', - 'path' => 'user/%', - 'weight' => -99, - 'parent_path' => '', - // Note: @username is dynamically replaced by default, we just invoke - // replacement by setting the 't' key here. - 'options' => array('extra class' => 'admin-menu-action', 't' => array()), - ); - $links[] = array( - 'title' => 'Log out', - 'path' => 'user/logout', - 'weight' => -100, - 'parent_path' => '', - 'options' => array('extra class' => 'admin-menu-action'), - ); - foreach ($links as $item) { - $path = $item['path']; - $item = admin_menu_link_build($item); - $menu_links[$path] = $item; - $sort[$path] = 1; - } - return $deleted; + return $links; } /** @@ -418,10 +351,6 @@ function admin_menu_theme_settings() { * Wipe the menu so it can be rebuilt from scratch. */ function admin_menu_flush_caches() { - // Flush cached menu tree. - db_delete('menu_links')->condition('menu_name', 'admin_menu')->execute(); - menu_cache_clear('admin_menu'); - variable_set('admin_menu_rebuild_links', TRUE); // Flush cached output of admin_menu. cache_clear_all('admin_menu:', 'cache_menu', TRUE); // Flush client-side cache hashes. @@ -533,7 +462,13 @@ function admin_menu_toggle_modules() { function admin_menu_flush_cache($name = NULL) { switch ($name) { case 'admin_menu': - admin_menu_flush_caches(); + // Flush cached menu tree. + db_delete('menu_links') + ->condition('menu_name', 'admin_menu') + ->condition('customized', 0) + ->execute(); + menu_cache_clear('admin_menu'); + menu_rebuild(); break; case 'cache': @@ -547,7 +482,7 @@ function admin_menu_flush_cache($name = break; case 'menu': - module_invoke('menu', 'rebuild'); + menu_rebuild(); break; case 'registry': Index: admin_menu.install =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/admin_menu/admin_menu.install,v retrieving revision 1.15 diff -u -p -r1.15 admin_menu.install --- admin_menu.install 18 Apr 2009 17:24:56 -0000 1.15 +++ admin_menu.install 11 Jun 2009 00:56:12 -0000 @@ -16,6 +16,7 @@ function admin_menu_schema() { function admin_menu_install() { // Create cache table. drupal_install_schema('admin_menu'); + db_query("INSERT INTO {menu_custom} (menu_name, title, description) VALUES ('admin_menu', 'Administration menu', '')"); } /** @@ -83,3 +84,16 @@ function admin_menu_update_7300() { db_create_table($ret, 'cache_admin_menu', $schema); return $ret; } + +/** + * Add "admin_menu" menu. + */ +function admin_menu_update_7301() { + $ret = array(); + if (db_query("SELECT menu_name FROM {menu_custom} WHERE menu_name = 'admin_menu'")->fetchField()) { + return $ret; + } + $ret[] = update_sql("INSERT INTO {menu_custom} (menu_name, title, description) VALUES ('admin_menu', 'Administration menu', '')"); + return $ret; +} + Index: admin_menu.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/admin_menu/admin_menu.module,v retrieving revision 1.73 diff -u -p -r1.73 admin_menu.module --- admin_menu.module 11 Jun 2009 01:39:20 -0000 1.73 +++ admin_menu.module 11 Jun 2009 19:21:54 -0000 @@ -48,6 +48,9 @@ function admin_menu_perm() { */ function admin_menu_theme() { return array( + 'admin_menu_links' => array( + 'arguments' => array('elements' => array()), + ), 'admin_menu_icon' => array( 'arguments' => array(), ), @@ -59,6 +62,7 @@ function admin_menu_theme() { */ function admin_menu_menu() { // AJAX callback. + // @see http://drupal.org/project/js $items['js/admin_menu/cache'] = array( 'page callback' => 'admin_menu_js_cache', 'access arguments' => array('access administration menu'), @@ -87,6 +91,44 @@ function admin_menu_menu() { } /** + * Implementation of hook_menu_alter(). + */ +function admin_menu_menu_alter(&$items) { + // Move all items below admin/* into administration menu. + foreach ($items as $path => $item) { + if (strpos($path, 'admin') === 0) { + if (!isset($item['type'])) { + $items[$path]['menu_name'] = 'admin_menu'; + } + else { + if ($item['type'] & MENU_IS_LOCAL_TASK) { + // Work around a core bug, which defines local tasks below menu + // callbacks (e.g. for admin/build/node-type/article/edit). + $parent = explode('/', $path); + array_pop($parent); + $parent = implode('/', $parent); + if (isset($items[$parent]['type']) && $items[$parent]['type'] & MENU_CALLBACK) { + continue; + } + // Trick this item into the visible menu tree. + $items[$path]['_visible'] = TRUE; + } + if ($item['type'] & MENU_CALLBACK) { + continue; + } + $items[$path]['menu_name'] = 'admin_menu'; + } + } + } + + // Remove 'admin', so children appear on the top-level. + $items['admin']['_visible'] = FALSE; + // Remove local tasks on 'admin'. + $items['admin/by-task']['_visible'] = FALSE; + $items['admin/by-module']['_visible'] = FALSE; +} + +/** * Implementation of hook_init(). * * We can't move this into admin_menu_footer(), because PHP-only based themes @@ -174,67 +216,9 @@ function admin_menu_suppress($set = TRUE /** * Implementation of hook_footer(). - * - * @todo Since admin_menu is rebuilt in the same request, we should be able - * to use a helper function instead of a variable to remind us to rebuild - * (variable_set() is slow). */ function admin_menu_footer($main = 0) { - if (!user_access('access administration menu') || admin_menu_suppress(FALSE)) { - return; - } - global $user, $language; - - $cache_server_enabled = variable_get('admin_menu_cache_server', TRUE); - - // Determine whether we need to rebuild. - $rebuild = variable_get('admin_menu_rebuild_links', FALSE); - $cid = 'admin_menu:' . $user->uid . ':' . $language->language; - - // Do nothing at all here if the client supports client-side caching, no - // rebuild is needed, the user has a hash, and is NOT requesting the cache - // update path. Consult the hash cache last, since it requires a DB request. - // @todo Implement a sanity-check to prevent permanent double requests; i.e. - // what if the client-side cache fails for any reason and performs a second - // request on every page? - if (!empty($_COOKIE['has_js']) && !$rebuild && strpos($_GET['q'], 'js/admin_menu/cache') !== 0) { - if (admin_menu_cache_get($cid)) { - return; - } - } - - // Check for the flag indicating that we need to rebuild the menu. - if ($rebuild) { - module_load_include('inc', 'admin_menu'); - _admin_menu_rebuild_links(); - variable_del('admin_menu_rebuild_links'); - } - // Try to load and output administration menu from server-side cache. - elseif ($cache_server_enabled) { - $cache = cache_get($cid, 'cache_menu'); - if ($cache && isset($cache->data)) { - $content = $cache->data; - } - } - - // Rebuild the output. - if (!isset($content)) { - $content = '
'; - $content .= admin_menu_tree_output(menu_tree_all_data('admin_menu')); - $content .= '
'; - - // Cache the menu for this user. - if ($cache_server_enabled) { - cache_set($cid, $content, 'cache_menu'); - } - } - - // Store the new hash for this user. - if (!empty($_COOKIE['has_js'])) { - admin_menu_cache_set($cid, md5($content)); - } - - return $content; + return admin_menu_output(); } /** @@ -284,8 +268,8 @@ function admin_menu_cache_set($cid, $dat * Menu callback; Output administration menu for HTTP caching via AJAX request. */ function admin_menu_js_cache($hash = NULL) { - // Fetch the menu. - $content = admin_menu_footer(); + // Get the rendered menu. + $content = admin_menu_output(); // @todo According to http://www.mnot.net/blog/2006/05/11/browser_caching, // IE will only cache the content when it is compressed. @@ -341,6 +325,125 @@ function admin_menu_get_user_count() { } /** + * Build the administration menu output. + * + * @todo Since admin_menu is rebuilt in the same request, we should be able + * to use a helper function instead of a variable to remind us to rebuild + * (variable_set() is slow). + */ +function admin_menu_output() { + if (!user_access('access administration menu') || admin_menu_suppress(FALSE)) { + return; + } + global $user, $language; + + $cache_server_enabled = variable_get('admin_menu_cache_server', TRUE); + + // Determine whether we need to rebuild. + $rebuild = variable_get('admin_menu_rebuild_links', FALSE); + $cid = 'admin_menu:' . $user->uid . ':' . $language->language; + + // Do nothing at all here if the client supports client-side caching, no + // rebuild is needed, the user has a hash, and is NOT requesting the cache + // update path. Consult the hash cache last, since it requires a DB request. + // @todo Implement a sanity-check to prevent permanent double requests; i.e. + // what if the client-side cache fails for any reason and performs a second + // request on every page? + if (!empty($_COOKIE['has_js']) && !$rebuild && strpos($_GET['q'], 'js/admin_menu/cache') !== 0) { + if (admin_menu_cache_get($cid)) { + return; + } + } + + // Check for the flag indicating that we need to rebuild the menu. + if (0 && $rebuild) { + module_load_include('inc', 'admin_menu'); + _admin_menu_rebuild_links(); + variable_del('admin_menu_rebuild_links'); + } + // Try to load and output administration menu from server-side cache. + elseif ($cache_server_enabled) { + $cache = cache_get($cid, 'cache_menu'); + if ($cache && isset($cache->data)) { + $content = $cache->data; + } + } + + // Rebuild the output. + if (!isset($content)) { + // @todo Always output container to harden JS-less support. + $content['#prefix'] = '
'; + $content['#suffix'] = '
'; + + module_load_include('inc', 'admin_menu'); + $content['links'] = array( + '#theme' => 'admin_menu_links', + ); + $content['links'] += admin_menu_adjust_items(); + + // @todo How can we use #theme here? + $content['menu'] = array( + '#markup' => admin_menu_tree_output(menu_tree_all_data('admin_menu')), + ); + + drupal_alter('admin_menu_output', $content); + $content = drupal_render($content); + + // Cache the menu for this user. + if ($cache_server_enabled) { + cache_set($cid, $content, 'cache_menu'); + } + } + + // Store the new hash for this user. + if (!empty($_COOKIE['has_js'])) { + admin_menu_cache_set($cid, md5($content)); + } + + return $content; +} + +/** + * Render a themed list of links. + * + * @param $elements + * A structured drupal_render()-array of links using the following keys: + * - '#attributes': Optional array of attributes for the list item, processed + * via drupal_attributes(). + * Note that we use an array for 'class'. + * - '#title': Title of the link, passed to l(). + * - '#href': Path of the link, passed to l(). + * - '#description': Optional alternative text for the link, passed to l(). + * - '#options': Optional alternative text for the link, passed to l(). + * + * The array key of each child element itself is passed as path for l(). + */ +function theme_admin_menu_links(&$elements) { + $output = ''; + foreach (element_children($elements, TRUE) as $path) { + $elements[$path] += array( + '#attributes' => array(), + '#options' => array(), + ); + // Render children to determine whether this link is expandable. + $children = theme('admin_menu_links', $elements[$path]); + if (!empty($children)) { + $elements[$path]['#attributes']['class'][] = 'expandable'; + } + if (isset($elements[$path]['#attributes']['class'])) { + $elements[$path]['#attributes']['class'] = implode(' ', $elements[$path]['#attributes']['class']); + } + + $output .= ''; + $output .= l($elements[$path]['#title'], $elements[$path]['#href'], $elements[$path]['#options']); + $output .= $children; + $output .= ''; + } + // @todo #attributes probably required for UL, but already used for LI. + return $output ? '
    ' . $output . '
' : ''; +} + +/** * Return a rendered menu tree. * * @param $tree @@ -354,6 +457,11 @@ function admin_menu_tree_output($tree) { $output = ''; foreach ($tree as $data) { + // Skip dynamic items. + if (isset($data['link']['type']) && $data['link']['type'] == MENU_CALLBACK) { + continue; + } + // @todo Obsolete. $extra_class = isset($data['link']['localized_options']['extra class']) ? $data['link']['localized_options']['extra class'] : NULL; $link = admin_menu_item_link($data['link']); @@ -434,13 +542,6 @@ function theme_admin_menu_item($link, $h } /** - * Implementation of hook_menu_alter(). - */ -function admin_menu_menu_alter() { - variable_set('admin_menu_rebuild_links', TRUE); -} - -/** * Implementation of hook_translated_menu_link_alter(). * * Here is where we make changes to links that need dynamic information such