Access control
Note that some aspects of access control have been changed in Drupal 6.2. This handbook pages reflects these API changes (more).
The access callback and the access arguments decide whether the user has access to a given menu entry or not.
<?php
$items['admin/user/roles'] = array(
'title' => 'Roles',
'description' => 'List, edit, or add user roles.',
'access callback' => 'user_access',
'access arguments' => array('administer access control'),
);
?>The menu system will call the function user_access with the arguments administer access control.
If your module defines access arguments but does not define an access callback, then the menu system defaults to using user_access as the access callback. In other words:
<?php
$items['admin/user/roles'] = array(
'title' => 'Roles',
'description' => 'List, edit, or add user roles.',
'access arguments' => array('administer access control'),
);
?>is equivalent to:
<?php
$items['admin/user/roles'] = array(
'title' => 'Roles',
'description' => 'List, edit, or add user roles.',
'access callback' => 'user_access', // menu system adds this callback automatically
'access arguments' => array('administer access control'),
);
?>From Drupal 6.2 on, access callback and access arguments are no longer inherited from parent items. This API change has taken place to ensure every single menu item is correctly secured by access-control. This means to you that:
- If you define
access arguments, then you can be completely positive thatuser_accesswill be the defaultaccess callback. -
If you want to use a custom
access callbackinstead, you need to define it for every single menu item.Note: Of course you only need to define it for those menu items which you want to use it. The statement is emphasising that the callback is not inherited. If you specify a callback for some menu items but not others, the others will always use user_access.
-
You need to define
access argumentsfor every single menu item.Note: If you don't define access arguments then they default to the empty array. If you have defined a custom access callback that takes no arguments (like the printer_friendly_access example above), then this shouldn't pose a problem (so this is the one situation where it actually makes sense *not* to define access arguments). Again, the statement above is emphasising that access arguments are not inherited in any way.
- Default local tasks being the exception to all of this of course.
The only exception to this is for items of type MENU_DEFAULT_LOCAL_TASK: They still inherit both access callback and access arguments from their parent item.
<?php
$items['user'] = array(
'title' => 'My account',
'page callback' => 'user_view',
'page arguments' => array(1),
'access callback' => 'user_view_access',
'access arguments' => array(1),
);
$items['user/%user'] = array(
'title' => 'View',
'access callback' => 'user_view_access',
'access arguments' => array(1),
);
$items['user/%user/view'] = array(
'title' => 'View',
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['user/%user/edit'] = array(
'title' => 'Edit',
'page callback' => 'user_edit',
'access arguments' => array('administer users'),
'type' => MENU_LOCAL_TASK,
);
$items['user/%user/delete'] = array(
'title' => 'Delete',
'page callback' => 'user_edit',
'access arguments' => array('administer users'),
'type' => MENU_CALLBACK,
);
?>'user/%user'doesn't inheritaccess callbackoraccess argumentsfrom its parent item. As the callback always defaults touser_access, we generally don't need to define it explicitly. But as we want to use a custom callback instead, we need to define bothaccess callbackandaccess argumentsexplicitly.'user/%user/view'inherits bothaccess callbackandaccess argumentsfrom its parent item, as it is an item of the type MENU_DEFAULT_LOCAL_TASK.'user/%user/edit'and'user/%user/delete'don't inheritaccess callbackoraccess argumentsfrom their parent item. As the callback always defaults touser_access, it doesn't need to be explicitly defined. What we need to define explicitly isaccess arguments.
We support 'access callback' => TRUE (meaning all users and anonymous are allowed access) (and 'access callback' => FALSE for no access by any user).
Often you will find no callback that suits your needs. In this case, you should write one:
<?php
function printer_friendly_access()
return user_access('access content') && user_access('see printer-friendly version');
}
?>Of course, the callback can be much more complex:
<?php
function user_view_access($account) {
return $account && $account->uid &&
(
// Always let users view their own profile.
($GLOBALS['user']->uid == $account->uid) ||
// Administrators can view all accounts.
user_access('administer users') ||
// The user is not blocked and logged in at least once.
($account->access && $account->status && user_access('access user profiles'))
);
}
...
$items['user/%user_current'] = array(
'access callback' => 'user_view_access',
'access arguments' => array(1),
);
?>Important note: the value for the 'access callback' must always be a string which is the the name of the function - never a function call. It may also be assigned the value TRUE or FALSE if the page is always (or never) accessible.

Numbers used in arguments are passed as path component
Note that using an argument which is a number results in the appropriate path component getting passed to the access, title, or page callback function (i.e. if the argument is
array(2)the function will be called with the value ofarg(2)as its argument). If you want to pass an actual number, you'll need to hide it in a string.For example, this will not work, because
$og_nidis an integer:<?phpforeach (og_all_groups_options() as $og_nid => $og_name) {
$items['my_maps/'. $og_nid] = array(
'type' => MENU_NORMAL_ITEM,
'title' => '!group',
'title arguments' => array('!group' => $og_name),
'page callback' => 'user_menu_do',
'page arguments' => array($og_nid, 'view'),
'access callback' => 'user_menu_access_custom',
'access arguments' => array('group_member', $og_nid),
);
}
?>
Instead, you would have to pass a string:
<?phpforeach (og_all_groups_options() as $og_nid => $og_name) {
$items['my_maps/'. $og_nid] = array(
'type' => MENU_NORMAL_ITEM,
'title' => '!group',
'title arguments' => array('!group' => $og_name),
'page callback' => 'user_menu_do',
'page arguments' => array("nid$og_nid", 'view'),
'access callback' => 'user_menu_access_custom',
'access arguments' => array('group_member', "nid$og_nid"),
);
}
?>
The access and page callbacks then would need to strip off the text which was added before the nid:
$og_nid = substr($og_nid,3)openflows.com
it was not difficult to write
edit : i remove my comment, i understand now how this new writing avoid me to write boring lines of code before an menu item...