开发者

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).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