I'm not sure how to use this module. Just think of the following use case:

I'd like to deny node view access to anonymous users depending on a custom node field, implementing hook_node_access. This works fine, but corresponding menu items (e.g. in book pages) aren't removed, giving a 403 access denied.

I think chain_menu_access might help me solve this issue, so I've tried the following code:

function mymodule_menu_alter (&$items) {
  chain_menu_access_chain($items['node/%node'], 'mymodule_access_check', array(1));
  chain_menu_access_chain($items['node/%node/view'], 'mymodule_access_check', array(1));
}

function mymodule_access_check ($node) {
  global $user;
  if ($user->uid > 0)
    return TRUE;
  $node = node_load ($node->nid);
  $public = field_get_items('node', $node, 'field_public');
  if ($public[0]['value'] != 1)
    return FALSE;
  return TRUE;
}

Looks fine to me, but doesn't work. Seems the access check function doesn't get called. So what am I doing wrong?

Thx for help,

Boris

CommentFileSizeAuthor
#7 screenshot.png68.22 KBdrubb

Comments

salvis’s picture

Leave out 'node/%node/view' — it inherits its access from its parent.

Did you clear the menu cache?

drubb’s picture

You're right, I forgot to clear the cache. Now the situation is the following;

- my custom access check function is called
- no need to use node_load, the node object is already fully populated
- access to node is denied depending on field_public value, no need to use hook_node_access anymore

But sadly, the corresponding menu item is still displayed, giving the user an "access denied" page.

Any way to remove menu items the user has'nt access to?

At the moment, this module implements just an alternative to hook_node_access, giving some more possibilities as it's not restricted to node paths.

salvis’s picture

At the moment, this module implements just an alternative to hook_node_access, giving some more possibilities as it's not restricted to node paths.

No, this module is completely unrelated to node access. It can be used to block access to node paths because it can block access to any path, but it's a completely different mechanism and effect.

Forum Access successfully uses CMA to hide the Forum menu item when a user does not have access to any forums. I don't know why it's not working in your case, but I suspect this is beyond the scope of CMA.

You mention book pages — if you're referring to the page-to-page links of the book pages, then this is definitely out of scope for CMA, because these are not menu items. They are artifacts of the book page content type, and you'll have to go to the core queue. If that's what you're after, then you may actually have better luck by implementing hook_node_access(), because the module implementing the book page type is more likely to check node access than menu access.

P.S. Please give this thread a title that would let you find it the next time you're in this situation. The current title is too broad to be useful.

drubb’s picture

Title: How should this module be used? » Use of this module to remove unwanted menu items?

I've just had a quick glance at the forum_access module. Maybe the removal of corresponding menu items does work there because the module is implementing node grants?

Well, this is surely a core issue: there should be a possibility to automatically remove menu items and links to paths the user hasn't access to. As far as I've seen, this ONLY works using the (role-based) permission system, or implementing the node grant system.

Think I'll setup a clean minimal D7 install to do some tests regarding this problem, using node paths and none-node paths, menu items and standard links, and implementing different ways to block access. If you're interested, I'll post the results in some days.

Thanks so far,
Boris

P.S. I've changed the issue title. Better?

salvis’s picture

No, the 'Forum' menu item does NOT go away based on node access. Removing the 'Forum' menu item is the very reason for writing CMA in the first place.

As far as I've seen, this ONLY works using the (role-based) permission system [...]

That's menu router access, exactly what CMA is using. Whether the decision is taken based on roles (i.e. calling user_access()) or anything else (like your chained function) is completely irrelevant, so please don't call this "role-based".

[...] or implementing the node grant system.

In the OP you wrote node access wasn't working (which I agree with), and now you write it's working? I'm confused.

Let's get this straight: node access has absolutely no effect on menu paths. It aborts generation of node pages by calling drupal_access_denied() AFTER the menu system has already granted access. There's no way for the menu system to know what node access will do later on in the process. For the user the end result looks the same, but internally it's completely different.

automatically remove menu items and links to paths the user hasn't access to.

Menu items and links are two completely different things. I think you're mixing up the two. CMA is intended to manage menu items, but there is no mechanism to manage links.

I'm still not completely sure what you want to do. Please post a screenshot and mark in red the things you want to get rid of, so that we both know what we're talking about.

Yes, the new title is better, and I think we'll find that CMA effectively does exactly that.

To investigate this, install Devel module, implement hook_init() and dpm(menu_get_item());. The 'access' key shows you whether the menu system has decided to grant access or not. This is precisely where CMA intervenes, and I believe that it not only 403's pages (i.e. if you see FALSE you'll get the 403 page), but it also removes the corresponding menu item. If you see TRUE, your user has access to the path and there's no reason for the menu system to remove the menu item, even if some other component may decide later on to deny access. Links are never affected by any of this.

drubb’s picture

In the OP you wrote node access wasn't working (which I agree with), and now you write it's working? I'm confused.

- It doesn't work using hook_node_access() (corresponding menu items are not removed)
- It works by implementing hook_node_access_records / hook_node_grants (corresponding menu items are removed)

I agree it can't work with non-menu links, because the generating function (e.g. l() or the book module) would have to check accessibilty, that's not a menu problem. So let's concentrate on real menu items.

I'll do a test setup and post some screenshots!

drubb’s picture

StatusFileSize
new68.22 KB

I did some further investigations now, and I think the described problem is a core issue, not your module's fault. I've set up a site with ten example nodes, all with menu links, and restricted access to half of them using hook_node_access. This works fine, but the corresponding menu items still show up, despite of the value in menu item's 'access'. Have a look at the attached screenshot.

Using the chain_menu_access module, it's possible to change menu access further on, but items aren't removed, too. Look at the following example code:

<?php
/**
 * Implements hook_init().
 */
function d7a_init() {
  dpm(menu_get_item());
}

/**
 * Implements hook_node_access().
 */
function d7a_node_access($node, $op, $account) {
  if ($account->uid > 0)
    return NODE_ACCESS_IGNORE;
  if ($op != 'view')
    return NODE_ACCESS_IGNORE;
  if ($node->nid % 2)
    return NODE_ACCESS_IGNORE;
  return NODE_ACCESS_DENY;
}

/**
 * Implements hook_menu_alter().
 */

function d7a_menu_alter (&$items) {
  chain_menu_access_chain($items['node/%node'], 'd7a_access_check', array(1));
}

function d7a_access_check ($node) {
  return FALSE;
}

Access is now denied for ALL items, but the menu items aren't removed. So I think this module just does what it's been designed for, namely chaining menu access functions. And this works fine.

Guess I have to find another way to remove inaccessable menu items, maybe by filing a core issue.

Thx for your patience!

drubb’s picture

Here's a quick workaround for this problem: it's possible to remove menu items the user hasn't access to using a little theme function in template.php:

function mytheme_menu_link(array $variables) {
  $path = $variables['element']['#href'];
  $item = menu_get_item($path);
  if ($item['access'] == TRUE)
    return theme_menu_link ($variables);
}

This has nothing to do with chain menu access api, just for interest. I've made this a core issue: http://drupal.org/node/1277596

salvis’s picture

Thank you for the follow-up.

I just had an idea; have you tried chaining to 'node/2'? I'm not exactly sure how it works internally, but if you create a menu item for your node, then I'd guess that core creates a menu router item for that nid and it may not look at 'node/%' when it sets up the menu.

salvis’s picture

Status: Active » Closed (fixed)

Feel free to reopen if necessary.