role_delegation.module

Tracking master branch
  1. drupal
    1. 7 contributions/role_delegation/role_delegation.module

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

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

Functions & methods

NameDescription
role_delegation_accessAccess callback for menu hook.
role_delegation_action_infoImplements hook_action_info().
role_delegation_delegate_roles_action
role_delegation_delegate_roles_action_form
role_delegation_delegate_roles_action_submit
role_delegation_form_alterImplements hook_form_alter().
role_delegation_form_user_admin_account_alterImplements hook_form_FORM_ID_alter() for user_admin_account().
role_delegation_form_user_admin_role_alterImplements hook_form_FORM_ID_alter() for user_admin_role().
role_delegation_form_user_admin_role_submitSubmit function for the user_admin_role form: When a role is renamed, renames the "assign role" permission for that role. We can't use hook_user_role_update() for this, because it doesn't have access to the old role name.
role_delegation_helpImplements hook_help().
role_delegation_menuImplements hook_menu().
role_delegation_permissionImplements hook_permission().
role_delegation_roles_formProvides a form for assigning roles to the current user.
role_delegation_roles_form_submitSaves the roles assigned to the account given in the form.
role_delegation_save
role_delegation_themeImplements hook_theme().
role_delegation_user_operationsImplements hook_user_operations().
role_delegation_user_presaveImplements hook_user_presave().
role_delegation_user_role_deleteImplements hook_user_role_delete(). When a role is deleted, deletes the "assign role" permission for that role.
theme_role_delegation_delegate_roles_action_form
_role_delegation_add_roles_to_formHelper function to create the role options.
_role_delegation_rolesReturns all existing roles, except anonymous and authenticated user.

File

