In an earlier article, I tried to argue, somewhat inconvincingly, that theming menu items is less than ideal. I think my example was not well chosen. Fortunately, real life furnished me with a perfect example, during the course of my work.
A client required to associate specific icons with menu items, by specifying a custom class for specific menu items. A legitimate request that has been around for years on desktop menu systems. How to implement this on Drupal? Not so simple using the current menu theming setup. Here's the problem:
The crucial failure of this function is to call theme('menu_item') with the rendered link instead of the original menu item object. This effectively prevents anyone from simply overriding this theme function to add classes or attributes based on the item's metadata. IMO, menu_tree_output needs to be rewritten.
But I had to deliver that functionality, so here's how I did it (without PECL runkit this time):
<?php
// @file mymodule.module
/**
* Implementation of hook_form_FORMID_alter() for menu_edit_item.
*/
function mymodule_form_menu_edit_item_alter(&$form, &$form_state) {
$form['menu']['classes'] = array(
'#type' => 'textfield',
'#title' => t('Extra classes'),
'#description' => t('Add extra classes for this menu item that are relevant for your theme.'),
'#default_value' => variable_get('menu_item_extra_classes_' . $form['menu']['mlid']['#value'], ''),
);
$form['#submit'][] = 'mymodule_menu_edit_item_submit';
}
function mymodule_menu_edit_item_submit($form, &$form_state) {
variable_set('menu_item_extra_classes_' . $form_state['values']['menu']['mlid'], $form_state['values']['menu']['classes']);
}
?>theme('menu_item') and theme('menu_item_link') to enable the first to receive the menu item object from the second. This is done using a global variable.<?php
// @file mymodule.module
/**
* Implementation of hook_theme_registry_alter().
*/
function mymodule_theme_registry_alter(&$theme_registry) {
$theme_registry['menu_item_link']['function'] = 'mymodule_menu_item_link';
$theme_registry['menu_item']['function'] = 'mymodule_menu_item';
}
function mymodule_menu_item_link($link) {
$GLOBALS['mymodule']['link'][] = $link; // push the link object
return theme_menu_item_link($link);
}
function mymodule_menu_item($link, $has_children, $menu = '', $in_active_trail = FALSE, $extra_class = NULL) {
$link_object = array_pop($GLOBALS['mymodule']['link']); // pop the topmost link object
$class[] = ($menu ? 'expanded' : ($has_children ? 'collapsed' : 'leaf'));
if (!empty($extra_class)) {
$class[] = $extra_class;
}
if ($in_active_trail) {
$class[] = 'active-trail';
}
$class[] = variable_get('menu_item_extra_classes_' . $link_object['mlid'], 0);
$attributes['class'] = implode(' ', array_filter($class));
$attributes['id'] = 'menu-item-' . $link_object['mlid'];
return '<li'. drupal_attributes($attributes) .' ">'. $link . $menu ."</li>\n";
}
?>Now the site builder/themer is free to specify any number of classes for any menu item, and to design their CSS accordingly!
Comments
There's still time to get bug
There's still time to get bug fixes into D7 ;)
Menu Attributes module
The Menu Attributes module works great for this sort of thing as well.
http://drupal.org/project/menu_attributes
-mike
Thanks for the link. I have
Thanks for the link. I have to check out hook_menu_link_alter which is the core of your module. It might save me from doing the
$GLOBALSacrobatics :-)Oh, menu attributes module is
Oh, menu attributes module is just great, thanks.
Thanks
The menu attributes function is something I've been trying to get my head round for days and now I find this!! Wonderful.
I know!
Been working with these functions all day, trying to get multi-column dropdown menus. It would have been easy if I could override menu_tree_output (in the theme) but this is impossible. I had to count the number of list items in a
<
ul> but by the time it gets to theme_menu_tree submenus are included in the variable, and the number of columns has to be determined by counting direct childs only. Now I have to resort to jQuery to style the menus.
could be helpful?
I have a small module that does something similar: http://drupal.org/project/menu_css_names
In my case, I was wanting to do highlight particular menu items, but also do css sprtes if needed (although there are modules that handle that more completely). Just thought I'd mention it!