User login

Faking an active trail for items not in a menu: Drupal 7 active state for main menu based on content type

Situation: You have lots of content that you don't really want in a menu at all, but you want to appear to be below or within a section of the site when you are viewing that content. For instance, when viewing news content the menu link for the overall views listing should appear to be active, or rather in the active path or trail.

It is not pretty, it involves custom, special-case code, but this is not hard to do in Drupal 7. Note the need to trigger menu items to have Drupal's one run-time menu link altering hook actually run. (New links can as shown there be set, when they are saved, to be alterable at run-time. Given the special-casing involved, you likely already have your links and need to make them alterable with an update hook, as shown below. First, the code that alters the links that are marked as alterable.)

In a .module file, in this example the feature_frontend.module:

<?php
/**
* Implements hook_translated_menu_link_alter().
*
* This only runs if a menu item has been marked for alter-ability.  See
* function sdl_resave_main_menu_links() in profiles/sdl/sdl.install.
*
* The $item handed in is one of the many menu items on a page.  We check which
* menu item by looking at its link path-- much more reliable for our purposes
* than looking at its menu link ID.  Somewhat confusingly, we then get our
* information about the current context of the page from the *current* menu
* item, retrieved with menu_get_item(), which has the node object embedded in
* it if it is a node-based page (which our content *and* associated image
* galleries are).
*/
function feature_frontend_translated_menu_link_alter(&$item, $map) {
 
// Get the current menu item ('cmi').
 
$cmi = menu_get_item();
  if (
$cmi['load_functions'] && isset($cmi['load_functions'][1]) && $cmi['load_functions'][1] === 'node_load') {
    if (
$cmi['page_arguments'][0]->type === 'project' && $item['link_path'] === 'projects') {
     
$item['in_active_trail'] = TRUE;
    }
    elseif (
$cmi['page_arguments'][0]->type === 'in_the_news' && $item['link_path'] === 'news') {
     
$item['in_active_trail'] = TRUE;
    }
    elseif (
$cmi['page_arguments'][0]->type === 'event' && $item['link_path'] === 'events') {
     
$item['in_active_trail'] = TRUE;
    }
   
// The About Daniel section is the ordinary Page content type, but belongs
    // to the About Daniel book, which is our needed extra check before putting
    // it under 'Studio'.
   
elseif ($cmi['page_arguments'][0]->type === 'page' && $item['link_path'] === 'studio' && isset($cmi['page_arguments'][0]->book)) {
     
$bid = variable_get('sdl_about_daniel_nid', 18);
      if (
$cmi['page_arguments'][0]->book['bid'] == $bid) {
       
// If you can remember back that far in the elseif statement above,
        // this will put 'Studio' in the active trail when in Daniel's section.
       
$item['in_active_trail'] = TRUE;
      }
    }
   
// This case never happens in the current site, but no reason not to add
    // another elseif at the end to future-proof it.
   
elseif ($cmi['page_arguments'][0]->type === 'person' && $item['link_path'] === 'studio') {
     
$item['in_active_trail'] = TRUE;
    }
  }
}
?>

Before that will work, you need to make the menu links for altering by hook_translated_menu_link_alter():

<?php
/**
* Implements hook_update_N().
*
* Re-save menu links to mark for altering at time (and place) of display.
*/
function feature_frontend_update_7001() {
 
feature_frontend_resave_main_menu_links();
}

/**
* Re-save all main menu links as alterable, so that we can set active state.
*
* The only way to set active state, short of doing it in the theme layer with
* template_preprocess_links() which is ugly, is to use the strangely named
* hook_translated_menu_link_alter(), which unlike hook_menu_link_alter() runs
* on every page load.
*/
function feature_frontend_resave_main_menu_links() {
 
$links = menu_load_links('main-menu');
  foreach (
$links as $item) {
   
$item['options']['alter'] = TRUE;
   
menu_link_save($item);
  }
}
?>
Searched words: 
menu state active path trail per section based on content node type with special casing

Comments

Just wondering if this could

Just wondering if this could help make it less complicated:
http://drupal.org/node/1298642

It seems that those functions

It seems that those functions will be added in Drupal 7.9

Drupal 6?

Thanks for that! Any idea how one could do the same in Drupal 6? :-)

I use path auto to generate

I use path auto to generate URL based on content type and use http://drupal.org/project/menu_trail_by_path to get active trail

http://drupal.org/project/menu_position also achieves this I guess

The easiest way is to use

The easiest way is to use "menu trail by path" - and using pathauto to set the urls.

If you are organised this is quite fast, you just have make sure you have a logical URL structure.

Cotnext module

I'm fairly sure you can set the active menu trail and breadcrumbs etc using the Context module, either based on content type, path or any other filter handled by Context.

http://drupal.org/project/context

Post new comment

The content of this field is kept private and will not be shown publicly.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.
  • You can use Markdown syntax to format and style the text. Also see Markdown Extra for tables, footnotes, and more.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <img> <blockquote> <small> <h2> <h3> <h4> <h5> <h6> <sub> <sup> <p> <br> <strike> <table> <tr> <td> <thead> <th> <tbody> <tt> <output>
  • Syntax highlight code surrounded by the {syntaxhighlighter SPEC}...{/syntaxhighlighter} tags, where SPEC is a Syntaxhighlighter options string or "class="OPTIONS" title="the title".
  • Lines and paragraphs break automatically.

More information about formatting options

By submitting this form, you accept the Mollom privacy policy.