Positive experience with new menu API for Drupal
Submitted by dave on Fri, 2008-06-06 19:34.
If, like me, you develop modules for Drupal, then you know the Pain. The Pain happens once or twice a year. The Pain has another name: upgrading your modules to the next version of Drupal.
Before the Pain, someone shows you a hot new feature - perhaps coolest thing in the world - and you think, "I want that." Ah yes, but what must you do to get it? There's a page on the web to answer that question. You know where to find it. The browser wheel spins... the answer is about to appear... and you hope the list is not long. You hope in vain. The Pain has begun.
For Drupal's latest release, that list of changes begins with "entirely new menu system." Those four words mean you're about to change every module you've ever written. Recently I've been doing just that. And despite the Pain I'm pleased to report this new menu api has nice features. To illustrate, here's a problem that has nagged me in the past and a solution using the new menu API.
The Problem
Some sites display user names in a non-standard way. For example, profile fields for First Name and Last Name, or a picture beside the name. Drupal has a theme layer, which is supposed to make this sort of thing completely configurable. The theme layer comes close but falls short on some pages. One page that's particularly difficult is the search results page, where you search for users by name. Take a look at http://drupal.org/search/user/drupal for example. Not exactly a thing of beauty, so I want to customize it.
That page is produced by the search API. For technical reasons, it's not using theme('username') to display those names. Further, its only searching the username, whereas I want my search to include profile fields and other information I may have about my users. So I'd like to make a complete new search page, to replace the default user search. There's a search API to do this, which is an important part of the solution. This allows me to add my own custom user search, but still I cannot hide the default one. So the crux of the problem is that if I enable search and allow users to view one another's profiles, I can't hide Drupal's user search page.
Menu API to the rescue
At least, I can't hide it in Drupal 5. In Drupal 6, the user search results page has the same limitations; however the menu API allows me to change who has permission to see it. This turns out to be quite simple to do.
In the new menu API, there's a data structure that lists every path the site provides. When I say 'path', I mean something similar to a URL, but not quite. Consider example.com/search/user/foo and example.com/search/user/bar. These are two distinct URLs, but to Drupal there is one path "search/user/..." corresponding to one function that serves both URLs.
Now back to the menu data structure. It's built in a fairly complex way. Some of its entries come from modules, and others from the database. Administrators can modify the database through Drupal's administration pages. Also, modules get a chance to modify the entire structure. The latter is the key new feature that allows me to hide the default search results page.
Without getting into too much detail, the path that corresponds to user search results is 'search/user/%menu_tail'. And the function which allows me to modify it is named hook_menu_alter. Here's how my module (called 'user_tweaks') changes things so that only administrators can see the user search results.
<?php
function user_tweaks_menu_alter(&$callbacks) {
// Allow only admins to search/user.
if ($callbacks['search/user/%menu_tail']) {
$callbacks['search/user/%menu_tail']['access callback'] =
'user_access';
$callbacks['search/user/%menu_tail']['access arguments'] =
array('administer users');
$callbacks['search/user/%menu_tail']['title'] = t('Users (admin)');
unset($callbacks['search/user/%menu_tail']['title callback']);
}
}
?>It's simple and fairly clear. The 'access callback' and 'access arguments' control who can view the page. The changes to 'title' and 'title callback' make it clear to an administrator they are seeing a page only administrators can see.
To make my solution complete, I need to implement my own user search which displays usernames just the way I want. This too is fairly easy, using hook_search. The following example started with a copy of hook_search from the user module. I've changed it to show the 'name_display' from my user_tweaks table. I've also made the query more efficient by avoiding a LOWER() in the WHERE clause. Further reading on both of these topics is available on Drupal.org.
<?php
/**
* Implementation of hook_search().
*/
function user_tweaks_search($op = 'search', $keys = NULL, $skip_access_check = FALSE) {
switch ($op) {
case 'name':
if ($skip_access_check || user_access('access user profiles')) {
return t('Users');
}
case 'search':
if (user_access('access user profiles')) {
$find = array();
// Replace wildcards with MySQL/PostgreSQL wildcards.
$keys = preg_replace('!\*+!', '%', $keys);
if (user_access('administer users')) {
$result =
pager_query("SELECT u.*, ut.name_display FROM {users} u \
LEFT JOIN {user_tweaks} ut ON ut.uid = u.uid \
WHERE ut.name_display_index \
LIKE LOWER(REPLACE('%%%s%%', ' ', '')) \
OR name LIKE LOWER('%%%s%%')",
15, 0, NULL, $keys, $keys);
}
else {
$result =
pager_query("SELECT u.*, ut.name_display \
FROM {users} u \
LEFT JOIN {user_tweaks} ut \
ON ut.uid = u.uid \
WHERE ut.name_display_index \
LIKE LOWER(REPLACE('%%%s%%', ' ', ''))",
15, 0, NULL, $keys);
}
while ($account = db_fetch_object($result)) {
// Show email to admins only
// TODO: use theme to render text
if (user_access('administer users')) {
$text = $account->name_display . ' (' . $account->mail . ')';
}
else
$text = $account->name_display;
$find[] = array('title' => $text,
'link' => url('user/'. $account->uid, array('absolute' => TRUE)));
}
return $find;
}
}
}
?>I'm still wrapping my head around the new features of the menu API and everything it can do. But the ease with which I solved this problem makes me happy these improvements made it into Drupal 6. Maybe, just maybe, the Pain is worth it (sometimes).


D6
Submitted by maartenvd@drupal.org on Thu, 2008-06-12 20:00.
I was going to ask what pushed you over the edge to D6, and then realized the answer is in your post. I'll be curious to hear how the move went; seems that you have enough modules to think about that it'd be non-trivial.
Me, back to thinking about image upload/mgmt modules.
login or register to post comments