Main menu

Drupal lets us define permissions using hook_perms. When writing a module, we grant users access to a page using the menu access argument. It accepts user permissions.

Users with the permissions can access the page, others don't.

This is a basic access scenario solely depending on the user access rights. Sometimes this isn't going to cut it and we need something more flexible.

Access depending on multiple factors

Instead of using a permission as access argument to control the access to a page, we use a custom access callback.

This access callback is a function that returns true or false to grant or deny access to a page. As a function it can take several arguments and variables into account when evaluating access.

True, when we're not declaring an access callback it isn't like the page doesn't have an access callback. Rather, it uses the default access callback 'user_access'.

Below we find a code example.

/**
 * Implements hook_menu().
 *
 * We want to create some admin pages and the display of contact information.
 */
function usertest_menu() {
  $items = array();

  $items['admin/config/people/usertest'] = array(
    'title' => 'Usertest',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('usertest_admin_settings'),
    'access arguments' => array('administer usertest'),
    'file' => 'usertest.admin.inc',
    'type' => MENU_NORMAL_ITEM,
  );

  $items['user/%user/contact-information'] = array(
    'title' => 'Contact information',
    'page callback' => 'usertest_display_user_contact_information',
    'page arguments' => array(1),
    'access callback' => '_usertest_access_user_contact_information',
    'access arguments' => array(1),
    'type' => MENU_LOCAL_TASK,
    'weight' => 5,
    'file' => 'usertest.pages.inc',
  );

  return $items;
}

The first page uses the default 'user_access' access callback. Access to the second page is controlled by a custom access callback '_usertest_access_user_contact_information'.

Different types of input to take into consideration

There are three different inputs that can be taken into consideration to grant or deny a user access:

  1. Data from the visitor that is viewing the page
  2. The page content
  3. The context when the page is viewed

Data from the visitor that is viewing the page

Access the data of the viewing user global $user. Use his permissions or user data as factors to grant or give permissions.

This one comes closest to the standard Drupal user_access. But now we can also take other user data into consideration, not only the permissions.

The page content

Pass an argument into the access callback. For instance, the node id of the node we're attempting to view, or the user id of the user account we're attempting to view.

In the above code example, the custom access callback takes one argument, the user id (of a user account).

It's passed through 'access arguments' => array(1).

We could then load that user (not the viewing user, but the user of the account). And take some data about the user into consideration.

The context when the page is viewed

Besides the user who is viewing a page, and the content of the page, we can also takes something unrelated to the two into consideration.

An example would be the current time. Depend on the time something is shown to a user.

A practical example

In the above code example, '_usertest_access_user_content' was used as an access callback. Below we see the function.

Each user can decide if his contact details may be shown to other users.

Depending on the user's preference, the existence of contact information and the visitor’s role, the user contact information is displayed to the visitor.

/**
 * User access callback
 *
 * We want people to see the contact information only when the user gives his permission.
 */
function _usertest_access_user_contact_information($account) {
  $privacy = $account->data['privacy'];
  $information = $account->data['contact-information'];
  global $user;

  // User administrators should always have access to contact information when available.
  if(user_access('administer users') && ($information != '')) {
    return TRUE;
  }
  // Users should always be able to see their own contact information when available.
  if(($user->uid == $account->uid) && ($information != '')) {
    return TRUE;
  }
  // Others will see contact information depending of the user account privacy settings and availability.
  elseif($information != '') {
    return $privacy;
  }
  else {
    return FALSE;
  }

}

2 comments

Default avatar
Sw3b
Thu, 16/06/2011 - 22:42

Nice post, Excllent exemple. Could it be possible to use this process for other thing like the node user create, etc. ?
Default avatar
Seatone
Tue, 05/07/2011 - 09:11

Nice post