Doing some review of D7 code today, detailing how the primary links are made and how they can be altered in modules or themes.
How Drupal 7 primary links are made : code trace
template_preprocess_page(&$variables)
Sets up the variables that are passed to the theme, generally in page.tpl.php. The primary_links variable is set up by calling...menu_main_menu()
...which is just a wrapper function for...menu_navigation_links($menu_name, $level = 0)
...which gets the tree of menu items at the specified depth by calling...menu_tree_page_data($menu_name, $max_depth = NULL, $only_active_trail = FALSE)
...which checks the current language, depth, etc. and prepares a query for...menu_build_tree($menu_name, $parameters)
...which mainly checks the access on the data returned from..._menu_build_tree($menu_name, $parameters)
...which gets the menu data directly from themenu_links
andmenu_router
tables in the database. These links are passed back all the way throughmenu_build_tree
andmenu_tree_page_data
, to...menu_navigation_links($menu_name, $level = 0)
...which takes the array of menu items that it got from the functions in 4, 5, and 6, and sets up a renderable array of $links that is returned to...template_preprocess_page(&$variables)
...and passed as the variable $main_menu to the tpl.php file.page.tpl.php (file)
The default page.tpl.php, as well as the preprocess function for core themes like Bartik, renders the $main_menu by calling...theme('links__system_main_menu', array('links' => $main_menu, ...))
...which passes links on to theme hook functions, along with a renderable array of variables. If there is an implementation of theme_links__system_main_menu, then that function will be called; if not, it will be the function for theme_links. It is always helpful to review the documentation of Drupal'stheme
function.
How to alter Drupal 7 primary links : some options
There are several options for changing or altering the main menu, and which one you want depends on what you want to accomplish and whether you are writing a module or a theme.
Register a theme function (module)
A module may register a theme function for theme_links__system_main_menu($variables). This is the approach taken by modules like responsive dropdown menus, which is intended to alter the main menu for any enabled theme. It accomplishes the task by implementing hook_theme() and registering a theme hook for links__system_main_menu, along with a function that will be called in order to theme the menu:
<?php
function responsive_dropdown_menus_theme($existing, $type, $theme, $path) {
return array(
// Override the main menu theme.
'links__system_main_menu' => array(
'variables' => array('inline' => FALSE),
'function' => 'responsive_dropdown_menus_main_menu',
),
);
}
?>
The function responsive_dropdown_menus_main_menu($vars)
then returns a renderable array for the main menu.
Override a theme function (theme)
A theme may override a the theme function by implementing {themename}_links__system_main_menu($variables). I couldn't quickly find an example of any theme that does this, but it is simple enough, at least in theory. In the theme's template.php file, you create a function to theme the links, which will be passed to your function in the $variables parameter. Here's a hypothetical implementation from a stackexchange question:
<?php
function MYTHEME_links__system_main_menu($variables) {
$html = "<div>\n";
$html .= " <ul>\n";
foreach ($variables['links'] as $link) {
$html .= "<li>" . l($link['title'], $link['path'], $link) . "</li>";
}
$html .= " </ul>\n";
$html .= "</div>\n";
return $html;
}
?>
Implement hook_preprocess_page to provide variables to the tpl.php files (theme)
A theme may implement hook_preprocess_page(&$vars) to override or create new variables for the page.tpl.php file. This is the approach I've decided to take at with my present theme project: I wanted to implement a drop-down menu in the theme, but despite much discussion Drupal still doesn't do this by default. Working from an example by James Morrish, I used a helper function to clean up the array of all links returned from menu_tree_page_data
:
<?php
function incubator_prograde_alpha_preprocess_page(&$vars) {
$menu = variable_get('menu_main_links_source', 'main-menu');
$vars['main_menu'] = incubator_prograde_clean_links(menu_tree_page_data($menu, 3));
}
function incubator_prograde_clean_links($links, $level = 1) {
$result = array();
foreach($links as $id => $item) {
if (!$item['link']['hidden']) {
$new_item = array(
'title' => '<span>' . check_plain($item['link']['link_title']) . '</span>',
'link_path' => $item['link']['link_path'],
'href' => $item['link']['href'],
'html' => TRUE,
);
$new_item += $item['link']['options'];
$new_item['attributes']['class'][] = "level-$level";
if (!empty($item['below'])) {
$new_item['below'] = incubator_prograde_clean_links($item['below'], $level + 1);
}
$result[] = $new_item;
}
}
return $result;
}
?>
Conclusion and Unresolved Issues
We've taken a look at how the main menu is determined in Drupal 7, and we've examined three methods of altering the data. Still missing from the final method is a means of determining the active trail, which does not happen with this code, although since Drupal 7.9 it can be set using menu_tree_set_path
.