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.
$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:
$items['admin/user/roles'] = array(
'title' => 'Roles',
'description' => 'List, edit, or add user roles.',
'access arguments' => array('administer access control'),
);
is equivalent to:
$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_access
will be the defaultaccess callback
. -
If you want to use a custom
access callback
instead, 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 arguments
for 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.
$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 callback
oraccess arguments
from 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 callback
andaccess arguments
explicitly.'user/%user/view'
inherits bothaccess callback
andaccess arguments
from 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 callback
oraccess arguments
from 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:
function printer_friendly_access() {
return user_access('access content') && user_access('see printer-friendly version');
}
Of course, the callback can be much more complex:
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.
Comments
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_nid
is an integer:Instead, you would have to pass a string:
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)
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...
Can't return TRUE from user_view_access function
I found your post very helpful, but I ran across a problem when I'm trying to return a value of true. Here's a case where I tried to limit the access of user/1 location to only the admin account.
Returning FALSE works fine. When the check_access returns TRUE i get this error:
Fatal error: require_once() [function.require]: Failed opening required '/user.pages.inc' (include_path='.:/home/httpd/inc:/home/httpd/inc/smarty:/usr/share/pear') in /home/devel/includes/menu.inc on line 346
Page execution time was 236.84 ms.
I'm lost on this, any insight would be great.
try including the
try including the user.pages.inc in the check access / module init function
--------------------------------------------------------------------------------------------------------
if you can use drupal why use others?
VicTheme.com
For anyone coming across this
For anyone coming across this in future, its a problem with menu items trying to get files from the locations where they are defined, not actually related to this TRUE/FALSE issue mentioned.
The solution is to add the following 'file path' key to the array, either in hook_menu or hook_menu_alter:
xtfer.com
platform.sh
user access
I've given user administration to a role on my site, and i found that they can view and edit user 1 information, so I tried to set user access using hook_menu_alter so that only user 1 can access user/1:
but when i run this I get access denied even when signed in as user 1. Any one have any ideas on why this is happening?
admin (user)
Access denied
You are not authorized to access this page.
You'll note that I use 'user_access' rather than 'access callback' because i get an access denied rather than a blank page/
Thanks.
Re: user access
I had this problem too
your access call back has to be in the .module file and not in the registry or pages include or the hook won't find your custom access callback and always give false
this is (afaik) because your include pages are not exposed to hook_menu or hook_menu_alter
a fix for this might be to define your include files in the .info file
i hope this fixes your problem
edit: Oh and make sure you flushed the menu cache
access final solution
Final Solution: I found using the following worked, it's all about getting the right arguments in the menu items (took forever to figure this out). pay close attention to %user_uid_optional, and %user_category. If you just use %user in these places, the page becomes a white screen of death.
For those that don't want to deal with this, I found a module that does most of this for you:
http://drupal.org/project/administerusersbyrole
Thanks to all who tried to help.