Community Documentation

Add a submenu tree in a block

Last updated April 7, 2010. Created by praseodym on November 20, 2007.
Edited by capono, LeeHunter, clemens.tolboom, sime. Log in to edit this page.

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

Comments

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!!

too complicated for beginner

Hi my friend, your solution is aimed at coders, have you anything on beginners terms; I am not able to see submenus on the submenu tree module, gra.

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

works with current active menu

<?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);
?>

Works great

Brilliant! This worked great for me.

no _menu_get_active_trail function

I use the above code , but when I save the block , that say no the function _menu_get_active_trail

and then I can not entry the site , and modify the wrong code to fix the problem ................

how to do next step ???? reinstall all ?

submenutree

Hi, I get the faintest hint you may be able to help me seeing you are proficient in php; first and foremost I cant get submenus or sibling menus to appear on my sidebars or act as a parent item, secondly I know an itsy weeny bit of php, can enable php in my drupal site, can paste it into a page, but for the life of me would not have the guts to fiddle around with drupal core code or any code in my filemanagers view, except for downlaoding modules and extracting them or maybe downlaoding an image or two, can you help me, gra.

This works with Drupal 7

I don't know if this is the best way, but it works fine.

<?php
  $menus
= menu_tree('main-menu');

 
$output = array();
  foreach(
$menus as $key => $data) {
    if (isset(
$data['#original_link'])) {
      if(!empty(
$data['#original_link']['in_active_trail'])) {
       
$output[$key] = $data;
      }
    }
    else {
     
$output[$key] = $data;
    }
  }

  print
drupal_render($output);
?>

If somebody can tell me about some error, or bad way, please do so!
Sorry for my bad English!

According to your snippet,

According to your snippet, this should also work:

<?php
  $menus
= menu_tree('main-menu');

 
$output = array();
  foreach(
$menus as $key => $data) {
    if (isset(
$data['#original_link']) && (!empty($data['#original_link']['in_active_trail']))) {
       
$output[$key] = $data;
      }
  }

  print
drupal_render($output);
?>

If $data['#original_link'] is set and its 'in_active_trail' is not empty, it should populate the $output array. If not, just render the empty array.

I haven't tested this, but I think this is what the code should look like.

Edit: Tested. It works!

Displaying menu items inside nodes using PHP?

I wonder how to make this work inside node pages not blocks.

In a Drupal 7 site I have 3 parent links inside main menu (with sub-links), something like this:

-About
--Team
--Contact us
--Privacy

-Sections
--Wood
--Concrete

-Blog
--Kim's blog
--Andrea's blog

I want to create three node pages named: a) About b) Sections and c) Blog, each one of them displaying a list of its child-links (as configured inside the main-menu).

Is there a PHP code that could be used in order to extract the links from the main-menu and insert them inside the node pages ?!

Thanks.

Drupal 7 submenus

The snippets above for Drupal 7 seem to only return the top level submenu. If you are after a lower level submenu, they still return the top level. I had the following code working for Drupal 6 (not mine, found it somewhere) - works when pasted into a Page (not sure about Blocks)

Drupal 6

<?php
$menus
= menu_tree_page_data('primary-links');
print
menu_tree_output(menu_tree_below($menus));

function
menu_tree_below($tree) {
  foreach (
$tree as $menu) {
    if(!empty(
$menu['link']['in_active_trail']) && $menu['below']) {
     
$r = menu_tree_below($menu['below']);
      if (
$r==0) { return $menu['below']; }
      else { return
$r; }
    }
  }
  return
0;
}
?>

I worked out I could just make a few changes, and get the following to work fine when pasted in a page. It would have saved me an hour or so of fiddling if I'd known it, so hopefully it helps someone else.

Drupal 7

<?php
$menus
= menu_tree('main-menu');
print
drupal_render(menu_tree_below($menus));

function
menu_tree_below($tree) {
  foreach (
$tree as $menu) {
    if(!empty(
$menu['#original_link']['in_active_trail']) && $menu['#below']) {
     
$r = menu_tree_below($menu['#below']);
      if (
$r==0) { return $menu['#below']; }
      else { return
$r; }
    }
  }
  return
0;
}
?>