How do I dynamically set the active menu item?

Solution 1:

For similar purpose, I used menu_set_item(). You should leave the $path argument NULL and use the menu item for the internal path of 'galleries/lastgallery' as $router_item.

function custom_nodeapi(&$node, $op, $a3 = NULL, $a4  = NULL) {
  if ($op == 'view' && $node->nid == 31) {
    $path = drupal_get_normal_path('galleries/lastgallery');
    if($path && $menu_item = menu_get_item($path)) {
      menu_set_item(NULL, $menu_item);
    }
  }
}

Solution 2:

As tmsimont pointed out, using menu_set_item() has side-effects...

A quick look at menu_set_item() and menu_get_item() shows us that they trigger hooks and database calls (). Not perfect.

If you're using Drupal 7.9+, there's a new function called menu_tree_set_path() that will do the trick:

menu_tree_set_path('user-menu', 'shopping-cart');

The first argument is the menu name followed by the path you want to select.

NOTE: This does not apply the .active class but applies .active-trail instead so you'll have to update your CSS to accommodate both!

Solution 3:

I know I'm a bit late in the game here and this already has an answer but I thought I'd post this in case it helps someone

With reference to the problem of menu_set_item(NULL, $menu_item) killing local tasks - it's not limited to this.

I found that when using the discuss module it didn't load the block. I think the issue is that by doing something like:

$path = drupal_get_normal_path('galleries/lastgallery');
$menu_item = menu_get_item($path);
menu_set_item(NULL, $menu_item);

You effectively tell drupal you're on a different page/item.

The solution that worked for me was this:

$menu_item = menu_get_item();
$menu_item['href'] = 'galleries/lastgallery';
menu_set_item(NULL, $menu_item);

This seems to just play with the href and leave rest alone so local tasks still work and so did disqus.