hook_menu_alter() to override a LOCAL_TASK with a MENU_NORMAL_ITEM
I want to override a tab that shows up on the user profile, and make it a menu item in primary links instead.
For example, I'm using the Invite module which defines some menu items, including...
function invite_menu() {
// bunch of menu items...
// User profile tabs
$items['user/%user/invites'] = array(
'title' => 'Invitations',
'page callback' => 'invite_user_overview',
'access callback' => 'invite_user_access',
'access arguments' => array('track invitations', 1),
'type' => MENU_LOCAL_TASK,
'file' => 'invite_admin.inc',
);
}
Since I want to override that MENU_LOCAL_TASK, I define a hook_menu_alter() as follows, setting the item to be a MENU_NORMAL_ITEM in primary links instead.
function mymod_menu_alter(&$items) {
if (!empty($items['user/%user/invites'])) {
$items['user/%user/invites']['title'] = 'My Override';
$items['user/%user/invites']['type'] = MENU_NORMAL_ITEM;
$items['user/%user/invites']['menu_name'] = 'primary-links';
}
}
All good so far... I rebuild the menu_router to see the change, and the item does not appear in primary links.
Stepping through the code, I've discovered that after rebuilding the menu_router, my new menu item gets run through menu_link_save() (menu.inc). My menu item comes into this function with primary links set as it's "menu_name". But that gets overwritten to the "navigation" menu in the following way.
function menu_link_save(&$item) {
// ...
// do some stuff
// ...
// -----------------------------------------------------------
// THIS FAILS
// -----------------------------------------------------------
if (isset($item['plid'])) {
$parent = db_fetch_array(db_query("SELECT * FROM {menu_links} WHERE mlid = %d", $item['plid']));
}
My item has no plid. If it did, we'd be gold later on...
else {
// Find the parent - it must be unique.
$parent_path = $item['link_path'];
$where = "WHERE link_path = '%s'";
// Only links derived from router items should have module == 'system', and
// we want to find the parent even if it's in a different menu.
// -----------------------------------------------------------
// THIS PASSES, SO ...
// -----------------------------------------------------------
if ($item['module'] == 'system') {
$where .= " AND module = '%s'";
$arg2 = 'system';
}
else {
// If not derived from a router item, we respect the specified menu name.
$where .= " AND menu_name = '%s'";
$arg2 = $item['menu_name'];
}
do {
// -----------------------------------------------------------
// WE FIND THE PARENT ("user/%")
// -----------------------------------------------------------
$parent = FALSE;
$parent_path = substr($parent_path, 0, strrpos($parent_path, '/'));
$result = db_query("SELECT COUNT(*) FROM {menu_links} ". $where, $parent_path, $arg2);
// Only valid if we get a unique result.
if (db_result($result) == 1) {
$parent = db_fetch_array(db_query("SELECT * FROM {menu_links} ". $where, $parent_path, $arg2));
}
} while ($parent === FALSE && $parent_path);
}
So, now we've got a parent, and it's 'user/%', whose "menu_开发者_如何学JAVAname" is "navigation". Okay, but that's no good for my menu item who wants to live in primary links. What happens next is the overwriting...
if ($parent !== FALSE) {
$item['menu_name'] = $parent['menu_name'];
}
My question is, how do I accomplish this? From what I can gather, I need a plid, or something that creates a plid. Seems like this shouldn't be super hard. What am I missing?
As you said: "I need a plid, or something that creates a plid" - So if you want your item to appear at the top level of the primary-links menu, you could try to add a plid of 0 (== no parent == top level entry) in your hook_menu_alter
entry:
function mymod_menu_alter(&$items) {
if (!empty($items['user/%user/invites'])) {
$items['user/%user/invites']['title'] = 'My Override';
$items['user/%user/invites']['type'] = MENU_NORMAL_ITEM;
$items['user/%user/invites']['menu_name'] = 'primary-links';
$items['user/%user/invites']['plid'] = 0;
}
}
This might do the trick. If you want your item to appear under an existing item of the primary-links menu, you'd obviously need to get the mlid of that existing item fist.
NOTE/DISCLAIMER: Explicitly setting a plid like that is not documented for hook_menu()
, and the menu system does quite some data massaging on the items array before menu_link_save()
gets called, so:
- This might not work in the first place, due to the plid getting lost on the way.
- Even if this works, it might not be reliable and/or have unintended side effects.
So you might want to consider the alternative of creating your desired primary-links entry not by means of hook_menu_alter
, but separately using menu_link_maintain()
(should allow wildcard paths).
精彩评论