1

I have installed the plugin Menu block and I am very happy with the result, but I'd like to change some classes in the structure.

This is what I get right now:

<div class="content">
    <ul class="menu">
        <li class="first last expanded">
            <a class="active" href="http://example.com">News</a>
            <ul class="menu">
                <li class="first last leaf">
                    <a class="active" href="http://example.com">Test</a>
                </li>
            </ul>
        </li>
    </ul>
</div>

..and this is what I want:

<div class="content">
    <ul id="nav-list">
        <li class="first last expanded has-submenu">
            <a class="active" href="http://example.com">News</a>
            <ul class="sub-menu">
                <li class="first last leaf">
                    <a class="active" href="http://example.com">Test</a>
                </li>
            </ul>
        </li>
    </ul>
</div>

So I basically want to..

  1. ..add/change/remove the <ul>'s class and id attributes (level specific, so not the same for the primary/secondary <ul>).
  2. ..add a has-submenu class to an <li> if it contains another submenu.

What I've tried:

As explained in Menu Block's documentation, a hook function hook_menu_block_tree_alter() can be implemented. To start 'somewhere, I tried implementing this in template.php without any success (but doesn't even get called):

function THEME_menu_block_tree_alter(&$tree, &$config) {
  foreach ($tree as $key => &$value) {
    if ($tree[$key]['link']['access'] && !$tree[$key]['link']['hidden']) {
      $items[] = $tree[$key];
    }
  }

  $num_items = count($items);
  foreach ($items as $i => &$data) {
    $class = array();

    if ($data['link']['has_children']) {
      $class[] = 'has-submenu';
    }
    $element['#attributes']['class'] = $class;
  }
}

Any help would be much appreciated as I don't really have deep understanding of how and where to implement this hook function. Also my impression is, that some more detailed documentation is missing for this specific hook function..

  • Welcome to Drupal Answers! Just to be sure, did you change THEME with the short name of the theme? If THEME is not the short name of the theme, that hook will never get invoked. – kiamlaluno Jan 7 '15 at 7:05
  • Hi @kiamlaluno :-) Yes I did so, but it wouldn't even do anything if adding a var_dump($example) or drupal_set_message("Example").. I decided to go the way @sareed explained in his answer below, but still..shouldn't this be working? And is template.php the place to put it or am I doing something wrong? – James Cazzetta Jan 7 '15 at 19:39
1

HOOK_menu_block_tree_alter(&$tree, &$config) is not a theme hook. You have to use it in a custom module and then it will work. So if you create a module called "mymodule, then put the following code in the myModule.module file, it should print the arguments:

/**
 * Alter the menu tree and its configuration before the tree is rendered.
 *
 * @param $tree
 *   An array containing the unrendered menu tree.
 * @param $config
 *   An array containing the configuration of the tree.
 */
function mymodule_menu_block_tree_alter(&$tree, &$config) {
  dsm($config);
  dsm($tree);
}

I'm not really sure if you can qualify it further with the menu block name--it didn't work when I tried that. You might have to just conditionally select the menu block you want on that page, if there's more than one.

  • If the hook is invoked using drupal_alter(), also a theme can implement it, since it is an alter hook. Differently from the other hooks, alter hooks can be implemented from themes too, in Drupal 7 and if the module uses drupal_alter() to invoke the hook. Differently, they could not be invoked. – kiamlaluno Dec 27 '18 at 18:49
1

Other than the regular clear cache I am not sure why that would not be called. I do not have much experience with that hook. However I am fairly certain theme_menu_link would work here as well. Take a look at the answer here. A test for $vars['element']['#below'] should allow you to accomplish what you want.

Note: If you have a small menu you can always add classes to menu items through the UI.

EDIT: To answer part 1 try something like this (taken from bootstrap_menu_link):

if ($element['#below']) {
  if ((!empty($element['#original_link']['depth'])) && ($element['#original_link']['depth'] == 1)) {
    // Add our own wrapper.
    unset($element['#below']['#theme_wrappers']);
    $sub_menu = '<ul class="dropdown-menu">' . drupal_render($element['#below']) . '</ul>';
    // Generate as standard dropdown.
    $element['#title'] .= ' <span class="caret"></span>';
    $element['#attributes']['class'][] = 'dropdown';
    $element['#localized_options']['html'] = TRUE;

    // Set dropdown trigger element to # to prevent inadvertant page loading
    // when a submenu link is clicked.
    $element['#localized_options']['attributes']['data-target'] = '#';
    $element['#localized_options']['attributes']['class'][] = 'dropdown-toggle';
    $element['#localized_options']['attributes']['data-toggle'] = 'dropdown';
  }
}
  • So I cleared cache, but wouldn't change the fact. Also trying to "drupal_set_message()" something or var_dump($tree)" will not work. I must be doing something wrong.. Could you give me some more help on how to do it using "theme_menu_link"? Thx anymway – James Cazzetta Jan 6 '15 at 23:50
  • You add the function to your template.php function YOUR_THEME_menu_link($vars) {}. I edited the answer to add the link (my bad I missed that first go around). The documentation should help a lot just pay attention to how the return string is set up. If it is wrong your menu can go away and cause other issues. – sareed Jan 7 '15 at 0:06
  • Thanks for the help! will look at this. I will leave this question open for a moment though, as this only covers one part of my question. – James Cazzetta Jan 7 '15 at 0:22
  • 1
    Sounds good, what parts are do you need clarification on. Perhaps add edits to your question to track progress. – sareed Jan 7 '15 at 0:30
  • So I managed to add classes to the <li> parts using theme_menu_link() (what is point 2 in my question). Concerning point 1 in my question, I cant figure out how to add/change/remove the <ul>'s class and id attributes (level specific, so not the same for the primary/secondary <ul>). – James Cazzetta Jan 7 '15 at 0:40
1

In the 7.x-2.x branch, that hook is invoked using the following code. (See menu_block.module.)

  // Allow alteration of the tree and config before we begin operations on it.
  drupal_alter('menu_block_tree', $tree, $config);

  // Localize the tree.
  if (module_exists('i18n_menu')) {
    $tree = i18n_menu_localize_tree($tree);
  }

Since drupal_alter() contains the following code, even a theme can implement that alter hook.

// Allow the theme to alter variables after the theme system has been
// initialized.
global $theme, $base_theme_info;
if (isset($theme)) {
  $theme_keys = array();
  foreach ($base_theme_info as $base) {
    $theme_keys[] = $base->name;
  }
  $theme_keys[] = $theme;
  foreach ($theme_keys as $theme_key) {
    $function = $theme_key . '_' . $hook;
    if (function_exists($function)) {
      $functions[$cid][] = $function;
    }
    if (isset($extra_types)) {
      foreach ($extra_types as $extra_type) {
        $function = $theme_key . '_' . $extra_type . '_alter';
        if (function_exists($function)) {
          $functions[$cid][] = $function;
        }
      }
    }
  }
}

The theme implementing the hook must be the theme used to render the page, or one of its parent themes. Differently, the alter hook would not be invoked.

The same is true for the 7.x-3.x branch of the module. (See its menu_block.module.)

If you edited an already enabled theme, or one of its parent themes to add the alter hook, then Drupal will not notice you added a new hook, since it saves in its cache the list of implemented hooks.
For Drupal to notice the new hook, you should visit the page listing all the modules, and install a module that you then uninstall, or disable a module you then re-enable. In this way, Drupal will clear the cache containing the list of the implemented hooks, and it will notice the new hook.

It could also be the alter hook has been implemented for the wrong theme, for example one that is never used for any page, or that is not the parent theme of any theme used for the page shown in your site.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.