View source
  1. <?php
  2. /**
  3. * @file
  4. *
  5. * This module allows site administrators to grant some roles the authority to
  6. * change roles assigned to users, without them needing the 'administer access
  7. * control' permission.
  8. *
  9. * It provides its own tab in the user profile so that roles can be changed
  10. * without needing access to the user edit form.
  11. */
  12. /**
  13. * Implements hook_help().
  14. */
  15. function role_delegation_help($path, $arg) {
  16. switch ($path) {
  17. case 'admin/help#role_delegation':
  18. $output = '<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>';
  19. $output .= '<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>';
  20. return $output;
  21. }
  22. }
  23. /**
  24. * Implements hook_theme().
  25. */
  26. function role_delegation_theme() {
  27. return array(
  28. 'role_delegation_delegate_roles_action_form' => array(
  29. 'render element' => 'form',
  30. ),
  31. );
  32. };
  33. /**
  34. * Implements hook_permission().
  35. */
  36. function role_delegation_permission() {
  37. $roles = _role_delegation_roles();
  38. $perms['assign all roles'] = array(
  39. 'title' => t('Assign all roles'),
  40. 'restrict access' => TRUE,
  41. );
  42. foreach ($roles as $rid => $role) {
  43. $perms["assign $role role"] = array(
  44. 'title' => t('Assign %role role', array('%role' => $role)),
  45. );
  46. }
  47. return $perms;
  48. }
  49. /**
  50. * Implements hook_menu().
  51. */
  52. function role_delegation_menu() {
  53. $items['user/%user/roles'] = array(
  54. 'title' => 'Roles',
  55. 'page callback' => 'drupal_get_form',
  56. 'page arguments' => array('role_delegation_roles_form', 1),
  57. 'access callback' => 'role_delegation_access',
  58. 'type' => MENU_LOCAL_TASK,
  59. );
  60. return $items;
  61. }
  62. /**
  63. * Helper function to create the role options.
  64. */
  65. function _role_delegation_add_roles_to_form(&$form, $account) {
  66. $roles_current = $account->roles;
  67. $roles_delegate = array();
  68. $roles = _role_delegation_roles();
  69. foreach ($roles as $rid => $role) {
  70. if (user_access('assign all roles') || user_access("assign $role role")) {
  71. $roles_delegate[$rid] = isset($form['account']['roles']['#options'][$rid]) ? $form['account']['roles']['#options'][$rid] : $role;
  72. }
  73. }
  74. if (empty($roles_delegate)) {
  75. // No role can be assigned.
  76. return;
  77. }
  78. if (!isset($form['account'])) {
  79. $form['account'] = array(
  80. '#type' => 'value',
  81. '#value' => $account,
  82. );
  83. }
  84. // Generate the form items.
  85. $form['account']['roles_change'] = array(
  86. '#type' => 'checkboxes',
  87. '#title' => isset($form['account']['roles']['#title']) ? $form['account']['roles']['#title'] : t('Roles'),
  88. '#options' => $roles_delegate,
  89. '#default_value' => array_keys(array_intersect_key($roles_current, $roles_delegate)),
  90. '#description' => isset($form['account']['roles']['#description']) ? $form['account']['roles']['#description'] : t('Change roles assigned to user.'),
  91. );
  92. }
  93. /**
  94. * Provides a form for assigning roles to the current user.
  95. */
  96. function role_delegation_roles_form($form, $form_state, $account) {
  97. _role_delegation_add_roles_to_form($form, $account);
  98. $form['submit'] = array(
  99. '#type' => 'submit',
  100. '#value' => t('Save'),
  101. );
  102. return $form;
  103. }
  104. /**
  105. * Saves the roles assigned to the account given in the form.
  106. */
  107. function role_delegation_roles_form_submit($form, &$form_state) {
  108. if (is_array($form_state['values']['roles_change']) && isset($form_state['values']['account']->uid)) {
  109. $uid = (int)$form_state['values']['account']->uid;
  110. role_delegation_save(array($uid), $form_state['values']['roles_change']);
  111. drupal_set_message(t('The roles have been updated.'));
  112. }
  113. }
  114. function role_delegation_save($uids, $roles_change) {
  115. $rolenames = user_roles(TRUE);
  116. foreach ($roles_change as $rid => $value) {
  117. if (!empty($value)) {
  118. // Use the role name for changed roles.
  119. $roles_change[$rid] = $rolenames[$rid];
  120. }
  121. }
  122. $accounts = user_load_multiple($uids);
  123. foreach ($accounts as $account) {
  124. $roles_current = $account->roles;
  125. $roles = array_filter($roles_change + $roles_current);
  126. user_save($account, array('roles' => $roles));
  127. }
  128. }
  129. /**
  130. * Access callback for menu hook.
  131. */
  132. function role_delegation_access() {
  133. // Check access to user profile page.
  134. if (!user_access('access user profiles')) {
  135. return FALSE;
  136. }
  137. // Check if they can edit users. In that case, the Roles tab is not needed.
  138. if (user_access('administer users')) {
  139. return FALSE;
  140. }
  141. // Check access to role assignment page.
  142. if (user_access('administer permissions')) {
  143. return TRUE;
  144. }
  145. $perms = array_keys(role_delegation_permission());
  146. foreach ($perms as $perm) {
  147. if (user_access($perm)) {
  148. return TRUE;
  149. }
  150. }
  151. return FALSE;
  152. }
  153. /**
  154. * Returns all existing roles, except anonymous and authenticated user.
  155. */
  156. function _role_delegation_roles() {
  157. $roles = user_roles(TRUE);
  158. unset($roles[DRUPAL_AUTHENTICATED_RID]);
  159. // Do not allow to delegate users to administator role. Let's keep this
  160. // privilege to users with 'Administer permissions' permission only.
  161. unset($roles[variable_get('user_admin_role', 0)]);
  162. return $roles;
  163. }
  164. /**
  165. * Implements hook_form_alter().
  166. */
  167. function role_delegation_form_alter(&$form, $form_state, $form_id) {
  168. // Only add role delegation options to:
  169. // * Registration tab of account registration form
  170. // * Account editing tab of account editing form
  171. // These are normally the only tabs present on those forms, but other tabs may be added
  172. // by other modules, e.g. profile editing tabs by Profile 2.
  173. if (!( ($form_id == 'user_register_form' && $form['#user_category'] == 'register')
  174. || ($form_id == 'user_profile_form' && $form['#user_category'] == 'account' ) )) {
  175. return;
  176. }
  177. if (user_access('administer permissions')) {
  178. return;
  179. }
  180. $account = $form['#user'];
  181. _role_delegation_add_roles_to_form($form, $account);
  182. }
  183. /**
  184. * Implements hook_form_FORM_ID_alter() for user_admin_role().
  185. */
  186. function role_delegation_form_user_admin_role_alter(&$form, $form_state) {
  187. $form['#submit'][] = 'role_delegation_form_user_admin_role_submit';
  188. }
  189. /**
  190. * Submit function for the user_admin_role form:
  191. * When a role is renamed, renames the "assign role" permission for that role.
  192. * We can't use hook_user_role_update() for this, because it doesn't have
  193. * access to the old role name.
  194. */
  195. function role_delegation_form_user_admin_role_submit($form, $form_state) {
  196. $op = $form_state['values']['op'];
  197. if ($op != t('Save role')) {
  198. return;
  199. }
  200. $oldrole = $form_state['build_info']['args'][0]->name;
  201. $newrole = $form_state['values']['name'];
  202. if ($oldrole == $newrole) {
  203. return;
  204. }
  205. db_update('role_permission')
  206. ->condition('permission', "assign $oldrole role")
  207. ->fields(array('permission' => "assign $newrole role"))
  208. ->execute();
  209. }
  210. /**
  211. * Implements hook_user_role_delete().
  212. * When a role is deleted, deletes the "assign role" permission for that role.
  213. */
  214. function role_delegation_user_role_delete($role) {
  215. db_delete('role_permission')
  216. ->condition('permission', "assign {$role->name} role")
  217. ->execute();
  218. }
  219. /**
  220. * Implements hook_user_presave().
  221. */
  222. function role_delegation_user_presave(&$edit, $account, $category) {
  223. if (isset($edit['roles_change'])) {
  224. $edit['roles'] = array_filter($edit['roles_change'] + $edit['roles']);
  225. unset($edit['roles_change']);
  226. }
  227. }
  228. /**
  229. * Implements hook_user_operations().
  230. */
  231. function role_delegation_user_operations($form = array(), $form_state = array()) {
  232. // Only provide role add/remove operations when user can't assign permissions
  233. // without Role Delegation.
  234. if (user_access('administer permissions')) {
  235. return;
  236. }
  237. // Provide add/remove operations for delegated roles.
  238. $add_roles = array();
  239. $remove_roles = array();
  240. foreach (_role_delegation_roles() as $rid => $role) {
  241. if (user_access('assign all roles') || user_access("assign $role role")) {
  242. $add_roles['role_delegation_add_role-' . $rid]['label'] = t('Add role: !role', array('!role' => $role));
  243. $remove_roles['role_delegation_remove_role-' . $rid]['label'] = t('Remove role: !role', array('!role' => $role));
  244. }
  245. }
  246. $operations = $add_roles + $remove_roles;
  247. // If the form has been posted, insert the proper data for role editing.
  248. if (!empty($form_state['submitted'])) {
  249. $operation_rid = explode('-', $form_state['values']['operation']);
  250. $operation = $operation_rid[0];
  251. if ($operation == 'role_delegation_add_role' || $operation == 'role_delegation_remove_role') {
  252. if (array_key_exists($form_state['values']['operation'], $operations)) {
  253. $rid = $operation_rid[1];
  254. $operations[$form_state['values']['operation']] += array(
  255. // use the standard add_role and remove_role operations:
  256. 'callback' => 'user_multiple_role_edit',
  257. 'callback arguments' => array(str_replace('role_delegation_', '', $operation), $rid),
  258. );
  259. }
  260. else {
  261. watchdog('security', 'Detected malicious attempt to alter protected user fields.', array(), WATCHDOG_WARNING);
  262. return;
  263. }
  264. }
  265. }
  266. return $operations;
  267. }
  268. /**
  269. * Implements hook_form_FORM_ID_alter() for user_admin_account().
  270. *
  271. * In the user bulk update form, separates out the role delegation operations
  272. * and groups and relabels them under 'Add a role' and 'Remove a role' optgroups.
  273. */
  274. function role_delegation_form_user_admin_account_alter(&$form, $form_state, $form_id) {
  275. $options = $form['options']['operation']['#options'];
  276. $roles = _role_delegation_roles();
  277. $add_roles = array();
  278. $remove_roles = array();
  279. foreach ($options as $option => $label) {
  280. $operation_rid = explode('-', $option);
  281. $operation = $operation_rid[0];
  282. if ($operation == 'role_delegation_add_role') {
  283. $rid = $operation_rid[1];
  284. $add_roles[$option] = $roles[$rid];
  285. unset($options[$option]);
  286. }
  287. elseif ($operation == 'role_delegation_remove_role') {
  288. $rid = $operation_rid[1];
  289. $remove_roles[$option] = $roles[$rid];
  290. unset($options[$option]);
  291. }
  292. }
  293. if (count($add_roles)) {
  294. $form['options']['operation']['#options'] = $options + array(
  295. t('Add a role to the selected users') => $add_roles,
  296. t('Remove a role from the selected users') => $remove_roles,
  297. );
  298. }
  299. }
  300. /**
  301. * Implements hook_action_info().
  302. */
  303. function role_delegation_action_info() {
  304. return array(
  305. 'role_delegation_delegate_roles_action' => array(
  306. 'type' => 'user',
  307. 'label' => t('Delegate roles'),
  308. 'configurable' => TRUE,
  309. 'triggers' => array(),
  310. ),
  311. );
  312. }
  313. function role_delegation_delegate_roles_action_form($context) {
  314. $form['#tree'] = TRUE;
  315. $form['#theme'] = 'role_delegation_delegate_roles_action_form';
  316. foreach (_role_delegation_roles() as $rid => $role_name) {
  317. $form['role_change'][$rid] = array(
  318. '#type' => 'select',
  319. '#title' => check_plain($role_name),
  320. '#default_value' => isset($context['roles_change'][$rid]) ? $context['roles_change'][$rid] : -1,
  321. '#options' => array(
  322. -1 => t('Do not change'),
  323. 1 => t('Add this role'),
  324. 0 => t('Remove this role'),
  325. ),
  326. );
  327. }
  328. return $form;
  329. }
  330. function theme_role_delegation_delegate_roles_action_form($variables) {
  331. $form = $variables['form'];
  332. $rows = array();
  333. $header = array(
  334. t('Role'),
  335. t('Operation'),
  336. );
  337. foreach (element_children($form['role_change']) as $key) {
  338. $role = $form['role_change'][$key]['#title'];
  339. unset($form['role_change'][$key]['#title']);
  340. $operation = drupal_render($form['role_change'][$key]);
  341. $row = array(
  342. array('data' => $role),
  343. array('data' => $operation),
  344. );
  345. $rows[] = $row;
  346. }
  347. $output = drupal_render($form['actions_label']);
  348. $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'role-delegation-table')));
  349. $output .= drupal_render_children($form);
  350. return $output;
  351. }
  352. function role_delegation_delegate_roles_action_submit($form, $form_state) {
  353. $roles_change = array();
  354. foreach ($form_state['values']['role_change'] as $rid => $value) {
  355. if ($value > -1) {
  356. $roles_change[$rid] = $value;
  357. }
  358. }
  359. return array('roles_change' => $roles_change);
  360. }
  361. function role_delegation_delegate_roles_action(&$user, $context) {
  362. $roles_current = $user->roles;
  363. $roles_change = $context['roles_change'];
  364. role_delegation_save(array($user->uid), $roles_change);
  365. }