Drupal - How do I programmatically assign the access to a block?

Setting the "roles" array in the array returned from hook_block_info() doesn't work because:

  • The roles that are allowed to see a block, and which are set in the user interface, are saved from block_admin_configure_submit() in the "block_role" table

    $query = db_insert('block_role')->fields(array('rid', 'module', 'delta'));
    foreach (array_filter($form_state['values']['roles']) as $rid) {
      $query->values(array(
        'rid' => $rid,
        'module' => $form_state['values']['module'],
        'delta' => $form_state['values']['delta'],
      ));
    }
    $query->execute();
    
  • The code that decides which blocks should be shown to the currently logged-in user is contained in block_block_list_alter(), which is an implementation of hook_block_list_alter(), and uses only the content of that table

    $result = db_query('SELECT module, delta, rid FROM {block_role}');
    foreach ($result as $record) {
      $block_roles[$record->module][$record->delta][] = $record->rid;
    }
    
    foreach ($blocks as $key => $block) {
      if (!isset($block->theme) || !isset($block->status) || $block->theme != $theme_key || $block->status != 1) {
        // This block was added by a contrib module, leave it in the list.
        continue;
      }
    
      // If a block has no roles associated, it is displayed for every role.
      // For blocks with roles associated, if none of the user's roles matches
      // the settings from this block, remove it from the block list.
      if (isset($block_roles[$block->module][$block->delta]) && !array_intersect($block_roles[$block->module][$block->delta], array_keys($user->roles))) {
        // No match.
        unset($blocks[$key]);
        continue;
      }
    
      // …
    
    }
    
  • There isn't another Drupal function that checks the roles property in the data returned from hook_block_info(), nor is the content of the "block_role" table merged with what returned from the hook_block_info() implementations.

You could verify the user has the required role to see the block in hook_block_view(), but at that point Drupal is already rendering the block; that means the user would still see the block title, if one has been already set.

What you can do is implementing hook_block_list_alter() to remove the information about that block when the user doesn't have the required role.
To avoid confusion to the users who administer the blocks, I would also alter the form used to edit a block, and disable the form field used to set which roles can see that block, since the module implementing it is going to use its own list of roles; the minimal code should at least show a message about the role settings not having any effect, but I would also disable the form elements for the role settings.

Since the Block module already shows form fields to select which roles see a block, you could also simply set a default for your block, and let the administrator users change it if necessary.

screenshot

As per checking the roles a user has versus checking the permissions a user has, the last is preferred, especially when the alternative would be hard-coding a list of roles in a module.
As shown from the Block module, using permission is not the only alternative: A module could have setting for deciding which roles are allowed to see something.
Clearly, it is not always worth having a setting for which roles are allowed to do something. I imagine also what for the administrator users would mean if 10 modules would have their own settings for which roles are allowed to do something, instead of using permissions, and allowing the administrator users to use a single page to set them.

Tags:

Users

7

Blocks