You are here

role_delegation.module in Role Delegation 6

This module allows site administrators to grant some roles the authority to assign selected roles to users, without them needing the 'administer access control' permission.

It provides its own tab in the user profile so that roles can be assigned without needing access to the user edit form.

File

role_delegation.module
View source
<?php

/**
 * @file
 *
 * This module allows site administrators to grant some roles the authority to
 * assign selected roles to users, without them needing the 'administer access
 * control' permission.
 *
 * It provides its own tab in the user profile so that roles can be assigned
 * without needing access to the user edit form.
 */

/**
 * Implementation of hook_help().
 */
function role_delegation_help($section) {
  switch ($section) {
    case 'admin/help#role_delegation':
      return '<p>' . t('This module allows site administrators to grant some roles the authority to assign selected roles to users, without them needing the <em>administer permissions</em> permission.') . '</p><p>' . t('It provides its own tab in the user profile so that roles can be assigned without needing access to the user edit form.') . '</p>';
  }
}

/**
 * Implementation of hook_perm().
 */
function role_delegation_perm() {
  $roles = _role_delegation_roles();
  $perms = array(
    'assign all roles',
  );
  foreach ($roles as $role) {
    $perms[] = _role_delegation_make_perm($role);
  }
  return $perms;
}

/**
 * Implementation of hook_menu().
 */
function role_delegation_menu() {
  global $user;
  $items = array();
  $items['user/%user/roles'] = array(
    'title' => 'Roles',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'role_delegation_roles_form',
      1,
    ),
    'access callback' => 'role_delegation_access',
    'type' => MENU_LOCAL_TASK,
  );
  return $items;
}

/**
 * Provides a form for assigning roles to the current user.
 */
function role_delegation_roles_form(&$form_state, $account) {
  $form['roles'] = array(
    '#type' => 'fieldset',
    '#title' => t('Roles'),
    '#tree' => TRUE,
  );

  // Provide a separate checkbox for each role but hide those the user has no authority over.
  $roles = _role_delegation_roles();
  $roles_preserve = array(
    'authenticated user',
  );
  foreach ($roles as $rid => $role) {
    if (!(user_access('assign all roles') || user_access(_role_delegation_make_perm($role)) || user_access('administer permissions'))) {

      // Hide roles the user can't assign.
      $form['roles'][$rid] = array(
        '#type' => 'value',
        '#value' => isset($account->roles[$rid]),
      );
      if (isset($account->roles[$rid])) {
        $roles_preserve[] = $role;
      }
    }
    else {
      $form['roles'][$rid] = array(
        '#type' => 'checkbox',
        '#title' => check_plain($role),
        '#default_value' => isset($account->roles[$rid]),
      );
    }
  }
  $form['roles']['#description'] = t('The user receives the combined permissions of the %roles role(s), and all roles selected here. ', array(
    '%roles' => implode(', ', $roles_preserve),
  ));
  $form['account'] = array(
    '#type' => 'value',
    '#value' => $account,
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );
  drupal_set_title(check_plain($account->name));
  return $form;
}

/**
 * Saves the roles assigned to the account given in the form.
 */
function role_delegation_roles_form_submit($form, &$form_state) {
  if (is_array($form_state['values']['roles']) && isset($form_state['values']['account']->uid)) {
    $account = user_load(array(
      'uid' => (int) $form_state['values']['account']->uid,
    ));
    $myroles = array();
    $rolenames = user_roles(TRUE);
    foreach (array_keys(array_filter($form_state['values']['roles'])) as $rid) {
      $myroles[$rid] = $rolenames[$rid];
    }
    user_save($account, array(
      'roles' => $myroles,
    ));

    // Delete the user's menu cache.
    cache_clear_all($form_state['values']['account']->uid . ':', 'cache_menu', TRUE);
    drupal_set_message(t('The roles have been updated.'));
  }
}

/**
 * Access callback for menu hook.
 */
