17

How can I add new conditions on top of existing conditions that determine whether a menu item is visible? These conditions should not be limited to permissions settings.

As an example use case (not necessarily the reason for this question): Suppose I have a content type of which users are only allowed to create one node. I have a menu item for adding that type of content. But if the user has already created a node of that content type, I want to hide the menu item. My first thought is to run a query to check for the existence of a node created by the current user that is of the specific content type. If one exists, hide the menu item.

I would think this type of functionality should go into hook_menu_alter() and add the required logic there. But I'm not sure how to do that without bypassing the existing checks, like checking if the user has permissions to create that type of content. Would I have to include that logic within my own condition? Or can I add to the existing access logic without overwriting it?


Edit: Some people seem focused on answering "how do I limit a user to creating one node of a content type". That is not the question here. The question is how to I add custom access conditions to a menu item.

5 Answers 5

11

What you need to do is to add your callback via hook_menu_alter(), and then inside your callback you simply do your logic then return the data via the original callback.

To be certain that you don't overwrite any other hook_menu_alter() changes, you should pass the previous callback to your callback via the access argument.

This is all theoretical, but the code should go something like this:

MYMODULE_menu_alter(&$items) {
  $items['menu']['access arguments'] = array_merge(array($items['menu']['access callback']), $item['menu']['access arguments']);
  $items['menu']['access callback'] = 'MYMODULE_access_callback';
}

MYMODULE_access_callback() {
  $args = func_get_args();

  // Do Stuff.
  if ($something == FALSE) {
    return FALSE;
  }

  $function = array_shift($args);
  return call_user_func_array($function, $args);
}
4
  • so if i assign a new access callback function, does that definitely overwrite the original callback?
    – Chaulky
    Commented Mar 2, 2011 at 23:44
  • Yes, you can only have one access callback per menu item, hence make certain that you pass back to the original callback. I have seen a module that does something like this, one of the permissions heavy modules, can't remember which one though.
    – Decipher
    Commented Mar 2, 2011 at 23:45
  • what is the array_shift on $args doing?
    – Chaulky
    Commented Mar 4, 2011 at 1:10
  • It pulls out the the first argument from the 'access arguments', which we made the old 'access callback'. So if the old callback was 'MYMODULE2_access_callback', that's what array_shift is returning. It also removes it from the array so that we only pass the arguments that the callback is expecting.
    – Decipher
    Commented Mar 4, 2011 at 2:33
1

In response to the comments above, the solution in D7 would be to use:

/**
 * Implements hook_node_access().
 */
function mymodule_node_access($node, $op, $account) {
  $type = is_string($node) ? $node : $node->type;

  if ($op == 'create' && $type == 'mynodetype' && db_query("SELECT 1 FROM {node} WHERE type = :type AND uid = :uid", array(':type' => $type, ':uid' => $account->uid))->fetchField()) {
    // If the user has already created a node of a specific type, they cannot
    // create any more.
    return NODE_ACCESS_DENY;
  }

  // Otherwise do not affect any node access.
  return NODE_ACCESS_IGNORE;
}
3
  • 1
    This doesn't seem to have anything to do with menu items. I'm not too familiar with D7 yet, but it looks like this is specific to node creation. The question is focused on menu items in general.
    – Chaulky
    Commented Mar 17, 2011 at 20:14
  • Oh i see... this is in response to my comment asking for more details about your D7 solution suggested in your answer that points to the Node Limit module. Still a bit off topic, but appreciated.
    – Chaulky
    Commented Mar 17, 2011 at 20:18
  • Because the visibility of the create mynodetype links is controlled by the node_access() function, which would invoke this hook in Drupal 7.
    – Dave Reid
    Commented Mar 17, 2011 at 21:24
1

You're looking for the Chain Menu Access API module.

Chain Menu Access API allows your module to chain its own menu access callback functions into other modules' menu router entries.

There is at least one example on Drupal Stack Exchange of how to use it.

-1

One option would be to create a new role that has create content permission for your content type. After a user creates a node of that type, take away that role and they wont be able to create any more.

-1

Perhaps you should try the Node Limit module.

From the project page:

The Node Limit module allows administrators to restrict the number of nodes of a specific type that roles or users may create. For example, if a site has an "Advertiser" role that can create "advertisement" nodes, then the node limit administrator can restrict all users in that role to a specific number of nodes. He may also restrict users on a per-user basis.

4
  • Limiting to one node is just an example use case for adding a custom access callback method. Also, Node Limit doesn't remove the menu item, it just prevents the user from adding another node of that content type.
    – Chaulky
    Commented Mar 4, 2011 at 1:11
  • That's true now that I look over the module description again. If this were on Drupal 7 it would actually be easy since you can use hook_node_access($node, 'create', $account) which would affect visibility of the create node type link itself.
    – Dave Reid
    Commented Mar 4, 2011 at 1:45
  • That's interesting. I do plan to move to D7 soon. would you mind writing it up in more detail and posting an answer?
    – Chaulky
    Commented Mar 13, 2011 at 16:31
  • D7 version of the answer posted.
    – Dave Reid
    Commented Mar 17, 2011 at 19:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.