One of the issues I wrestled with in building websites with Drupal was how difficult it seemed to access the entire menu tree in your page template. There are several modules available that people use to “solve” this issue, but at the cost of overcomplexity and one specific feature: knowing which menu item is active or in the active trail.
At first I wrote some code to make it work, until I discovered I was duplicating code already present in Drupal. So in the end the solution turned out to be extremely simple as it is based on a small tweak in the menu_tree() function.
The code below generates a nested unsorted list (UL) which you can style in any way you like. You should be able to paste the code in any template.php file and see it working by calling print menu_tree_full(’primary-links’);.
<?php
function menu_tree_full($menu_name = 'navigation') {
static $menu_output = array();
if (!isset($menu_output[$menu_name])) {
$tree = menu_tree_all_data($menu_name); // This is the tweak!
$menu_output[$menu_name] = menu_tree_output($tree);
}
return $menu_output[$menu_name];
}
The problem with this is that it doesn’t really tell us the active trail to the menu; it shows which menu leafs are expanded, but if you have other branches expanded by default (in menu administration), then it’s not obvious how we can tell which is which using CSS. The next code fixes that by adding active and active-trail to the LI classes. It includes a slightly modified menu_tree_full().
function menu_tree_full($menu_name = 'navigation') {
static $menu_output = array();
if (!isset($menu_output[$menu_name])) {
$tree = menu_find_active_trail(menu_tree_all_data($menu_name));
$menu_output[$menu_name] = menu_tree_output($tree);
}
return $menu_output[$menu_name];
}
/**
* Wrapper function
*/
function menu_find_active_trail(&$menu_tree) {
$item = menu_get_item();
_menu_find_active_trail($menu_tree, $item);
return $menu_tree;
}
/**
* Recursive function to find the active menu and the active trail in the given tree.
*/
function _menu_find_active_trail(&$menu_tree, $item) {
$level_is_expanded = FALSE;
foreach($menu_tree as &$menu_item) {
$link = &$menu_item['link'];
if ($link['href']==$item['href']) { // Found the exact location in the tree
$link['active'] = TRUE;
$link['in_active_trail'] = TRUE;
return true;
} else {
if ($link['has_children']) {
$result = _menu_find_active_trail($menu_item['below'], $item);
$link['in_active_trail'] = $result;
if ($result) $level_is_expanded = TRUE;
}
}
}
return $level_is_expanded;
}

Thank you for this tutorial!
so your solution is changing the core: menu module. but this is not a very nice idea especially when you want to update drupal core system. is there anyway to overwrite function menu_tree_full without changing the core?
Sure. You could use different method names and package the whole thing as a module.
If I find some time I can make the module and put it up on Drupal.org.
Thanks for this. Very surprised at how far I had to dig before I found this.
Thank you. You should poke and prod the drupal devs until this is the standard way of working.
Thank you.
Thank you. However it seems there is a problem with this algo if 2 items link to the same content in the same branch
“Thank you. You should poke and prod the drupal devs until this is the standard way of working.”
Agreed. Every word.
Thanks a lot. It was very useful.
Could this code be modified to work in D7?
Probably, yes. The core menu API doesn’t seem to have changed (or at least not by much) from Drupal 6 to Drupal 7. I’d say give it a shot and shout if you need help.
Struggling to get the above code ported over to D7 (I’m not much of a programmer!)
I think menu _tree_full has been replaced/dropped but I’m not sure what with?
Any help would be greatly appreciated.
S
Hi all,
I’ve made a similar function that you can use in D7.
function menu_tree_all_data_with_active_trail($menu_name) {
function menu_tree_active_trail(&$tree_data, $menu_item) {
foreach($tree_data as &$item) {
if(!empty($item['link']) && $item['link']['href'] == $menu_item['href']) {
$item['link']['in_active_trail'] = TRUE;
return TRUE;
} elseif(!empty($item['below'])) {
if(menu_tree_active_trail($item['below'], $menu_item)) {
$item['link']['in_active_trail'] = TRUE;
return TRUE;
}
}
}
return FALSE;
}
$menu_item = menu_get_item();
$menu_tree = menu_tree_all_data($menu_name);
menu_tree_active_trail($menu_tree, $menu_item);
return $menu_tree;
}
Usage is the same as in menu_tree_all_data()
$tree = menu_tree_all_data_with_active_trail('menu-product-categories');