1

Following this tutorial I'm trying to restrict viewable user pages (and other tabs created in views). In particular, users belonging to role coordinator need to view and edit only pages of users associated with them via en entity reference field in core profile, and see an access denied page for others. I've already queried the db to get all the users id associated with current user and put it in a session var $_SESSION["arUidCoordinated"] that I use in other hooks, but I can't make it work with hook_menu_alter. Here's the code:

function mymodule_perm() {
  return array('my permission');
}

function mymodule_menu_alter(&$items) { 

    $items['user/%user_category'] = array(
        'title' => 'Edit',
        'page callback' => 'user_edit',
        'page arguments' => array(1),
        'access callback' => 'my_module_validate',
        'access arguments' => array(1),
        'type' => MENU_LOCAL_TASK,
        'load arguments' => array('%map', '%index'),
        'file' => 'user.pages.inc',
      );

}

function mymodule_validate($account){

    global $user;

    $uid=arg(1);
    if (in_array("coordinator", $user->roles) AND in_array($uid, $_SESSION["arUidCoordinated"])){

        return true;

    } else {

     return false;

    }   

}

I followed this post toot, but I can't make it work, I get "access denied" on every user/% page, or failed opening required user.pages.inc and I don't understand where I go wrong...

4
  • what version of drupal are you attempting this on? the reason I ask is the first post referenced give a drupal 6 answer while the second gives a drupal 7 one.
    – Jimajamma
    Dec 2, 2013 at 1:49
  • Menus are cached, in a way that does not respect roles, so using hook_menu_alter to control access requires care. Care to only put logic in 'access callback'. Now, in your code you have 'access callback' => 'my_module_validate' but the function is named mymodule_validate - was it just a typo while obfuscating name to paste it here? Also, please don't just say I can't make it work - it's useless. What happens? What have you expected? What's the difference? What error messages are on screen, watchdog, error.log?
    – Mołot
    Dec 2, 2013 at 7:40
  • Sorry, I should have been more precise in my explanation. @Jimajamma: I'm using D7 so probably that's why I can't make it work. Mołot is right for my typo, but that's not the case. I can't make it work in sense that I always get "access denied" on every user/% page, even when I log-in as administrator, and get "failed opening required user.pages.inc" when I login as a coordinator. So, any suggestion, or tutorial or page documentation I could read to improve my knowledge of user access in D7 to implement this in the right way and achieve my goal? Thank you for you patience.
    – Cheope
    Dec 2, 2013 at 13:42
  • Now I'll give a new try following this: drupal.org/node/483324
    – Cheope
    Dec 2, 2013 at 14:31

1 Answer 1

3

All the pieces of the original user menu items are defined in modules/user/user.module via the User Module's hook_menu(). The key ones here are (IMHO) user/%user for viewing the user page, and user/%user/edit for editing it, eg pages like user/1 to look at uid 1's user page, and user/1/edit to edit that same page.

So, from a logic standpoint, I am going to assume you want this:

If a user wants to look at or edit his or her own page, allow them to do so. Furthermore, if another user has appropriate permissions, allow them to look at and/or edit these pages as well. But no one else should be able to look at any other user's pages (let alone edit them).

Ok, so how can we do this. Let's first look at the original view the user page menu item:

$items['user/%user'] = array(
  'title' => 'My account',
  'title callback' => 'user_page_title',
  'title arguments' => array(1),
  'page callback' => 'user_view_page',
  'page arguments' => array(1),
  'access callback' => 'user_view_access',
  'access arguments' => array(1),
  // By assigning a different menu name, this item (and all registered child
  // paths) are no longer considered as children of 'user'. When accessing the
  // user account pages, the preferred menu link that is used to build the
  // active trail (breadcrumb) will be found in this menu (unless there is
  // more specific link), so the link to 'user' will not be in the breadcrumb.
  'menu_name' => 'navigation',
);

Here we would want to change the 'access callback' to include further logic, so the menu alter would only have to change that, nothing else, eg, something along the lines of this:

function YOURMODULE_menu_alter(&$items) {

  $items['user/%user']['access callback']='YOURMODULE_user_access';

}

and then you could write something like this:

function YOURMODULE_user_access($account) {

  if ($account->uid) { // so we don't try to look at an anonymous user

    $account = user_load($account->uid); // load the full user object (ADDITION:  was user_load($uid) in original post, oops)

    if ($GLOBALS['user']->uid == 1) { // admin super user
      return TRUE;
    }

    if ($GLOBALS['user']->uid == $account->uid) {
      return TRUE; // same user so allow (we will assume blocked users aren't logged in)
    }

    if ( /* whatever logic you need to allow coordinators to access the user is TRUE */ ) {
      return TRUE; // or return $account->status; if you want to take blocked accounts into effect
    }

  }

  return FALSE;

}

So, barring any brain freezes or typos on my part, the above should work for viewing, but I did just type it in freehand, so buyer beware. But, now, let's look at the original user edit menu item:

$items['user/%user/edit'] = array(
  'title' => 'Edit',
  'page callback' => 'drupal_get_form',
  'page arguments' => array('user_profile_form', 1),
  'access callback' => 'user_edit_access',
  'access arguments' => array(1),
  'type' => MENU_LOCAL_TASK,
  'file' => 'user.pages.inc',
);

And once again, all you want or need to do is change the 'access callback', eg, your hook_menu_alter() is now...

function YOURMODULE_menu_alter(&$items) {

  $items['user/%user']['access callback']='YOURMODULE_user_access';
  $items['user/%user/edit']['access callback']='YOURMODULE_user_access';

}

and yes, since the logic is the same, you can use the same function all over again.

Caveats: We are totally ignoring Drupal's built in permissions, eg, user_access('access user profiles') and user_access('administer users') in the above.

Also, if you find after doing this you can't load user.pages.inc, Drupal is most probably looking for it in your module's directory instead of the user module's where it sits, so set 'file' => drupal_get_path('module', 'user') . '/user.pages.inc' and you should be set.

Anyways hope this stream of consciousness helps out!

3
  • Thank you very much!!! Everything in much clearer now!! Thanks for your detailed explanations. You got the problem. In my first attempts I understood that I should have changed just things needed in hook_menu_alter, but not that I should have written my access callback function in order to catch every user case... I was still adding things to... nothing! Probably I can use the same logic to hide views-generated tabs on user pages? Thanx again. You saved my nights!!
    – Cheope
    Dec 2, 2013 at 16:05
  • quite welcome. I had a typo in my code, ($uid vs the correct $account->uid), now changed, but hope all is working for you.
    – Jimajamma
    Dec 3, 2013 at 0:40
  • Yes, I know, already found and corrected at the first glance. I carefully followed your instructions and now everything works perfectly! Thanks again.
    – Cheope
    Dec 3, 2013 at 0:53

Your Answer

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

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