3

I'm trying to implement an access callback per role for a specific menu item, but other roles can view that menu item also. Any tip is welcome.

/**
 * Implements hook_menu();
 */
function MODULENAME_menu() {

  global $user;

  $items['admin/backoffice/production/taxonomie'] = array(
    'title' => 'Taxonomie',
    'type' => MENU_LOCAL_TASK,
    'weight' => 0,
    'page callback' => 'MODULENAME_backoff_taxonomy',
    'access callback' => 'MODULENAME_user_has_role',   
    'access arguments' =>  array($user, array('éditeur taxonomie', 'administrator')),
    );

  return $items;
}

function MODULENAME_backoff_taxonomy(){
  module_load_include('inc', 'taxonomy', 'taxonomy.admin');
  $form = drupal_get_form('taxonomy_overview_vocabularies');
  drupal_set_title('Backoffice - Taxonomie');
  return drupal_render($form);
}

function MODULENAME_user_has_role($user,$roles) { 
  foreach ($roles as $role) {
    if (in_array($role, $user->roles)) {
      return TRUE;
    }
  }
  return FALSE; 
}

2 Answers 2

2

To have a single access callback handle a dynamic number of roles which varies per menu link, try something like this in hook_menu()...

// hook_menu()
$items['foo'] = array(
  /* ... */
  'access callback' => 'MODULENAME_user_has_role',   
  'access arguments' =>  array('éditeur taxonomie', 'administrator'),
);
$items['bar'] = array(
  /* ... */
  'access callback' => 'MODULENAME_user_has_role',   
  'access arguments' =>  array('some other role'),
);

Then your access callback implementation:

function MODULENAME_user_has_role() { 
  global $user;
  $roles = func_get_args();
  foreach ($roles as $role) {
    if (in_array($role, $user->roles)) {
      return TRUE;
    }
  }
  return FALSE; 
}

That should do it.

Some alternatives

Remove the global $user and access arguments from hook_menu().

Then change your access callback function implementation to this:

function MODULENAME_user_has_role() { 
  global $user;
  $roles = array('éditeur taxonomie', 'administrator');
  foreach ($roles as $role) {
    if (in_array($role, $user->roles)) {
      return TRUE;
    }
  }
  return FALSE; 
}

Since hook_menu() is invoked only when the menu router is being rebuilt (e.g. a cache clear, typically done by an admin or drush), global $user is not usually very useful from within that function. Instead, an access callback function is invoked at a more appropriate time (i.e. when the page is being viewed, or a menu link is about to be shown [or not] in a menu), at this point it is much easier to make a decision about the current user's context.

To make the access callback more generic, and be able to handle multiple menu link paths, I'll typically use a switch/case on an arg() to further curate the context:

switch (arg(3)) {
  case 'taxonomie':
    // do stuff...
    break;
}
2
  • 1
    this works fine thanks. I had done that because I wanted it to be more generic. I mean to pass $role from $item directly, so that I would have a single MODULENAME_user_has_role() for all the menu items....
    – Kojo
    Commented Jul 21, 2015 at 16:42
  • 1
    I've updated the answer to include the dynamic approach you are looking for. Commented Jul 21, 2015 at 16:56
1

A small addition I would make would be to do a check for UID = 1 like the rest of D7. A little late but hope it might helps some one.

function MODULE_user_has_role() {
  global $user;
  if ($user->uid == 1) {
    return TRUE;
  }
  $roles = func_get_args();
  foreach ($roles as $role) {
    if (in_array($role, $user->roles)) {
      return TRUE;
    }
  }
  return FALSE;
}

Your Answer

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

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