Developers are all familiar with the default behavior of the drupal menu systems "local tasks" (aka tabs). These appear throughout most Drupal sites, primarily in the administration area, but also on other pages like the user profile.
Generally, developers are pretty good about creating logical local tasks, meaning only those menu items which logically live under another menu item (like view, edit, revisions, workflow, etc... live under the node/% menu item).
But sometimes, these tabs either don't really make sense as tabs or you simply want to have the flexibility of working with the items as "normal menu items", or those menu items which appear under admin/build/menu.
I recently wanted to move some of the tabs on the user profile page (user/UID) into the main menu so that I could include them as blocks.
For some reason, developers think the user profile page is a great place to put tabs for user related pages such as friendslist, tracker, bookmarks, notifications and so on. But these types of items are less a part of the user's account information than they are resources for specific users. Personally, I would not think to look at my account information on a site to find stuff like favorites or buddies. I'd expect those items to be presented somewhere much more obvious like a navigation block.
Initially, this may seem like a trivial task. My first thought was to simply use hook_menu_alter() and change the 'type' value of the menu item from MENU_LOCAL_TASK to MENU_NORMAL_ITEM. However, for reasons I don't understand well enough to explain in detail, this does not work.
In order to achieve the desired result, you must change the path of the menu item and incorporate the '%user_uid_optional' argument, replacing the default '%user' argument.
All very confusing, I know. Let's look at an example.
The notifications module (which provides notification on changes to subscribed to content) uses the user profile page rather heavily. I don't want its links there, I want them in the sidebar where users can always see them.
<?php
/**
* Implementation of hook_menu_alter().
*/
function MODULENAME_menu_alter(&$callbacks) {
// NOTIFICATIONS MODULE
$callbacks['notifications/%user_uid_optional'] = $callbacks['user/%user/notifications'];
$callbacks['notifications/%user_uid_optional']['type'] = MENU_NORMAL_ITEM;
unset($callbacks['user/%user/notifications']);
<SNIP>
}
?>
So I have moved the notifications menu into my own menu, changed the type, used %user_uid_optional instead of %user, and unset the original menu item.
This works fine except for the fact that you'll lose all of the other menu items under user/%user/notifications! You need to account for all menu items in the hierarchy to properly reproduce the tabs in the main menu system, so we add the following:
<?php
$callbacks['notifications/%user_uid_optional/thread'] = $callbacks['user/%user/notifications/thread'];
unset($callbacks['user/%user/notifications/thread']);
$callbacks['notifications/%user_uid_optional/nodetype'] = $callbacks['user/%user/notifications/nodetype'];
unset($callbacks['user/%user/notifications/nodetype']);
$callbacks['notifications/%user_uid_optional/author'] = $callbacks['user/%user/notifications/author'];
unset($callbacks['user/%user/notifications/author']);
?>
And of course, we don't want this code executing at all if our module is not enabled, so you'd want to wrap the whole thing in:
<?php
if (module_exists('notifications')) {
<SNIP>
}
?>
Keep in mind that not all modules implement menu items using hook_menu(). It's becoming more and more common for developers to rely on the views module to generate menu items, and this is a wise choice. Menus generated using views (ala bookmark module) can be modified to get the desired result without any custom code.