diff --git includes/menu.inc includes/menu.inc index 1daee3c..080cdac 100644 --- includes/menu.inc +++ includes/menu.inc @@ -370,6 +370,7 @@ function menu_get_item($path = NULL, $router_item = NULL) { ->execute()->fetchAssoc(); if ($router_item) { $map = _menu_translate($router_item, $original_map); + $router_item['original_map'] = $original_map; if ($map === FALSE) { $router_items[$path] = FALSE; return FALSE; @@ -836,20 +837,25 @@ function menu_tree_output($tree) { * * @param $menu_name * The named menu links to return - * @param $item + * @param $link * A fully loaded menu link, or NULL. If a link is supplied, only the - * path to root will be included in the returned tree- as if this link + * path to root will be included in the returned tree - as if this link * represented the current page in a visible menu. + * @param $max_depth + * Optional maximum depth of links to retrieve. Typically useful if only one + * or two levels of a sub tree are needed in conjunction with a non-NULL + * $link, in which case $max_depth should be greater than $link['depth']. + * * @return * An tree of menu links in an array, in the order they should be rendered. */ -function menu_tree_all_data($menu_name, $item = NULL) { +function menu_tree_all_data($menu_name, $link = NULL, $max_depth = NULL) { $tree = &drupal_static(__FUNCTION__, array()); // Use $mlid as a flag for whether the data being loaded is for the whole tree. - $mlid = isset($item['mlid']) ? $item['mlid'] : 0; - // Generate a cache ID (cid) specific for this $menu_name and $item. - $cid = 'links:' . $menu_name . ':all-cid:' . $mlid; + $mlid = isset($link['mlid']) ? $link['mlid'] : 0; + // Generate a cache ID (cid) specific for this $menu_name, $item, and depth. + $cid = 'links:' . $menu_name . ':all-cid:' . $mlid . ':' . (int)$max_depth; if (!isset($tree[$cid])) { // If the static variable doesn't have the data, check {cache_menu}. @@ -885,18 +891,20 @@ function menu_tree_all_data($menu_name, $item = NULL) { $query->orderBy('p' . $i, 'ASC'); } $query->condition('ml.menu_name', $menu_name); - + if (isset($max_depth)) { + $query->condition('ml.depth', $max_depth, '<='); + } if ($mlid) { // The tree is for a single item, so we need to match the values in its // p columns and 0 (the top level) with the plid values of other links. $args = array(0); for ($i = 1; $i < MENU_MAX_DEPTH; $i++) { - $args[] = $item["p$i"]; + $args[] = $link["p$i"]; } $args = array_unique($args); $query->condition('ml.plid', $args, 'IN'); $parents = $args; - $parents[] = $item['mlid']; + $parents[] = $link['mlid']; } else { // Get all links in this menu. @@ -905,7 +913,11 @@ function menu_tree_all_data($menu_name, $item = NULL) { // 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($query->execute(), $parents); + $links = array(); + foreach ($query->execute() as $item) { + $links[] = $item; + } + $data['tree'] = menu_tree_data($links, $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. @@ -932,6 +944,9 @@ function menu_tree_all_data($menu_name, $item = NULL) { * * @param $menu_name * The named menu links to return + * @param $max_depth + * Optional maximum depth of links to retrieve. + * * @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. @@ -939,13 +954,16 @@ function menu_tree_all_data($menu_name, $item = NULL) { * 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) { +function menu_tree_page_data($menu_name, $max_depth = NULL) { $tree = &drupal_static(__FUNCTION__, array()); // Load the menu item corresponding to the current page. if ($item = menu_get_item()) { + if (isset($max_depth)) { + $max_depth = min($max_depth, MENU_MAX_DEPTH); + } // Generate a cache ID (cid) specific for this page. - $cid = 'links:' . $menu_name . ':page-cid:' . $item['href'] . ':' . (int)$item['access']; + $cid = 'links:' . $menu_name . ':page-cid:' . $item['href'] . ':' . (int)$item['access'] . ':' . (int)$max_depth; if (!isset($tree[$cid])) { // If the static variable doesn't have the data, check {cache_menu}. @@ -1056,7 +1074,15 @@ function menu_tree_page_data($menu_name) { } $query->condition('ml.menu_name', $menu_name); $query->condition('ml.plid', $args, 'IN'); - $data['tree'] = menu_tree_data($query->execute(), $parents); + if (isset($max_depth)) { + $query->condition('ml.depth', $max_depth, '<='); + } + + $links = array(); + foreach ($query->execute() as $item) { + $links[] = $item; + } + $data['tree'] = menu_tree_data($links, $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. @@ -1162,8 +1188,8 @@ function _menu_tree_check_access(&$tree) { /** * Build the data representing a menu tree. * - * @param $result - * The database result. + * @param $links + * An array of links (associative arrays) ordered by p1..p9. * @param $parents * An array of the plid values that represent the path from the current page * to the root of the menu tree. @@ -1172,71 +1198,41 @@ function _menu_tree_check_access(&$tree) { * @return * See menu_tree_page_data for a description of the data structure. */ -function menu_tree_data($result = NULL, $parents = array(), $depth = 1) { - list(, $tree) = _menu_tree_data($result, $parents, $depth); - return $tree; +function menu_tree_data($links, $parents = array(), $depth = 1) { + $links = array_reverse($links); + return _menu_tree_data($links, $parents, $depth); } /** * Recursive helper function to build the data representing a menu tree. * - * The function is a bit complex because the rendering of an item depends on - * the next menu item. So we are always rendering the element previously - * processed not the current one. + * The function is a bit complex because the rendering of a link depends on + * the next menu link. */ -function _menu_tree_data($result, $parents, $depth, $previous_element = '') { +function _menu_tree_data(&$links, $parents, $depth) { $remnant = NULL; $tree = array(); - foreach ($result as $item) { + while ($item = array_pop($links)) { // We need to determine if we're on the path to root so we can later build // the correct active trail and breadcrumb. $item['in_active_trail'] = in_array($item['mlid'], $parents); - // The current item is the first in a new submenu. - if ($item['depth'] > $depth) { - // _menu_tree returns an item and the menu tree structure. - list($item, $below) = _menu_tree_data($result, $parents, $item['depth'], $item); - if ($previous_element) { - $tree[$previous_element['mlid']] = array( - 'link' => $previous_element, - 'below' => $below, - ); - } - else { - $tree = $below; - } + $next = end($links); + $tree[$item['mlid']] = array( + 'link' => $item, + 'below' => array(), + ); + // The next item is the first in a new submenu. + if ($next && $next['depth'] > $depth) { + // _menu_tree_data returns the sub-tree. + $tree[$item['mlid']]['below'] = _menu_tree_data($links, $parents, $next['depth']); // We need to fall back one level. - if (!isset($item) || $item['depth'] < $depth) { - return array($item, $tree); - } - // This will be the link to be output in the next iteration. - $previous_element = $item; - } - // We are at the same depth, so we use the previous element. - elseif ($item['depth'] == $depth) { - if ($previous_element) { - // Only the first time. - $tree[$previous_element['mlid']] = array( - 'link' => $previous_element, - 'below' => FALSE, - ); - } - // This will be the link to be output in the next iteration. - $previous_element = $item; } - // The submenu ended with the previous item, so pass back the current item. - else { - $remnant = $item; + elseif (!$next || $next['depth'] < $depth) { + // Exit loop and return. break; } } - if ($previous_element) { - // We have one more link dangling. - $tree[$previous_element['mlid']] = array( - 'link' => $previous_element, - 'below' => FALSE, - ); - } - return array($remnant, $tree); + return $tree; } /** @@ -1404,7 +1400,7 @@ function menu_navigation_links($menu_name, $level = 0) { } // Get the menu hierarchy for the current page. - $tree = menu_tree_page_data($menu_name); + $tree = menu_tree_page_data($menu_name, $level + 1); // Go down the active trail until the right level is reached. while ($level-- > 0 && $tree) { diff --git modules/book/book.module modules/book/book.module index bbd900c..e0e0e26 100644 --- modules/book/book.module +++ modules/book/book.module @@ -592,14 +592,15 @@ function book_update_bid($book_link) { * A linear array of menu links in the order that the links are shown in the * menu, so the previous and next pages are the elements before and after the * element corresponding to $node. The children of $node (if any) will come - * immediately after it in the array. + * immediately after it in the array, and links will only be fetched as deep + * as one level deeper than $book_link. */ function book_get_flat_menu($book_link) { $flat = &drupal_static(__FUNCTION__, array()); if (!isset($flat[$book_link['mlid']])) { // Call menu_tree_all_data() to take advantage of the menu system's caching. - $tree = menu_tree_all_data($book_link['menu_name'], $book_link); + $tree = menu_tree_all_data($book_link['menu_name'], $book_link, $book_link['depth'] + 1); $flat[$book_link['mlid']] = array(); _book_flatten_menu($tree, $flat[$book_link['mlid']]); } @@ -1148,16 +1149,16 @@ function book_link_load($mlid) { * The root of the subtree will be the link passed as a parameter, so the * returned tree will contain this item and all its descendents in the menu tree. * - * @param $item + * @param $link * A fully loaded menu link. * @return * An subtree of menu links in an array, in the order they should be rendered. */ -function book_menu_subtree_data($item) { +function book_menu_subtree_data($link) { $tree = &drupal_static(__FUNCTION__, array()); - // Generate a cache ID (cid) specific for this $menu_name and $item. - $cid = 'links:' . $item['menu_name'] . ':subtree-cid:' . $item['mlid']; + // Generate a cache ID (cid) specific for this $menu_name and $link. + $cid = 'links:' . $link['menu_name'] . ':subtree-cid:' . $link['mlid']; if (!isset($tree[$cid])) { $cache = cache_get($cid, 'cache_menu'); @@ -1180,15 +1181,18 @@ function book_menu_subtree_data($item) { $query->fields($book_alias); $query->fields($menu_router_alias, array('load_functions', 'to_arg_functions', 'access_callback', 'access_arguments', 'page_callback', 'page_arguments', 'title', 'title_callback', 'title_arguments', 'type')); $query->fields('ml'); - $query->condition('menu_name', $item['menu_name']); - for ($i = 1; $i <= MENU_MAX_DEPTH && $item["p$i"]; ++$i) { - $query->condition("p$i", $item["p$i"]); + $query->condition('menu_name', $link['menu_name']); + for ($i = 1; $i <= MENU_MAX_DEPTH && $link["p$i"]; ++$i) { + $query->condition("p$i", $link["p$i"]); } for ($i = 1; $i <= MENU_MAX_DEPTH; ++$i) { $query->orderBy("p$i"); } - - $data['tree'] = menu_tree_data($query->execute(), array(), $item['depth']); + $links = array(); + foreach ($query->execute() as $item) { + $links[] = $item; + } + $data['tree'] = menu_tree_data($links, array(), $link['depth']); $data['node_links'] = array(); menu_tree_collect_node_links($data['tree'], $data['node_links']); // Compute the real cid for book subtree data. diff --git modules/book/book.test modules/book/book.test index 0e8e37b..ccf3d3e 100644 --- modules/book/book.test +++ modules/book/book.test @@ -110,7 +110,7 @@ class BookTestCase extends DrupalWebTestCase { // Check previous, up, and next links. if ($previous) { - $this->assertRaw(l('‹ ' . $previous->title, 'node/' . $previous->nid, array('attributes' => array('class' => 'page-previous', 'title' => t('Go to previous page')))), t('Prevoius page link found.')); + $this->assertRaw(l('‹ ' . $previous->title, 'node/' . $previous->nid, array('attributes' => array('class' => 'page-previous', 'title' => t('Go to previous page')))), t('Previous page link found.')); } if ($up) { diff --git modules/menu/menu.admin.inc modules/menu/menu.admin.inc index 4640990..a86c9dc 100644 --- modules/menu/menu.admin.inc +++ modules/menu/menu.admin.inc @@ -48,7 +48,11 @@ function menu_overview_form(&$form_state, $menu) { WHERE ml.menu_name = :menu ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC, p6 ASC, p7 ASC, p8 ASC, p9 ASC"; $result = db_query($sql, array(':menu' => $menu['menu_name']), array('fetch' => PDO::FETCH_ASSOC)); - $tree = menu_tree_data($result); + $links = array(); + foreach ($result as $item) { + $links[] = $item; + } + $tree = menu_tree_data($links); $node_links = array(); menu_tree_collect_node_links($tree, $node_links); // We indicate that a menu administrator is running the menu access check.