Index: includes/menu.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/menu.inc,v
retrieving revision 1.255.2.36
diff -u -9 -p -r1.255.2.36 menu.inc
--- includes/menu.inc	3 Nov 2010 12:00:28 -0000	1.255.2.36
+++ includes/menu.inc	3 Nov 2010 21:11:59 -0000
@@ -851,33 +851,48 @@ function menu_tree_all_data($menu_name =
 }
 
 /**
  * Get the data structure representing a named menu tree, based on the current page.
  *
  * The tree order is maintained by storing each parent in an individual
  * field, see http://drupal.org/node/141866 for more.
  *
  * @param $menu_name
- *   The named menu links to return
+ *   The named menu links to return.
+ * @param $only_active_trail
+ *   Whether to only return the links in the active trail (TRUE) instead of all
+ *   links on every level of the menu link tree (FALSE). Defaults to FALSE.
+ *   Internally used for breadcrumbs only.
  * @return
  *   An array of menu links, in the order they should be rendered. The array
  *   is a list of associative arrays -- these have two keys, link and below.
  *   link is a menu item, ready for theming as a link. Below represents the
  *   submenu below the link if there is one, and it is a subtree that has the
  *   same structure described for the top-level array.
  */
-function menu_tree_page_data($menu_name = 'navigation') {
+function menu_tree_page_data($menu_name = 'navigation', $only_active_trail = FALSE) {
   static $tree = array();
 
   // Load the menu item corresponding to the current page.
   if ($item = menu_get_item()) {
     // Generate a cache ID (cid) specific for this page.
     $cid = 'links:'. $menu_name .':page-cid:'. $item['href'] .':'. (int)$item['access'];
+    // If we are asked for the active trail only, and $menu_name has not been
+    // built and cached for this page yet, then this likely means that it
+    // won't be built anymore, as this function is invoked from
+    // template_process_page(). So in order to not build a giant menu tree that
+    // needs to be checked for access on all levels, we simply check whether we
+    // have the menu already in cache, or otherwise, build a minimum tree
+    // containing the breadcrumb/active trail only.
+    // @see menu_set_active_trail()
+    if (!isset($tree[$cid]) && $only_active_trail) {
+      $cid .= ':trail';
+    }
 
     if (!isset($tree[$cid])) {
       // If the static variable doesn't have the data, check {cache_menu}.
       $cache = cache_get($cid, 'cache_menu');
       if ($cache && isset($cache->data)) {
         // If the cache entry exists, it will just be the cid for the actual data.
         // This avoids duplication of large amounts of data.
         $cache = cache_get($cache->data, 'cache_menu');
         if ($cache && isset($cache->data)) {
@@ -904,19 +919,19 @@ function menu_tree_page_data($menu_name 
           }
           // We always want all the top-level links with plid == 0.
           $parents[] = '0';
 
           // Use array_values() so that the indices are numeric for array_merge().
           $args = $parents = array_unique(array_values($parents));
           $placeholders = implode(', ', array_fill(0, count($args), '%d'));
           $expanded = variable_get('menu_expanded', array());
           // Check whether the current menu has any links set to be expanded.
-          if (in_array($menu_name, $expanded)) {
+          if (!$only_active_trail && in_array($menu_name, $expanded)) {
             // Collect all the links set to be expanded, and then add all of
             // their children to the list as well.
             do {
               $result = db_query("SELECT mlid FROM {menu_links} WHERE menu_name = '%s' AND expanded = 1 AND has_children = 1 AND plid IN (". $placeholders .') AND mlid NOT IN ('. $placeholders .')', array_merge(array($menu_name), $args, $args));
               $num_rows = FALSE;
               while ($item = db_fetch_array($result)) {
                 $args[] = $item['mlid'];
                 $num_rows = TRUE;
               }
@@ -931,19 +946,19 @@ function menu_tree_page_data($menu_name 
           $placeholders = '%d';
           $parents = array();
         }
         // Select the links from the table, and recursively build the tree. We
         // LEFT JOIN since there is no match in {menu_router} for an external
         // link.
         $data['tree'] = menu_tree_data(db_query("
           SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.type, m.description, ml.*
           FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path
-          WHERE ml.menu_name = '%s' AND ml.plid IN (". $placeholders .")
+          WHERE ml.menu_name = '%s' AND " . ($only_active_trail ? 'ml.mlid' : 'ml.plid') . " IN (". $placeholders .")
           ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC, p6 ASC, p7 ASC, p8 ASC, p9 ASC", $args), $parents);
         $data['node_links'] = array();
         menu_tree_collect_node_links($data['tree'], $data['node_links']);
         // Cache the data, if it is not already in the cache.
         $tree_cid = _menu_tree_cid($menu_name, $data);
         if (!cache_get($tree_cid, 'cache_menu')) {
           cache_set($tree_cid, $data, 'cache_menu');
         }
         // Cache the cid of the (shared) data using the page-specific cid.
@@ -993,33 +1008,31 @@ function menu_tree_collect_node_links(&$
  * Check access and perform other dynamic operations for each link in the tree.
  *
  * @param $tree
  *   The menu tree you wish to operate on.
  * @param $node_links
  *   A collection of node link references generated from $tree by
  *   menu_tree_collect_node_links().
  */
 function menu_tree_check_access(&$tree, $node_links = array()) {
-
   if ($node_links) {
     // Use db_rewrite_sql to evaluate view access without loading each full node.
     $nids = array_keys($node_links);
     $placeholders = '%d'. str_repeat(', %d', count($nids) - 1);
     $result = db_query(db_rewrite_sql("SELECT n.nid FROM {node} n WHERE n.status = 1 AND n.nid IN (". $placeholders .")"), $nids);
     while ($node = db_fetch_array($result)) {
       $nid = $node['nid'];
       foreach ($node_links[$nid] as $mlid => $link) {
         $node_links[$nid][$mlid]['access'] = TRUE;
       }
     }
   }
   _menu_tree_check_access($tree);
-  return;
 }
 
 /**
  * Recursive helper function for menu_tree_check_access()
  */
 function _menu_tree_check_access(&$tree) {
   $new_tree = array();
   foreach ($tree as $key => $v) {
     $item = &$tree[$key]['link'];
@@ -1498,19 +1511,19 @@ function menu_get_active_menu_name() {
  * Note that this may not have the desired effect unless invoked very early
  * in the page load, such as during hook_boot, or unless you call
  * menu_execute_active_handler() to generate your page output.
  */
 function menu_set_active_item($path) {
   $_GET['q'] = $path;
 }
 
 /**
- * Sets or gets the active trail (path to root menu root) of the current page.
+ * Sets or gets the active trail (path to menu tree root) of the current page.
  *
  * @param $new_trail
  *   Menu trail to set, or NULL to use previously-set or calculated trail. If
  *   supplying a trail, use the same format as the return value (see below).
  *
  * @return
  *   Path to menu root of the current page, as an array of menu link items,
  *   starting with the site's home page. Each link item is an associative array
  *   with the following components:
@@ -1551,19 +1564,22 @@ function menu_set_active_trail($new_trai
         }
       }
       // Retrieve the menu item using the root path after wildcard replacement.
       $root_item = menu_get_item(implode('/', $parts));
       if ($root_item && $root_item['access']) {
         $item = $root_item;
       }
     }
 
-    $tree = menu_tree_page_data(menu_get_active_menu_name());
+    // Pass TRUE for $only_active_trail to make menu_tree_page_data() build a
+    // stripped down menu tree containing the active trail only, in case the
+    // given menu has not been built in this request yet.
+    $tree = menu_tree_page_data(menu_get_active_menu_name(), TRUE);
     list($key, $curr) = each($tree);
 
     while ($curr) {
       // Terminate the loop when we find the current path in the active trail.
       if ($curr['link']['href'] == $item['href']) {
         $trail[] = $curr['link'];
         $curr = FALSE;
       }
       else {
@@ -1600,19 +1616,19 @@ function menu_get_active_trail() {
 function menu_get_active_breadcrumb() {
   $breadcrumb = array();
 
   // No breadcrumb for the front page.
   if (drupal_is_front_page()) {
     return $breadcrumb;
   }
 
   $item = menu_get_item();
-  if ($item && $item['access']) {
+  if (!empty($item['access'])) {
     $active_trail = menu_get_active_trail();
 
     foreach ($active_trail as $parent) {
       $breadcrumb[] = l($parent['title'], $parent['href'], $parent['localized_options']);
     }
     $end = end($active_trail);
 
     // Don't show a link to the current page in the breadcrumb trail.
     if ($item['href'] == $end['href'] || ($item['type'] == MENU_DEFAULT_LOCAL_TASK && $end['href'] != '<front>')) {
