#509584 by pwolanin: Various API fixes for menu_tree_data() including depth param.
parent
8765df6593
commit
03a5563b89
|
@ -383,6 +383,7 @@ function menu_get_item($path = NULL, $router_item = NULL) {
|
||||||
->execute()->fetchAssoc();
|
->execute()->fetchAssoc();
|
||||||
if ($router_item) {
|
if ($router_item) {
|
||||||
$map = _menu_translate($router_item, $original_map);
|
$map = _menu_translate($router_item, $original_map);
|
||||||
|
$router_item['original_map'] = $original_map;
|
||||||
if ($map === FALSE) {
|
if ($map === FALSE) {
|
||||||
$router_items[$path] = FALSE;
|
$router_items[$path] = FALSE;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
@ -850,20 +851,25 @@ function menu_tree_output($tree) {
|
||||||
*
|
*
|
||||||
* @param $menu_name
|
* @param $menu_name
|
||||||
* The named menu links to return
|
* The named menu links to return
|
||||||
* @param $item
|
* @param $link
|
||||||
* A fully loaded menu link, or NULL. If a link is supplied, only the
|
* 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.
|
* 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
|
* @return
|
||||||
* An tree of menu links in an array, in the order they should be rendered.
|
* 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());
|
$tree = &drupal_static(__FUNCTION__, array());
|
||||||
|
|
||||||
// Use $mlid as a flag for whether the data being loaded is for the whole tree.
|
// Use $mlid as a flag for whether the data being loaded is for the whole tree.
|
||||||
$mlid = isset($item['mlid']) ? $item['mlid'] : 0;
|
$mlid = isset($link['mlid']) ? $link['mlid'] : 0;
|
||||||
// Generate a cache ID (cid) specific for this $menu_name and $item.
|
// Generate a cache ID (cid) specific for this $menu_name, $item, and depth.
|
||||||
$cid = 'links:' . $menu_name . ':all-cid:' . $mlid;
|
$cid = 'links:' . $menu_name . ':all-cid:' . $mlid . ':' . (int)$max_depth;
|
||||||
|
|
||||||
if (!isset($tree[$cid])) {
|
if (!isset($tree[$cid])) {
|
||||||
// If the static variable doesn't have the data, check {cache_menu}.
|
// If the static variable doesn't have the data, check {cache_menu}.
|
||||||
|
@ -878,7 +884,8 @@ function menu_tree_all_data($menu_name, $item = NULL) {
|
||||||
}
|
}
|
||||||
// If the tree data was not in the cache, $data will be NULL.
|
// If the tree data was not in the cache, $data will be NULL.
|
||||||
if (!isset($data)) {
|
if (!isset($data)) {
|
||||||
// Build and run the query, and build the tree.
|
// Build the query using a LEFT JOIN since there is no match in
|
||||||
|
// {menu_router} for an external link.
|
||||||
$query = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC));
|
$query = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC));
|
||||||
$query->leftJoin('menu_router', 'm', 'm.path = ml.router_path');
|
$query->leftJoin('menu_router', 'm', 'm.path = ml.router_path');
|
||||||
$query->fields('ml');
|
$query->fields('ml');
|
||||||
|
@ -899,27 +906,32 @@ function menu_tree_all_data($menu_name, $item = NULL) {
|
||||||
$query->orderBy('p' . $i, 'ASC');
|
$query->orderBy('p' . $i, 'ASC');
|
||||||
}
|
}
|
||||||
$query->condition('ml.menu_name', $menu_name);
|
$query->condition('ml.menu_name', $menu_name);
|
||||||
|
if (isset($max_depth)) {
|
||||||
|
$query->condition('ml.depth', $max_depth, '<=');
|
||||||
|
}
|
||||||
if ($mlid) {
|
if ($mlid) {
|
||||||
// The tree is for a single item, so we need to match the values in its
|
// 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.
|
// p columns and 0 (the top level) with the plid values of other links.
|
||||||
$args = array(0);
|
$args = array(0);
|
||||||
for ($i = 1; $i < MENU_MAX_DEPTH; $i++) {
|
for ($i = 1; $i < MENU_MAX_DEPTH; $i++) {
|
||||||
$args[] = $item["p$i"];
|
$args[] = $link["p$i"];
|
||||||
}
|
}
|
||||||
$args = array_unique($args);
|
$args = array_unique($args);
|
||||||
$query->condition('ml.plid', $args, 'IN');
|
$query->condition('ml.plid', $args, 'IN');
|
||||||
$parents = $args;
|
$parents = $args;
|
||||||
$parents[] = $item['mlid'];
|
$parents[] = $link['mlid'];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Get all links in this menu.
|
// Get all links in this menu.
|
||||||
$parents = array();
|
$parents = array();
|
||||||
}
|
}
|
||||||
// Select the links from the table, and recursively build the tree. We
|
// Select the links from the table, and build an ordered array of links
|
||||||
// LEFT JOIN since there is no match in {menu_router} for an external
|
// using the query result object.
|
||||||
// link.
|
$links = array();
|
||||||
$data['tree'] = menu_tree_data($query->execute(), $parents);
|
foreach ($query->execute() as $item) {
|
||||||
|
$links[] = $item;
|
||||||
|
}
|
||||||
|
$data['tree'] = menu_tree_data($links, $parents);
|
||||||
$data['node_links'] = array();
|
$data['node_links'] = array();
|
||||||
menu_tree_collect_node_links($data['tree'], $data['node_links']);
|
menu_tree_collect_node_links($data['tree'], $data['node_links']);
|
||||||
// Cache the data, if it is not already in the cache.
|
// Cache the data, if it is not already in the cache.
|
||||||
|
@ -946,6 +958,9 @@ function menu_tree_all_data($menu_name, $item = NULL) {
|
||||||
*
|
*
|
||||||
* @param $menu_name
|
* @param $menu_name
|
||||||
* The named menu links to return
|
* The named menu links to return
|
||||||
|
* @param $max_depth
|
||||||
|
* Optional maximum depth of links to retrieve.
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
* An array of menu links, in the order they should be rendered. The array
|
* 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.
|
* is a list of associative arrays -- these have two keys, link and below.
|
||||||
|
@ -953,13 +968,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
|
* submenu below the link if there is one, and it is a subtree that has the
|
||||||
* same structure described for the top-level array.
|
* 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());
|
$tree = &drupal_static(__FUNCTION__, array());
|
||||||
|
|
||||||
// Load the menu item corresponding to the current page.
|
// Load the menu item corresponding to the current page.
|
||||||
if ($item = menu_get_item()) {
|
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.
|
// 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 (!isset($tree[$cid])) {
|
||||||
// If the static variable doesn't have the data, check {cache_menu}.
|
// If the static variable doesn't have the data, check {cache_menu}.
|
||||||
|
@ -1070,7 +1088,15 @@ function menu_tree_page_data($menu_name) {
|
||||||
}
|
}
|
||||||
$query->condition('ml.menu_name', $menu_name);
|
$query->condition('ml.menu_name', $menu_name);
|
||||||
$query->condition('ml.plid', $args, 'IN');
|
$query->condition('ml.plid', $args, 'IN');
|
||||||
$data['tree'] = menu_tree_data($query->execute(), $parents);
|
if (isset($max_depth)) {
|
||||||
|
$query->condition('ml.depth', $max_depth, '<=');
|
||||||
|
}
|
||||||
|
// Build an ordered array of links using the query result object.
|
||||||
|
$links = array();
|
||||||
|
foreach ($query->execute() as $item) {
|
||||||
|
$links[] = $item;
|
||||||
|
}
|
||||||
|
$data['tree'] = menu_tree_data($links, $parents);
|
||||||
$data['node_links'] = array();
|
$data['node_links'] = array();
|
||||||
menu_tree_collect_node_links($data['tree'], $data['node_links']);
|
menu_tree_collect_node_links($data['tree'], $data['node_links']);
|
||||||
// Cache the data, if it is not already in the cache.
|
// Cache the data, if it is not already in the cache.
|
||||||
|
@ -1176,81 +1202,54 @@ function _menu_tree_check_access(&$tree) {
|
||||||
/**
|
/**
|
||||||
* Build the data representing a menu tree.
|
* Build the data representing a menu tree.
|
||||||
*
|
*
|
||||||
* @param $result
|
* @param $links
|
||||||
* The database result.
|
* An array of links (associative arrays) ordered by p1..p9.
|
||||||
* @param $parents
|
* @param $parents
|
||||||
* An array of the plid values that represent the path from the current page
|
* An array of the plid values that represent the path from the current page
|
||||||
* to the root of the menu tree.
|
* to the root of the menu tree.
|
||||||
* @param $depth
|
* @param $depth
|
||||||
* The depth of the current menu tree.
|
* The minimum depth of any link in the $links array.
|
||||||
* @return
|
* @return
|
||||||
* See menu_tree_page_data for a description of the data structure.
|
* See menu_tree_page_data for a description of the data structure.
|
||||||
*/
|
*/
|
||||||
function menu_tree_data($result = NULL, $parents = array(), $depth = 1) {
|
function menu_tree_data(array $links, array $parents = array(), $depth = 1) {
|
||||||
list(, $tree) = _menu_tree_data($result, $parents, $depth);
|
// Reverse the array so we can use the more efficient array_pop() function.
|
||||||
return $tree;
|
$links = array_reverse($links);
|
||||||
|
return _menu_tree_data($links, $parents, $depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursive helper function to build the data representing a menu tree.
|
* 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 function is a bit complex because the rendering of a link depends on
|
||||||
* the next menu item. So we are always rendering the element previously
|
* the next menu link.
|
||||||
* processed not the current one.
|
|
||||||
*/
|
*/
|
||||||
function _menu_tree_data($result, $parents, $depth, $previous_element = '') {
|
function _menu_tree_data(&$links, $parents, $depth) {
|
||||||
$remnant = NULL;
|
$done = FALSE;
|
||||||
$tree = array();
|
$tree = array();
|
||||||
foreach ($result as $item) {
|
while (!$done && $item = array_pop($links)) {
|
||||||
// We need to determine if we're on the path to root so we can later build
|
// We need to determine if we're on the path to root so we can later build
|
||||||
// the correct active trail and breadcrumb.
|
// the correct active trail and breadcrumb.
|
||||||
$item['in_active_trail'] = in_array($item['mlid'], $parents);
|
$item['in_active_trail'] = in_array($item['mlid'], $parents);
|
||||||
// The current item is the first in a new submenu.
|
// Look ahead to the next link, but leave it on the array so it's available
|
||||||
if ($item['depth'] > $depth) {
|
// to other recursive function calls if we return or build a sub-tree.
|
||||||
// _menu_tree returns an item and the menu tree structure.
|
$next = end($links);
|
||||||
list($item, $below) = _menu_tree_data($result, $parents, $item['depth'], $item);
|
// Add the current link to the tree.
|
||||||
if ($previous_element) {
|
$tree[$item['mlid']] = array(
|
||||||
$tree[$previous_element['mlid']] = array(
|
'link' => $item,
|
||||||
'link' => $previous_element,
|
'below' => array(),
|
||||||
'below' => $below,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$tree = $below;
|
|
||||||
}
|
|
||||||
// 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;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($previous_element) {
|
|
||||||
// We have one more link dangling.
|
|
||||||
$tree[$previous_element['mlid']] = array(
|
|
||||||
'link' => $previous_element,
|
|
||||||
'below' => FALSE,
|
|
||||||
);
|
);
|
||||||
|
// Check whether the next link is the first in a new sub-tree.
|
||||||
|
if ($next && $next['depth'] > $depth) {
|
||||||
|
// Recursively call _menu_tree_data to build the sub-tree.
|
||||||
|
$tree[$item['mlid']]['below'] = _menu_tree_data($links, $parents, $next['depth']);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Determine if we should exit the loop and return.
|
||||||
|
$done = (!$next || $next['depth'] < $depth);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return array($remnant, $tree);
|
return $tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1427,7 +1426,7 @@ function menu_navigation_links($menu_name, $level = 0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the menu hierarchy for the current page.
|
// 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.
|
// Go down the active trail until the right level is reached.
|
||||||
while ($level-- > 0 && $tree) {
|
while ($level-- > 0 && $tree) {
|
||||||
|
|
|
@ -600,14 +600,15 @@ function book_update_bid($book_link) {
|
||||||
* A linear array of menu links in the order that the links are shown in the
|
* 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
|
* 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
|
* 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) {
|
function book_get_flat_menu($book_link) {
|
||||||
$flat = &drupal_static(__FUNCTION__, array());
|
$flat = &drupal_static(__FUNCTION__, array());
|
||||||
|
|
||||||
if (!isset($flat[$book_link['mlid']])) {
|
if (!isset($flat[$book_link['mlid']])) {
|
||||||
// Call menu_tree_all_data() to take advantage of the menu system's caching.
|
// 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();
|
$flat[$book_link['mlid']] = array();
|
||||||
_book_flatten_menu($tree, $flat[$book_link['mlid']]);
|
_book_flatten_menu($tree, $flat[$book_link['mlid']]);
|
||||||
}
|
}
|
||||||
|
@ -1156,16 +1157,16 @@ function book_link_load($mlid) {
|
||||||
* The root of the subtree will be the link passed as a parameter, so the
|
* 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.
|
* returned tree will contain this item and all its descendents in the menu tree.
|
||||||
*
|
*
|
||||||
* @param $item
|
* @param $link
|
||||||
* A fully loaded menu link.
|
* A fully loaded menu link.
|
||||||
* @return
|
* @return
|
||||||
* An subtree of menu links in an array, in the order they should be rendered.
|
* 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());
|
$tree = &drupal_static(__FUNCTION__, array());
|
||||||
|
|
||||||
// Generate a cache ID (cid) specific for this $menu_name and $item.
|
// Generate a cache ID (cid) specific for this $menu_name and $link.
|
||||||
$cid = 'links:' . $item['menu_name'] . ':subtree-cid:' . $item['mlid'];
|
$cid = 'links:' . $link['menu_name'] . ':subtree-cid:' . $link['mlid'];
|
||||||
|
|
||||||
if (!isset($tree[$cid])) {
|
if (!isset($tree[$cid])) {
|
||||||
$cache = cache_get($cid, 'cache_menu');
|
$cache = cache_get($cid, 'cache_menu');
|
||||||
|
@ -1188,15 +1189,18 @@ function book_menu_subtree_data($item) {
|
||||||
$query->fields($book_alias);
|
$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($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->fields('ml');
|
||||||
$query->condition('menu_name', $item['menu_name']);
|
$query->condition('menu_name', $link['menu_name']);
|
||||||
for ($i = 1; $i <= MENU_MAX_DEPTH && $item["p$i"]; ++$i) {
|
for ($i = 1; $i <= MENU_MAX_DEPTH && $link["p$i"]; ++$i) {
|
||||||
$query->condition("p$i", $item["p$i"]);
|
$query->condition("p$i", $link["p$i"]);
|
||||||
}
|
}
|
||||||
for ($i = 1; $i <= MENU_MAX_DEPTH; ++$i) {
|
for ($i = 1; $i <= MENU_MAX_DEPTH; ++$i) {
|
||||||
$query->orderBy("p$i");
|
$query->orderBy("p$i");
|
||||||
}
|
}
|
||||||
|
$links = array();
|
||||||
$data['tree'] = menu_tree_data($query->execute(), array(), $item['depth']);
|
foreach ($query->execute() as $item) {
|
||||||
|
$links[] = $item;
|
||||||
|
}
|
||||||
|
$data['tree'] = menu_tree_data($links, array(), $link['depth']);
|
||||||
$data['node_links'] = array();
|
$data['node_links'] = array();
|
||||||
menu_tree_collect_node_links($data['tree'], $data['node_links']);
|
menu_tree_collect_node_links($data['tree'], $data['node_links']);
|
||||||
// Compute the real cid for book subtree data.
|
// Compute the real cid for book subtree data.
|
||||||
|
|
|
@ -110,7 +110,7 @@ class BookTestCase extends DrupalWebTestCase {
|
||||||
|
|
||||||
// Check previous, up, and next links.
|
// Check previous, up, and next links.
|
||||||
if ($previous) {
|
if ($previous) {
|
||||||
$this->assertRaw(l('‹ ' . $previous->title, 'node/' . $previous->nid, array('attributes' => array('class' => array('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' => array('page-previous'), 'title' => t('Go to previous page')))), t('Previous page link found.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($up) {
|
if ($up) {
|
||||||
|
|
|
@ -48,7 +48,11 @@ function menu_overview_form(&$form_state, $menu) {
|
||||||
WHERE ml.menu_name = :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";
|
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));
|
$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();
|
$node_links = array();
|
||||||
menu_tree_collect_node_links($tree, $node_links);
|
menu_tree_collect_node_links($tree, $node_links);
|
||||||
// We indicate that a menu administrator is running the menu access check.
|
// We indicate that a menu administrator is running the menu access check.
|
||||||
|
|
Loading…
Reference in New Issue