1

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; 
}
1

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;
}
  • 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 Jul 21 '15 at 16:42
  • 1
    I've updated the answer to include the dynamic approach you are looking for. – tyler.frankenstein Jul 21 '15 at 16:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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