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:
- Data from the visitor that is viewing the page
- The page content
- 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
Nice post
thanks