function role_delegation_access() {

  // Check access to user profile page.
  if (!user_access('access user profiles')) {
    return FALSE;
  }

  // Check if they can edit users. In that case, the Roles tab is not needed.
  if (user_access('administer users')) {
    return FALSE;
  }

  // Check access to role assignment page.
  if (user_access('administer permissions')) {
    return TRUE;
  }
  $perms = role_delegation_perm();
  foreach ($perms as $perm) {
    if (user_access($perm)) {
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * Returns all existing roles, except anonymous and authenticated user.
 */
function _role_delegation_roles() {
  $roles = user_roles(TRUE);
  unset($roles[DRUPAL_AUTHENTICATED_RID]);
  return $roles;
}

/**
 * Returns the delegation permission for a role.
 */
function _role_delegation_make_perm($role) {
  return "assign {$role} role";
}

/**
 * Implementation of hook_form_FORM_ID_alter() for user_admin_role().
 */
function role_delegation_form_user_admin_role_alter(&$form, $form_state) {
  $form['#submit'][] = 'role_delegation_form_user_admin_role_submit';
}

/**
 * Submit function for the user_admin_role form:
 * When a role is renamed or deleted, rename or delete the permission
 * to assign that role.
 */
function role_delegation_form_user_admin_role_submit($form, $form_state) {
  $op = $form_state['values']['op'];
  if ($op != t('Save role') && $op != t('Delete role')) {
    return;
  }
  $oldrole = $form['name']['#default_value'];
  $newrole = $form_state['values']['name'];
  if ($op == t('Save role') && $oldrole == $newrole) {
    return;
  }

  // Role is being renamed or deleted.
  // Loop through permission lists for all roles, renaming or deleting the
  // 'assign' permission for this role.
  $oldperm = _role_delegation_make_perm($oldrole);
  $result = db_query('SELECT * FROM {permission}');
  while ($row = db_fetch_object($result)) {
    $perms = explode(', ', $row->perm);
    for ($i = 0; $i < count($perms); ++$i) {
      if ($perms[$i] == $oldperm) {
        switch ($op) {
          case t('Save role'):
            $perms[$i] = _role_delegation_make_perm($newrole);
            break;
          case t('Delete role'):
            unset($perms[$i]);
            break;
        }
        if (count($perms)) {
          db_query("UPDATE {permission} SET perm = '%s' WHERE pid = %d", implode(', ', $perms), $row->pid);
        }
        else {
          db_query("DELETE FROM {permission} WHERE pid = %d", $row->pid);
        }
        break;
      }
    }
  }
}

/**
 * Implementation of hook_user().
 */
function role_delegation_user($op, &$edit, &$account, $category = NULL) {
  if ($op == 'register' || $op == 'form' && $category == 'account') {

    // Only alter user form when user can't assign permissions without Role Delegation.
    if (!user_access('administer permissions')) {

      // Split up roles based on whether they can be delegated or not.
      $current_roles = isset($account->roles) ? $account->roles : array();
      $rids_default = array();
      $rids_preserve = array(
        DRUPAL_AUTHENTICATED_RID => DRUPAL_AUTHENTICATED_RID,
      );
      $roles_preserve = array(
        'authenticated user',
      );
      $roles_options = array();
      $roles = _role_delegation_roles();
      foreach ($roles as $rid => $role) {
        if (user_access('assign all roles') || user_access(_role_delegation_make_perm($role))) {
          if (array_key_exists($rid, $current_roles)) {
            $rids_default[] = $rid;
          }
          $roles_options[$rid] = $role;
        }
        else {
          if (array_key_exists($rid, $current_roles)) {
            $rids_preserve[$rid] = $rid;
            $roles_preserve[] = $role;
          }
        }
      }
      if (empty($roles_options)) {

        // No role can be assigned.
        return;
      }

      // Generate the form items.
      $form['roles_preserve'] = array(
        '#type' => 'value',
        '#value' => $rids_preserve,
      );
      $roles_assign = array(
        '#type' => 'checkboxes',
        '#title' => t('Roles'),
        '#description' => t('The user receives the combined permissions of the %roles role(s), and all roles selected here. ', array(
          '%roles' => implode(', ', $roles_preserve),
        )),
        '#options' => $roles_options,
        '#default_value' => $rids_default,
        '#weight' => 10,
      );
      if ($op == 'register') {

        // Since the user module does array_merge() instead of array_merge_recursive() (see bug http://drupal.org/node/227690),
        // we must move this under 'account' later at role_delegation_form_user_register_alter()
        $form['roles_assign'] = $roles_assign;
      }
      else {
        $form['account']['roles_assign'] = $roles_assign;
      }
      return $form;
    }
  }
  elseif (isset($edit['roles_assign']) && ($op == 'insert' || $op == 'submit')) {
    $edit['roles'] = $edit['roles_preserve'] + array_filter($edit['roles_assign']);
  }
}

/**
 * Implementation of hook_form_FORM_ID_alter() for user_register().
 */
function role_delegation_form_user_register_alter(&$form, $form_state) {

  // Move our field where it belongs
  if (isset($form['roles_assign'])) {
    $form['account']['roles_assign'] = $form['roles_assign'];
    unset($form['roles_assign']);
    $form['account']['notify']['#weight'] = 11;
  }
}

/**
 * Implementation of hook_user_operations().
 */
function role_delegation_user_operations($form_state = array()) {

  // Only provide role add/remove operations when user can't assign permissions
  // without Role Delegation.
  if (user_access('administer permissions')) {
    return;
  }

  // Provide add/remove operations for delegated roles.
  $roles = _role_delegation_roles();
  $add_roles = array();
  $remove_roles = array();
  foreach ($roles as $rid => $role) {
    if (user_access('assign all roles') || user_access(_role_delegation_make_perm($role))) {

      // Use different operation names than those from user_user_operations(),
      // to keep user_user_operations() from emitting a warning about the
      // permissions.
      $add_roles['role_delegation_add_role-' . $rid] = $role;
      $remove_roles['role_delegation_remove_role-' . $rid] = $role;
    }
  }
  if (!count($add_roles)) {
    return;
  }
  $operations = array(
    t('Add a role to the selected users') => array(
      'label' => $add_roles,
    ),
    t('Remove a role from the selected users') => array(
      'label' => $remove_roles,
    ),
  );

  // If the form has been posted, insert the proper data for role editing if necessary.
  if (!empty($form_state['submitted'])) {
    $operation_rid = explode('-', $form_state['values']['operation']);
    $operation = $operation_rid[0];
    if ($operation == 'role_delegation_add_role' || $operation == 'role_delegation_remove_role') {
      $rid = $operation_rid[1];
      if ($add_roles['role_delegation_add_role-' . $rid]) {
        $operations[$form_state['values']['operation']] = array(
          'callback' => 'user_multiple_role_edit',
          'callback arguments' => array(
            str_replace('role_delegation_', '', $operation),
            $rid,
          ),
        );
      }
      else {
        watchdog('security', 'Detected malicious attempt to alter protected user fields.', array(), WATCHDOG_WARNING);
        return;
      }
    }
  }
  return $operations;
}

Functions

Namesort descending Description
role_delegation_access Access callback for menu hook.
role_delegation_form_user_admin_role_alter Implementation of hook_form_FORM_ID_alter() for user_admin_role().
role_delegation_form_user_admin_role_submit Submit function for the user_admin_role form: When a role is renamed or deleted, rename or delete the permission to assign that role.
role_delegation_form_user_register_alter Implementation of hook_form_FORM_ID_alter() for user_register().
role_delegation_help Implementation of hook_help().
role_delegation_menu Implementation of hook_menu().
role_delegation_perm Implementation of hook_perm().
role_delegation_roles_form Provides a form for assigning roles to the current user.
role_delegation_roles_form_submit Saves the roles assigned to the account given in the form.
role_delegation_user Implementation of hook_user().
role_delegation_user_operations Implementation of hook_user_operations().
_role_delegation_make_perm Returns the delegation permission for a role.
_role_delegation_roles Returns all existing roles, except anonymous and authenticated user.