Add a submenu tree in a block

Last modified: November 10, 2009 - 01:48

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.

  1. Go to the admin/build/block/add page.
  2. 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>'
    ?>
  3. Be sure to check the PHP code.
  4. 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;
    ?>
  5. Enable the checkbox 'Show if the following PHP code returns TRUE (PHP-mode, experts only).'
  6. Make the new block visible.
  7. When satisfied, uncomment the line as indicated in the above PHP code

theming

redtrafik - June 20, 2008 - 13:41

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

jpappe - October 1, 2008 - 10:06

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

julia_g - December 11, 2008 - 20:06

Can anyone update this for Drupal 6?

Here is my hackish Drupal 6

gengel - February 3, 2009 - 22:02

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?

milos1234 - February 18, 2009 - 17:55

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

chris_lu - February 24, 2009 - 09:10

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

netsensei - March 10, 2009 - 08:51

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']);
}
?>

works with current active menu

spidrupal - September 25, 2009 - 02:09

<?php
$menus
= menu_tree_page_data(menu_get_active_menu_name()); //get menu tree for active menu
 
$output='';
  foreach(
$menus as $data) {
      if(!empty(
$data['link']['in_active_trail'])){
       
$link = theme('menu_item_link', $data['link']);
       
$extra_class = NULL;
        if (
$data['below']) {
         
$output .= theme('menu_item', $link, $data['link']['has_children'], menu_tree_output($data['below']), $data['link']['in_active_trail'], $extra_class);
        }
        else {
         
$output .= theme('menu_item', $link, $data['link']['has_children'], '', $data['link']['in_active_trail'], $extra_class);
        }
      }
  }
  return
theme('menu_tree', $output);
?>

 
 

Drupal is a registered trademark of Dries Buytaert.