Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.860
diff -u -p -r1.860 common.inc
--- includes/common.inc	31 Jan 2009 19:07:45 -0000	1.860
+++ includes/common.inc	1 Feb 2009 16:27:49 -0000
@@ -4048,6 +4048,53 @@ function watchdog_severity_levels() {
   );
 }
 
+/**
+ * Returns an array whose keys and values contain the IDs of all administrative
+ * roles on the site.
+ */
+function drupal_administrative_roles() {
+  return variable_get('drupal_administrative_roles', array());
+}
+
+/**
+ * Returns a boolean indicating whether or not a given role ID is one of the
+ * administrative roles on the site.
+ *
+ * @param $rid
+ *   The ID of the role to check.
+ */
+function drupal_is_administrative_role($rid) {
+  $administrative_roles = drupal_administrative_roles();
+  return isset($administrative_roles[$rid]);
+}
+
+/**
+ * Add a role to the list of administrative roles on the site.
+ *
+ * @param $rid
+ *   The ID of the role to add.
+ */
+function drupal_add_administrative_role($rid) {
+  if (!drupal_is_administrative_role($rid)) {
+    $administrative_roles = drupal_administrative_roles();
+    $administrative_roles[$rid] = $rid;
+    variable_set('drupal_administrative_roles', $administrative_roles);
+  }
+}
+
+/**
+ * Remove a role from the list of administrative roles on the site.
+ *
+ * @param $rid
+ *   The ID of the role to remove.
+ */
+function drupal_remove_administrative_role($rid) {
+  if (drupal_is_administrative_role($rid)) {
+    $administrative_roles = drupal_administrative_roles();
+    unset($administrative_roles[$rid]);
+    variable_set('drupal_administrative_roles', $administrative_roles);
+  }
+}
 
 /**
  * Explode a string of given tags into an array.
Index: modules/simpletest/drupal_web_test_case.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/drupal_web_test_case.php,v
retrieving revision 1.81
diff -u -p -r1.81 drupal_web_test_case.php
--- modules/simpletest/drupal_web_test_case.php	25 Jan 2009 12:19:32 -0000	1.81
+++ modules/simpletest/drupal_web_test_case.php	1 Feb 2009 16:27:49 -0000
@@ -715,6 +715,27 @@ class DrupalWebTestCase {
   }
 
   /**
+   * Returns the ID of the newest role assigned to a given user account.
+   *
+   * This can be used to find the new role that was created when a user is
+   * added via drupalCreateUser().
+   *
+   * @param $account
+   *   A user object representing the account in which the newest role will
+   *   be searched for.
+   * @return
+   *   The ID of the newest role that is assigned to this user.
+   *
+   * @see drupalCreateUser()
+   */
+  protected function drupalGetNewestRole(stdClass $account) {
+    // Find the new role ID; it must be the maximum.
+    $all_rids = array_keys($account->roles);
+    sort($all_rids);
+    return array_pop($all_rids);
+  }
+
+  /**
    * Check to make sure that the array of permissions are valid.
    *
    * @param $permissions
Index: modules/user/user.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.admin.inc,v
retrieving revision 1.36
diff -u -p -r1.36 user.admin.inc
--- modules/user/user.admin.inc	19 Jan 2009 10:46:52 -0000	1.36
+++ modules/user/user.admin.inc	1 Feb 2009 16:27:49 -0000
@@ -646,7 +646,21 @@ function theme_user_admin_perm($form) {
   $header[] = (t('Permission'));
   foreach (element_children($form['role_names']) as $rid) {
     if (is_array($form['role_names'][$rid])) {
-      $header[] = array('data' => drupal_render($form['role_names'][$rid]), 'class' => 'checkbox');
+      $header_class = 'checkbox';
+      $header_title = '';
+      // Add a special class and title for the administrative roles.
+      if (drupal_is_administrative_role($rid)) {
+        $header_class .= ' user-administrative-role';
+        $header_title = t('This is an administrative role. Modules may automatically grant this role certain permissions for convenience of administering the site.');
+      }
+      $header_data = array(
+        'data' => drupal_render($form['role_names'][$rid]),
+        'class' => $header_class,
+      );
+      if (!empty($header_title)) {
+        $header_data['title'] = $header_title;
+      }
+      $header[] = $header_data;
     }
   }
   $output = theme('system_compact_link');
@@ -680,6 +694,12 @@ function user_admin_role() {
       '#maxlength' => 64,
       '#description' => t('The name for this role. Example: "moderator", "editorial board", "site architect".'),
     );
+    $form['administrative_role'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Make this an administrative role'),
+      '#default_value' => drupal_is_administrative_role($role->rid),
+      '#description' => t('Labeling a role "administrative" will allow modules to automatically grant the role certain permissions for convenience of administering the site. Only roles which are limited to trusted users should be labeled as administrative.'),
+    );
     $form['rid'] = array(
       '#type' => 'value',
       '#value' => $rid,
@@ -730,14 +750,20 @@ function user_admin_role_validate($form,
 function user_admin_role_submit($form, &$form_state) {
   if ($form_state['values']['op'] == t('Save role')) {
     db_query("UPDATE {role} SET name = '%s' WHERE rid = %d", $form_state['values']['name'], $form_state['values']['rid']);
-    drupal_set_message(t('The role has been renamed.'));
+    if ($form_state['values']['administrative_role']) {
+      drupal_add_administrative_role($form_state['values']['rid']);
+    }
+    else {
+      drupal_remove_administrative_role($form_state['values']['rid']);
+    }
+    drupal_set_message(t('The role has been saved.'));
   }
   elseif ($form_state['values']['op'] == t('Delete role')) {
     db_query('DELETE FROM {role} WHERE rid = %d', $form_state['values']['rid']);
     db_query('DELETE FROM {role_permission} WHERE rid = %d', $form_state['values']['rid']);
-    // Update the users who have this role set:
+    drupal_remove_administrative_role($form_state['values']['rid']);
+    // Update the users who have this role set.
     db_query('DELETE FROM {users_roles} WHERE rid = %d', $form_state['values']['rid']);
-
     drupal_set_message(t('The role has been deleted.'));
   }
   elseif ($form_state['values']['op'] == t('Add role')) {
@@ -802,11 +828,12 @@ function theme_user_admin_new_role($form
   $header = array(t('Name'), array('data' => t('Operations'), 'colspan' => 2));
   foreach (user_roles() as $rid => $name) {
     $edit_permissions = l(t('edit permissions'), 'admin/user/permissions/' . $rid);
+    $display_name = drupal_is_administrative_role($rid) ? theme('user_administrative_role', $name) : $name;
     if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
-      $rows[] = array($name, l(t('edit role'), 'admin/user/roles/edit/' . $rid), $edit_permissions);
+      $rows[] = array($display_name, l(t('edit role'), 'admin/user/roles/edit/' . $rid), $edit_permissions);
     }
     else {
-      $rows[] = array($name, t('locked'), $edit_permissions);
+      $rows[] = array($display_name, t('locked'), $edit_permissions);
     }
   }
   $rows[] = array(drupal_render($form['name']), array('data' => drupal_render($form['submit']), 'colspan' => 2));
Index: modules/user/user.css
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.css,v
retrieving revision 1.10
diff -u -p -r1.10 user.css
--- modules/user/user.css	9 Oct 2008 04:19:44 -0000	1.10
+++ modules/user/user.css	1 Feb 2009 16:27:49 -0000
@@ -28,6 +28,10 @@
   padding-bottom: .5em;
 }
 
+.user-administrative-role {
+  color: blue;
+}
+
 /* Generated by user.module but used by profile.module: */
 .profile {
   clear: both;
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.960
diff -u -p -r1.960 user.module
--- modules/user/user.module	22 Jan 2009 12:46:07 -0000	1.960
+++ modules/user/user.module	1 Feb 2009 16:27:49 -0000
@@ -57,6 +57,9 @@ function user_theme() {
     'user_list' => array(
       'arguments' => array('users' => NULL, 'title' => NULL),
     ),
+    'user_administrative_role' => array(
+      'arguments' => array('name' => NULL),
+    ),
     'user_admin_perm' => array(
       'arguments' => array('form' => NULL),
       'file' => 'user.admin.inc',
@@ -1027,6 +1030,18 @@ function theme_user_list($users, $title 
   return theme('item_list', $items, $title);
 }
 
+/**
+ * Theme the name of an administrative user role.
+ *
+ * @param $name
+ *   The name of the role.
+ *
+ * @ingroup themeable
+ */
+function theme_user_administrative_role($name) {
+  return $name . ' <span class="user-administrative-role">' . t('(administrative role; assign to trusted users only)') . '</span>';
+}
+
 function user_is_anonymous() {
   // Menu administrators can see items for anonymous when administering.
   return !$GLOBALS['user']->uid || !empty($GLOBALS['menu_admin']);
@@ -1640,7 +1655,13 @@ function user_edit_form(&$form_state, $u
     );
   }
   if (user_access('administer permissions')) {
+    // Get a list of possible roles, and label the administrative ones.
     $roles = user_roles(TRUE);
+    foreach ($roles as $rid => $name) {
+      if (drupal_is_administrative_role($rid)) {
+        $roles[$rid] = theme('user_administrative_role', $name);
+      }
+    }
 
     // The disabled checkbox subelement for the 'authenticated user' role
     // must be generated separately and added to the checkboxes element,
@@ -1964,6 +1985,34 @@ function user_roles($membersonly = FALSE
 }
 
 /**
+ * Assign a list of permissions to all administrative roles on the site.
+ *
+ * This function can be called from a module's implementation of hook_install()
+ * in order to automatically assign administrator-level permissions to trusted
+ * users.
+ *
+ * @param $perms
+ *   An array containing the permissions to assign.
+ */
+function user_assign_administrative_permissions($perms = array()) {
+  // Find all permissions that the administrative roles already have.
+  $existing_perms = user_role_permissions(drupal_administrative_roles());
+  // Assign the requested permissions to each administrative role, avoiding
+  // duplicates.
+  foreach (drupal_administrative_roles() as $role) {
+    $unassigned_perms = array_diff($perms, array_keys($existing_perms[$role]));
+    foreach ($unassigned_perms as $perm) {
+      db_insert('role_permission')
+        ->fields(array(
+          'rid' => $role,
+          'permission' => $perm,
+        ))
+        ->execute();
+    }
+  }
+}
+
+/**
  * Implementation of hook_user_operations().
  */
 function user_user_operations($form_state = array()) {
@@ -2170,23 +2219,23 @@ function user_help($path, $arg) {
 
   switch ($path) {
     case 'admin/help#user':
-      $output = '<p>' . t('The user module allows users to register, login, and log out. Users benefit from being able to sign on because it associates content they create with their account and allows various permissions to be set for their roles. The user module supports user roles which establish fine grained permissions allowing each role to do only what the administrator wants them to. Each user is assigned to one or more roles. By default there are two roles <em>anonymous</em> - a user who has not logged in, and <em>authenticated</em> a user who has signed up and who has been authorized.') . '</p>';
+      $output = '<p>' . t('The user module allows site visitors to register, log in, and log out of the site. Visitors benefit from being able to log in because it allows them to post content under their own names. Administrators benefit from having logged-in users because they can create roles for different groups of users and establish permissions for those roles. Each user can then be assigned to one or more roles in order to grant the user access to various features of the site. Every site visitor is assigned to at least one role by default: <em>anonymous</em> &mdash; for a user who has not logged in, or <em>authenticated</em> &mdash; for a user who has both signed up for an account and who is currently logged in.') . '</p>';
       $output .= '<p>' . t("Users can use their own name or handle and can specify personal configuration settings through their individual <em>My account</em> page. Users must authenticate by supplying a local username and password or through their OpenID, an optional and secure method for logging into many websites with a single username and password. In some configurations, users may authenticate using a username and password from another Drupal site, or through some other site-specific mechanism.") . '</p>';
       $output .= '<p>' . t('A visitor accessing your website is assigned a unique ID, or session ID, which is stored in a cookie. The cookie does not contain personal information, but acts as a key to retrieve information from your site. Users should have cookies enabled in their web browser when using your site.') . '</p>';
       $output .= '<p>' . t('For more information, see the online handbook entry for <a href="@user">User module</a>.', array('@user' => 'http://drupal.org/handbook/modules/user/')) . '</p>';
       return $output;
     case 'admin/user/user':
-      return '<p>' . t('Drupal allows users to register, login, log out, maintain user profiles, etc. Users of the site may not use their own names to post content until they have signed up for a user account.') . '</p>';
+      return '<p>' . t('Drupal allows users to register, log in, log out, maintain user profiles, etc. Users of the site may not use their own names to post content until they have signed up for a user account.') . '</p>';
     case 'admin/user/user/create':
     case 'admin/user/user/account/create':
       return '<p>' . t("This web page allows administrators to register new users. Users' e-mail addresses and usernames must be unique.") . '</p>';
     case 'admin/user/permissions':
-      return '<p>' . t('Permissions let you control what users can do on your site. Each user role (defined on the <a href="@role">user roles page</a>) has its own set of permissions. For example, you could give users classified as "Administrators" permission to "administer nodes" but deny this power to ordinary, "authenticated" users. You can use permissions to reveal new features to privileged users (those with subscriptions, for example). Permissions also allow trusted users to share the administrative burden of running a busy site.', array('@role' => url('admin/user/roles'))) . '</p>';
+      return '<p>' . t('Permissions let you control what users can do on your site. Each role listed across the top of the table below (and configured on the <a href="@role">user roles page</a>) has its own set of permissions. For example, you could give users classified as "administrators" permission to "administer nodes" but deny this power to ordinary, "authenticated" users. You can use permissions to reveal new features to privileged users (those with subscriptions, for example). Permissions also allow trusted users to share the administrative burden of running a busy site.', array('@role' => url('admin/user/roles'))) . '</p>';
     case 'admin/user/roles':
-      return t('<p>Roles allow you to fine tune the security and administration of Drupal. A role defines a group of users that have certain privileges as defined in <a href="@permissions">user permissions</a>. Examples of roles include: anonymous user, authenticated user, moderator, administrator and so on. In this area you will define the <em>role names</em> of the various roles. To delete a role choose "edit".</p><p>By default, Drupal comes with two user roles:</p>
+      return t('<p>Roles allow you to fine tune the security and administration of Drupal. A role defines a group of users that are granted certain privileges in the <a href="@permissions">user permissions</a> section. Examples of common roles include: administrator, editor, moderator, content contributor, and so on. Below, you can add new roles or edit existing ones. When editing a role, you can choose to label it as <em>administrative</em>, which will allow modules to automatically grant this role certain permissions for convenience of administering the site. Only roles which are limited to trusted users should be labeled as administrative.</p><p>By default, Drupal comes with at least two user roles:</p>
       <ul>
-      <li>Anonymous user: this role is used for users that don\'t have a user account or that are not authenticated.</li>
-      <li>Authenticated user: this role is automatically granted to all logged in users.</li>
+      <li><em>Anonymous user:</em> This role is given to users that don\'t have a user account or that are not logged in.</li>
+      <li><em>Authenticated user:</em> This role is automatically given to all logged in users.</li>
       </ul>', array('@permissions' => url('admin/user/permissions')));
     case 'admin/user/search':
       return '<p>' . t('Enter a simple pattern ("*" may be used as a wildcard match) to search for a username or e-mail address. For example, one may search for "br" and Drupal might return "brian", "brad", and "brenda@example.com".') . '</p>';
Index: modules/user/user.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.test,v
retrieving revision 1.27
diff -u -p -r1.27 user.test
--- modules/user/user.test	31 Jan 2009 16:50:57 -0000	1.27
+++ modules/user/user.test	1 Feb 2009 16:27:49 -0000
@@ -713,13 +713,8 @@ class UserPermissionsTestCase extends Dr
 
   function setUp() {
     parent::setUp();
-
     $this->admin_user = $this->drupalCreateUser(array('administer permissions', 'access user profiles'));
-
-    // Find the new role ID - it must be the maximum.
-    $all_rids = array_keys($this->admin_user->roles);
-    sort($all_rids);
-    $this->rid = array_pop($all_rids);
+    $this->rid = $this->drupalGetNewestRole($this->admin_user);
   }
 
   /**
@@ -749,6 +744,75 @@ class UserPermissionsTestCase extends Dr
 
 }
 
+class UserAdministrativeRolesTestCase extends DrupalWebTestCase {
+  function getInfo() {
+    return array(
+      'name' => t('Administrative roles'),
+      'description' => t('Verify that administrative roles can be added, removed, and assigned permissions.'),
+      'group' => t('User')
+    );
+  }
+
+  /**
+   * Test that a role can be labeled and unlabeled "administrative" via the
+   * API and via the user interface.
+   */
+  function testChangeAdministrativeRole() {
+    // Create a user and find the new role ID.
+    $admin_user = $this->drupalCreateUser(array('administer permissions'));
+    $rid = $this->drupalGetNewestRole($admin_user);
+    $this->assertFalse(drupal_is_administrative_role($rid), t('A newly created role is not labeled as administrative.'));
+
+    // Test the API.
+    drupal_add_administrative_role($rid);
+    $this->assertTrue(drupal_is_administrative_role($rid), t('A role can be labeled as administrative via the API.'));
+    $this->assertTrue(in_array($rid, drupal_administrative_roles()), t('The role that was just labeled as administrative appears in the list of administrative roles.'));
+    drupal_remove_administrative_role($rid);
+    $this->assertFalse(drupal_is_administrative_role($rid), t('The administrative label can be removed from a role via the API.'));
+    $this->assertFalse(in_array($rid, drupal_administrative_roles()), t('The role whose administrative label was just removed does not appear in the list of administrative roles.'));
+
+    // Test the user interface.
+    $this->drupalLogin($admin_user);
+    $edit_role_url = 'admin/user/roles/edit/'. $rid;
+    $this->drupalPost($edit_role_url, array('administrative_role' => TRUE), t('Save role'));
+    $this->assertTrue(drupal_is_administrative_role($rid), t('A role can be labeled as administrative via the user interface.'));
+    $this->drupalPost($edit_role_url, array('administrative_role' => FALSE), t('Save role'));
+    $this->assertFalse(drupal_is_administrative_role($rid), t('The administrative label can be removed from a role via the user interface.'));
+  }
+
+  /**
+   * Test that permissions can be assigned to administrative roles via the
+   * API.
+   */
+  function testAssignAdministrativePermissions() {
+    // Create three roles, two of which will be labeled as administrative.
+    $first_admin_user = $this->drupalCreateUser();
+    $first_admin_rid = $this->drupalGetNewestRole($first_admin_user);
+    $second_admin_user = $this->drupalCreateUser();
+    $second_admin_rid = $this->drupalGetNewestRole($second_admin_user);
+    $regular_user = $this->drupalCreateUser();
+
+    // Label the two administrative roles.
+    drupal_add_administrative_role($first_admin_rid);
+    drupal_add_administrative_role($second_admin_rid);
+
+    // Check that none of the roles have the 'administer users' permission.
+    $perm = 'administer users';
+    $this->assertFalse(user_access($perm, $first_admin_user), t('The first administrative user does not start off with the "@perm" permission.', array('@perm' => $perm)));
+    $this->assertFalse(user_access($perm, $second_admin_user), t('The second administrative user does not start off with the "@perm" permission.', array('@perm' => $perm)));
+    $this->assertFalse(user_access($perm, $regular_user), t('The regular user does not start off with the "@perm" permission.', array('@perm' => $perm)));
+
+    // Now assign the permission, and check that only the users with
+    // administrative roles have it.
+    user_assign_administrative_permissions(array($perm));
+    // Clear the static cache the first time we check, since permissions
+    // have changed.
+    $this->assertTrue(user_access($perm, $first_admin_user, TRUE), t('The first administrative user has the "@perm" permission after it has been assigned to all administrative roles.', array('@perm' => $perm)));
+    $this->assertTrue(user_access($perm, $second_admin_user), t('The second administrative user has the "@perm" permission after it has been assigned to all administrative roles.', array('@perm' => $perm)));
+    $this->assertFalse(user_access($perm, $regular_user), t('The regular user still does not have the "@perm" permission after it has been assigned to all administrative roles.', array('@perm' => $perm)));    
+  }
+}
+
 class UserAdminTestCase extends DrupalWebTestCase {
   function getInfo() {
     return array(
Index: profiles/default/default.profile
===================================================================
RCS file: /cvs/drupal/drupal/profiles/default/default.profile,v
retrieving revision 1.36
diff -u -p -r1.36 default.profile
--- profiles/default/default.profile	14 Jan 2009 21:16:21 -0000	1.36
+++ profiles/default/default.profile	1 Feb 2009 16:27:49 -0000
@@ -145,6 +145,33 @@ function default_profile_tasks(&$task, $
   ))->execute();
   db_insert('taxonomy_vocabulary_node_type')->fields(array('vid' => $vid, 'type' => 'article'))->execute();
 
+  // Create a role for site administrators so that modules can assign their
+  // administrative permissions to it.
+  $rid = db_insert('role')->fields(array('name' => 'administrator'))->execute();
+  drupal_add_administrative_role($rid);
+
+  // Assign some initial administrative permissions to this role.
+  $administrative_perms = array(
+    // Permissions for required core modules.
+    'administer blocks',
+    'administer filters',
+    'administer content types',
+    'administer nodes',
+    'access administration pages',
+    'access site reports',
+    'administer actions',
+    'administer files',
+    'administer site configuration',
+    'access user profiles',
+    'administer permissions',
+    'administer users',
+    // Permissions for other modules installed by this profile.
+    'administer comments',
+    'administer menu',
+    'administer taxonomy',
+  );
+  user_assign_administrative_permissions($administrative_perms);
+
   // Update the menu router information.
   menu_rebuild();
 
