Add a submenu tree in a block
Last modified: January 15, 2009 - 19:57
Warning: might not work for menu 'node/add' trail but certainly works for 'admin'. The test done was with a custom menu. So feedback is very welcome!
You can add an expanded submenu of the primary links by creating a block containing the following code.
The end result will be a block containing a submenu of the active menu. By using block visibility the block is only visible for the appropriate submenus.
- Go to the admin/build/block/add page.
- Paste the following code into the block body:
<?php
print '<ul class="menu">'
$menu_trail= _menu_get_active_trail();
$menu_top_level = array_shift( $menu_trail);
// Comment next line when finished testing
print 'top-level-id= ' . $menu_top_level .'<br/>';
$menu_sub_menu = array_shift( $menu_trail);
print menu_tree( $menu_sub_menu);
print '</ul>'
?> - Be sure to check the PHP code.
-
Paste the following code into 'Page specific visibility settings':
<?php
// Fill in the appropriate top level ID's in a comma-separated list
$valid_top_levels = array(1, 2);
// Uncomment next line when finished testing:
//return in_array( array_shift( _menu_get_active_trail()), $valid_top_levels);
return TRUE;
?> - Enable the checkbox 'Show if the following PHP code returns TRUE (PHP-mode, experts only).'
- Make the new block visible.
- When satisfied, uncomment the line as indicated in the above PHP code

theming
Thank you for this code. It is really helpfull.
When testing it, I realize that it would be better (for me) to use theme_menu_tree instead of menu_tree function as the first adds a "class=menu" to the root "ul" tag of the printed menu. This let you theme your block easier.
Again thx Drupal ;-)
Rafik
More approaches
You can also use the Menu Block module, though I find its way of selecting menus a bit clunky.
For my site, practically every page is part of one large menu (that's separate from the primary links). I wanted each page to contain a block that only shows its children in the menu. Ultimately, I found a custom PHP block was easier (though I don't like to use PHP in block/node content as a rule), and this article was very useful as a reference.
I created a custom block whose body is:
<?php
$menu_trail= _menu_get_active_trail();
if(count($menu_trail) > 2) {
$mid = $menu_trail[count($menu_trail) - 2];
print theme('menu_tree', $mid);
}
?>
And the visibility settings are:
<?php$valid_top_levels = array(98); // where 98 is the menu ID
return in_array( array_shift( _menu_get_active_trail()), $valid_top_levels);
?>
Now, any page in that menu that has children gets a block listing the children.
Can anyone update this for
Can anyone update this for Drupal 6?
Here is my hackish Drupal 6
Here is my hackish Drupal 6 solution. It won't work via embedded PHP, though - you'll have to stick this in your own custom module (and replace "module" below with whatever name you chose)
Also, swap "primary-links" with whichever the id of the menu you wish to search through. (E.g., 'navigation')
<?php
$menu = module_find_lowest_menu(menu_tree_page_data('primary-links'));
$content .= menu_tree_output($menu);
function module_find_lowest_menu($menu) {
foreach ($menu as $id => $item) {
if (!empty($item['below'])) {
foreach ($item['below'] as $id_new => $item_new) {
$next[$id_new] = $item_new;
}
}
}
if (!empty($next)) {
return module_find_lowest_menu($next);
}
return $menu;
}
?>
ok?
Cripes this is just what I need to do, can you please explain exactly what to do with the above code. THanks!!
Here is for drupal 6
You can embed this code in a module. Please replace primary-links with your menu name.
<?php
$menus = menu_tree_page_data('primary-links');
foreach($menus as $menu) {
if(!empty($menu['link']['in_active_trail']))
echo menu_tree_output($menu['below']);
}
?>
I used your code but it
I used your code but it triggered this error when I went from a node without a submenu to a node with a submenu:
warning: Invalid argument supplied for foreach() in includes/menu.inc on line 736
Solved it with an extra condition:
<?php
$menus = menu_tree_page_data('primary-links');
foreach($menus as $menu) {
if(!empty($menu['link']['in_active_trail']) && $menu['below'])
print menu_tree_output($menu['below']);
}
?>