? 371235-patched-benchmarks.txt
? 371235-unpatched-benchmarks.txt
? sites/all/modules/cvs
Index: modules/comment/comment.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.test,v
retrieving revision 1.25
diff -u -p -r1.25 comment.test
--- modules/comment/comment.test	7 Feb 2009 20:10:40 -0000	1.25
+++ modules/comment/comment.test	24 Feb 2009 20:56:12 -0000
@@ -9,12 +9,9 @@ class CommentHelperCase extends DrupalWe
   function setUp() {
     parent::setUp('comment');
     // Create users.
-    $this->admin_user = $this->drupalCreateUser(array('administer content types', 'administer comments', 'administer permissions', 'administer blocks'));
+    $this->admin_user = $this->drupalCreateUser(array('administer content types', 'administer comments', 'administer blocks'));
     $this->web_user = $this->drupalCreateUser(array('access comments', 'post comments', 'create article content'));
-
-    $this->drupalLogin($this->web_user);
-    $this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1));
-    $this->drupalLogout();
+    $this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'uid' => $this->web_user->uid));
   }
 
   /**
@@ -162,23 +159,6 @@ class CommentHelperCase extends DrupalWe
   }
 
   /**
-   * Set anonymous comment setting.
-   *
-   * @param boolean $enabled
-   *   Allow anonymous commenting.
-   * @param boolean $without_approval
-   *   Allow anonymous commenting without approval.
-   */
-  function setAnonymousUserComment($enabled, $without_approval) {
-    $edit = array();
-    $edit['1[access comments]'] = $enabled;
-    $edit['1[post comments]'] = $enabled;
-    $edit['1[post comments without approval]'] = $without_approval;
-    $this->drupalPost('admin/user/permissions', $edit, t('Save permissions'));
-    $this->assertText(t('The changes have been saved.'), t('Anonymous user comments ' . ($enabled ? 'enabled' : 'disabled') . '.'));
-  }
-
-  /**
    * Check for contact info.
    *
    * @return boolean Contact info is available.
@@ -373,7 +353,7 @@ class CommentAnonymous extends CommentHe
   function testAnonymous() {
     $this->drupalLogin($this->admin_user);
     // Enabled anonymous user comments.
-    $this->setAnonymousUserComment(TRUE, TRUE);
+    $this->drupalSetPermissions('anonymous user', array('access content', 'access comments', 'post comments', 'post comments without approval'));
     $this->setCommentAnonymous('0'); // Ensure that doesn't require contact info.
     $this->drupalLogout();
 
@@ -433,15 +413,14 @@ class CommentAnonymous extends CommentHe
 
     $this->drupalGet('admin/content/comment');
     $this->assertNoRaw('comments[' . $anonymous_comment3->id . ']', t('Comment was deleted.'));
+    $this->drupalLogout();
 
-    // Reset.
-    $this->drupalLogin($this->admin_user);
-    $this->setAnonymousUserComment(FALSE, FALSE);
+    // Reset permissions.
+    $this->drupalSetPermissions('anonymous user', array('access content'));
 
     // Attempt to view comments while disallowed.
     // NOTE: if authenticated user has permission to post comments, then a
     // "Login or register to post comments" type link may be shown.
-    $this->drupalLogout();
     $this->drupalGet('node/' . $this->node->nid);
     $this->assertNoRaw('<div id="comments">', t('Comments were not displayed.'));
     $this->assertNoLink('Add new comment', t('Link to add comment was found.'));
@@ -470,7 +449,7 @@ class CommentApprovalTest extends Commen
     $this->drupalLogin($this->admin_user);
 
     // Set anonymous comments to require approval.
-    $this->setAnonymousUserComment(TRUE, FALSE);
+    $this->drupalSetPermissions('anonymous user', array('access content', 'access comments', 'post comments'));
     $this->setCommentAnonymous('0'); // Ensure that doesn't require contact info.
     $this->drupalLogout();
 
@@ -504,7 +483,7 @@ class CommentApprovalTest extends Commen
     $this->drupalLogin($this->admin_user);
 
     // Set anonymous comments to require approval.
-    $this->setAnonymousUserComment(TRUE, FALSE);
+    $this->drupalSetPermissions('anonymous user', array('access content', 'access comments', 'post comments'));
     $this->setCommentAnonymous('0'); // Ensure that doesn't require contact info.
     $this->drupalLogout();
 
Index: modules/contact/contact.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/contact/contact.test,v
retrieving revision 1.14
diff -u -p -r1.14 contact.test
--- modules/contact/contact.test	30 Dec 2008 16:43:16 -0000	1.14
+++ modules/contact/contact.test	24 Feb 2009 20:56:12 -0000
@@ -22,7 +22,7 @@ class ContactSitewideTestCase extends Dr
    */
   function testSiteWideContact() {
     // Create and login administrative user.
-    $admin_user = $this->drupalCreateUser(array('administer site-wide contact form', 'administer permissions'));
+    $admin_user = $this->drupalCreateUser(array('administer site-wide contact form'));
     $this->drupalLogin($admin_user);
 
     // Set settings.
@@ -38,7 +38,7 @@ class ContactSitewideTestCase extends Dr
     $this->deleteCategories();
 
     // Ensure that the contact form won't be shown without categories.
-    $this->setPermission('anonymous user', array('access site-wide contact form' => TRUE));
+    $this->drupalSetPermissions('anonymous user', array('access site-wide contact form'));
     $this->drupalLogout();
     $this->drupalGet('contact');
     $this->assertText(t('The contact form has not been configured.'), t('Contact form will not work without categories configured.'));
@@ -73,7 +73,7 @@ class ContactSitewideTestCase extends Dr
     $this->assertRaw(t('Category %category has been updated.', array('%category' => $category)), t('Category successfully updated.'));
 
     // Ensure that the contact form is shown without a category selection input.
-    $this->setPermission('anonymous user', array('access site-wide contact form' => TRUE));
+    $this->drupalSetPermissions('anonymous user', array('access site-wide contact form'));
     $this->drupalLogout();
     $this->drupalGet('contact');
     $this->assertText($contact_form_information, t('Contact form is shown when there is one category.'));
@@ -91,17 +91,14 @@ class ContactSitewideTestCase extends Dr
     $this->assertTrue(db_query('DELETE FROM {flood}'), t('Flood table emptied.'));
 
     // Check to see that anonymous user cannot see contact page without permission.
-    $this->setPermission('anonymous user', array('access site-wide contact form' => FALSE));
+    $this->drupalSetPermissions('anonymous user');
     $this->drupalLogout();
 
     $this->drupalGet('contact');
     $this->assertResponse(403, t('Access denied to anonymous user without permission.'));
 
     // Give anonymous user permission and see that page is viewable.
-    $this->drupalLogin($admin_user);
-    $this->setPermission('anonymous user', array('access site-wide contact form' => TRUE));
-    $this->drupalLogout();
-
+    $this->drupalSetPermissions('anonymous user', array('access site-wide contact form'));
     $this->drupalGet('contact');
     $this->assertResponse(200, t('Access granted to anonymous user with permission.'));
 
@@ -227,29 +224,6 @@ class ContactSitewideTestCase extends Dr
     }
     return $categories;
   }
-
-  /**
-   * Set permission.
-   *
-   * @param string $role User role to set permissions for.
-   * @param array $permissions Key-value array of permissions to set.
-   */
-  function setPermission($role, $permissions) {
-    // Get role id (rid) for specified role.
-    $rid = db_result(db_query("SELECT rid FROM {role} WHERE name = '%s'", array($role)));
-    if ($rid === FALSE) {
-      $this->fail(t(' [permission] Role "' . $role . '" not found.'));
-    }
-
-    // Create edit array from permission.
-    $edit = array();
-    foreach ($permissions as $name => $value) {
-      $edit[$rid . '[' . $name . ']'] = $value;
-    }
-
-    $this->drupalPost('admin/user/permissions', $edit, t('Save permissions'));
-    $this->assertText(t('The changes have been saved.'), t(' [permission] Saved changes.'));
-  }
 }
 
 /**
Index: modules/simpletest/drupal_web_test_case.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/drupal_web_test_case.php,v
retrieving revision 1.86
diff -u -p -r1.86 drupal_web_test_case.php
--- modules/simpletest/drupal_web_test_case.php	22 Feb 2009 20:12:03 -0000	1.86
+++ modules/simpletest/drupal_web_test_case.php	24 Feb 2009 20:56:14 -0000
@@ -651,9 +651,13 @@ class DrupalWebTestCase {
    *   A fully loaded user object with pass_raw property, or FALSE if account
    *   creation fails.
    */
-  protected function drupalCreateUser($permissions = NULL) {
-    // Create a role with the given permission set.
-    if (!($rid = $this->_drupalCreateRole($permissions))) {
+  protected function drupalCreateUser(array $permissions = array('access comments', 'access content', 'post comments', 'post comments without approval')) {
+    // Create a new role and assign permissions ot the new role.
+    $edit = array();
+    $edit['name'] = $this->randomName();
+    $rid = user_role_save($edit);
+    $this->assertTrue($rid, t('Created role of name: @role_name, id: @rid', array('@role_name' => $edit['name'], '@rid' => $rid)), t('Role'));
+    if (!$this->drupalSetPermissions($rid, $permissions)) {
       return FALSE;
     }
 
@@ -678,39 +682,51 @@ class DrupalWebTestCase {
   }
 
   /**
-   * Internal helper function; Create a role with specified permissions.
+   * Assign permissions to a user role.
    *
+   * @param $role
+   *   A string with the role name, or an integer with the role ID.
    * @param $permissions
    *   Array of permission names to assign to role.
+   * @param $merge
+   *   A boolean if TRUE, will only add permissions instead of clearing all
+   *   existing permissions and then adding permissions.
    * @return
-   *   Role ID of newly created role, or FALSE if role creation failed.
+   *   TRUE if the role is a valid role and the permissions have been
+   *   assigned, otherwise FALSE.
    */
-  protected function _drupalCreateRole(array $permissions = NULL) {
-    // Generate string version of permissions list.
-    if ($permissions === NULL) {
-      $permissions = array('access comments', 'access content', 'post comments', 'post comments without approval');
+  protected function drupalSetPermissions($role, array $permissions = array(), $merge = FALSE) {
+    $rid = $role;
+    if (!is_numeric($role)) {
+      $rid = user_role_get_id($role);
+    }
+
+    if (!$rid) {
+      $this->fail(t('Role %role not found.', array('%role' => $role)));
+      return FALSE;
     }
 
+    // Check the all the permissions strings are valid.
     if (!$this->checkPermissions($permissions)) {
       return FALSE;
     }
 
-    // Create new role.
-    $role_name = $this->randomName();
-    db_query("INSERT INTO {role} (name) VALUES ('%s')", $role_name);
-    $role = db_fetch_object(db_query("SELECT * FROM {role} WHERE name = '%s'", $role_name));
-    $this->assertTrue($role, t('Created role of name: @role_name, id: @rid', array('@role_name' => $role_name, '@rid' => (isset($role->rid) ? $role->rid : t('-n/a-')))), t('Role'));
-    if ($role && !empty($role->rid)) {
-      // Assign permissions to role and mark it for clean-up.
-      foreach ($permissions as $permission_string) {
-        db_query("INSERT INTO {role_permission} (rid, permission) VALUES (%d, '%s')", $role->rid, $permission_string);
-      }
-      $count = db_result(db_query("SELECT COUNT(*) FROM {role_permission} WHERE rid = %d", $role->rid));
-      $this->assertTrue($count == count($permissions), t('Created permissions: @perms', array('@perms' => implode(', ', $permissions))), t('Role'));
-      return $role->rid;
+    // Check if there are new permissions to add.
+    $permissions_before = db_query("SELECT permission FROM {role_permission} WHERE rid = :rid", array(':rid' => $rid))->fetchCol();
+    $count_before = count($permissions_before);
+    $permissions_diff = array_diff($permissions, $permissions_before);
+
+    if ($permissions_diff) {
+      // Assign the user permissions.
+      user_role_set_permissions($rid, $permissions, $merge);
+      $count_after = db_query("SELECT COUNT(*) FROM {role_permission} WHERE rid = :rid", array(':rid' => $rid))->fetchField();
+      $result = $count_before + count($permissions_diff) == $count_after;
+      $this->assertTrue($result, t('Assigned permissions to role %role: @perms', array('%role' => $role, '@perms' => implode(', ', $permissions_diff))), t('Role'));
+      return $result;
     }
     else {
-      return FALSE;
+      $this->pass(t('No new permissions to assign to role %role.', array('%role' => $role)), t('Role'));
+      return TRUE;
     }
   }
 
@@ -2054,4 +2070,3 @@ class DrupalWebTestCase {
     return $instance;
   }
 }
-
Index: modules/simpletest/simpletest.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.test,v
retrieving revision 1.16
diff -u -p -r1.16 simpletest.test
--- modules/simpletest/simpletest.test	22 Jan 2009 12:46:06 -0000	1.16
+++ modules/simpletest/simpletest.test	24 Feb 2009 20:56:14 -0000
@@ -13,6 +13,8 @@ class SimpleTestFunctionalTest extends D
    */
   protected $test_ids = array();
 
+  private $test_user;
+
   function getInfo() {
     return array(
       'name' => t('SimpleTest functionality'),
@@ -108,7 +110,8 @@ class SimpleTestFunctionalTest extends D
     $this->pass($this->pass);
     $this->fail($this->fail);
 
-    $this->drupalCreateUser(array($this->valid_permission));
+    $account = $this->drupalCreateUser(array($this->valid_permission));
+    $this->drupalSetPermissions(end($account->roles), array($this->valid_permission));
     $this->drupalCreateUser(array($this->invalid_permission));
 
     $this->pass(t('Test ID is @id.', array('@id' => $this->testId)));
@@ -137,7 +140,9 @@ class SimpleTestFunctionalTest extends D
     $this->assertAssertion($this->pass, 'Other', 'Pass', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()');
     $this->assertAssertion($this->fail, 'Other', 'Fail', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()');
 
-    $this->assertAssertion(t('Created permissions: @perms', array('@perms' => $this->valid_permission)), 'Role', 'Pass', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()');
+    $this->assertAssertion(t('Assigned permissions to role'), 'Role', 'Pass', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()');
+    $this->assertAssertion(t('@perms', array('@perms' => $this->valid_permission)), 'Role', 'Pass', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()');
+    $this->assertAssertion(t('No new permissions to assign to role'), 'Role', 'Pass', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()');
     $this->assertAssertion(t('Invalid permission %permission.', array('%permission' => $this->invalid_permission)), 'Role', 'Fail', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()');
 
     // Check that a warning is catched by simpletest.
Index: modules/system/system.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.install,v
retrieving revision 1.308
diff -u -p -r1.308 system.install
--- modules/system/system.install	18 Feb 2009 15:19:56 -0000	1.308
+++ modules/system/system.install	24 Feb 2009 20:56:17 -0000
@@ -354,19 +354,10 @@ function system_install() {
   // This sets uid 1 (superuser). We skip uid 2 but that's not a big problem.
   db_query("UPDATE {user} SET uid = 1 WHERE name = '%s'", 'placeholder-for-uid-1');
 
-  // Built-in roles.
+  // Add built-in roles.
   db_query("INSERT INTO {role} (name) VALUES ('%s')", 'anonymous user');
   db_query("INSERT INTO {role} (name) VALUES ('%s')", 'authenticated user');
 
-  // Anonymous role permissions.
-  db_query("INSERT INTO {role_permission} (rid, permission) VALUES (%d, '%s')", 1, 'access content');
-
-  // Authenticated role permissions.
-  db_query("INSERT INTO {role_permission} (rid, permission) VALUES (%d, '%s')", 2, 'access comments');
-  db_query("INSERT INTO {role_permission} (rid, permission) VALUES (%d, '%s')", 2, 'access content');
-  db_query("INSERT INTO {role_permission} (rid, permission) VALUES (%d, '%s')", 2, 'post comments');
-  db_query("INSERT INTO {role_permission} (rid, permission) VALUES (%d, '%s')", 2, 'post comments without approval');
-
   db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", 'theme_default', 's:7:"garland";');
   db_query("UPDATE {system} SET status = %d WHERE type = '%s' AND name = '%s'", 1, 'theme', 'garland');
 
Index: modules/user/user.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.admin.inc,v
retrieving revision 1.39
diff -u -p -r1.39 user.admin.inc
--- modules/user/user.admin.inc	18 Feb 2009 15:19:57 -0000	1.39
+++ modules/user/user.admin.inc	24 Feb 2009 20:56:18 -0000
@@ -544,17 +544,17 @@ function user_admin_settings() {
 function user_admin_perm($form_state, $rid = NULL) {
 
   // Retrieve role names for columns.
-  $role_names = user_roles();
+  $roles = user_roles();
   if (is_numeric($rid)) {
-    $role_names = array($rid => $role_names[$rid]);
+    $roles = array($rid => $roles[$rid]);
   }
   // Fetch permissions for all roles or the one selected role.
-  $role_permissions = user_role_permissions($role_names);
+  $role_permissions = user_role_get_permissions(array_keys($roles));
 
   // Store $role_names for use when saving the data.
-  $form['role_names'] = array(
+  $form['roles'] = array(
     '#type' => 'value',
-    '#value' => $role_names,
+    '#value' => $roles,
   );
   // Render role/permission overview:
   $options = array();
@@ -572,7 +572,7 @@ function user_admin_perm($form_state, $r
           '#markup' => $perm_item['title'],
           '#description' => $hide_descriptions ? $perm_item['description'] : NULL,
         );
-        foreach ($role_names as $rid => $name) {
+        foreach ($roles as $rid => $name) {
           // Builds arrays for checked boxes for each role
           if (isset($role_permissions[$rid][$perm])) {
             $status[$rid][] = $perm;
@@ -583,7 +583,7 @@ function user_admin_perm($form_state, $r
   }
 
   // Have to build checkboxes here after checkbox arrays are built
-  foreach ($role_names as $rid => $name) {
+  foreach ($roles as $rid => $name) {
     $form['checkboxes'][$rid] = array('#type' => 'checkboxes', '#options' => $options, '#default_value' => isset($status[$rid]) ? $status[$rid] : array());
     $form['role_names'][$rid] = array('#markup' => $name, '#tree' => TRUE);
   }
@@ -598,13 +598,9 @@ function user_admin_perm($form_state, $r
  * @see user_admin_perm
  */
 function user_admin_perm_submit($form, &$form_state) {
-  foreach ($form_state['values']['role_names'] as $rid => $name) {
-    $checked = array_filter($form_state['values'][$rid]);
-    // Delete existing permissions for the role. This handles "unchecking" checkboxes.
-    db_query("DELETE FROM {role_permission} WHERE rid = %d", $rid);
-    foreach ($checked as $permission) {
-      db_query("INSERT INTO {role_permission} (rid, permission) VALUES (%d, '%s')", $rid, $permission);
-    }
+  foreach (array_keys($form_state['values']['roles']) as $rid) {
+    $permissions = array_filter($form_state['values'][$rid]);
+    user_role_set_permissions($rid, $permissions);
   }
 
   drupal_set_message(t('The changes have been saved.'));
@@ -710,14 +706,15 @@ function user_admin_role() {
 }
 
 function user_admin_role_validate($form, &$form_state) {
+  $form_state['values']['name'] = trim($form_state['values']['name']);
   if ($form_state['values']['name']) {
     if ($form_state['values']['op'] == t('Save role')) {
-      if (db_result(db_query("SELECT COUNT(*) FROM {role} WHERE name = '%s' AND rid != %d", $form_state['values']['name'], $form_state['values']['rid']))) {
+      if (($rid = user_role_get_id($form_state['values']['name'])) && $rid != $form_state['values']['rid']) {
         form_set_error('name', t('The role name %name already exists. Please choose another role name.', array('%name' => $form_state['values']['name'])));
       }
     }
     elseif ($form_state['values']['op'] == t('Add role')) {
-      if (db_result(db_query("SELECT COUNT(*) FROM {role} WHERE name = '%s'", $form_state['values']['name']))) {
+      if (user_role_get_id($form_state['values']['name'])) {
         form_set_error('name', t('The role name %name already exists. Please choose another role name.', array('%name' => $form_state['values']['name'])));
       }
     }
@@ -729,19 +726,15 @@ 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']);
+    user_role_save($form_state['values']);
     drupal_set_message(t('The role has been renamed.'));
   }
   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:
-    db_query('DELETE FROM {user_role} WHERE rid = %d', $form_state['values']['rid']);
-
+    user_role_delete($form_state['values']['rid']);
     drupal_set_message(t('The role has been deleted.'));
   }
   elseif ($form_state['values']['op'] == t('Add role')) {
-    db_query("INSERT INTO {role} (name) VALUES ('%s')", $form_state['values']['name']);
+    user_role_save($form_state['values']);
     drupal_set_message(t('The role has been added.'));
   }
   $form_state['redirect'] = 'admin/user/roles';
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.965
diff -u -p -r1.965 user.module
--- modules/user/user.module	19 Feb 2009 12:09:30 -0000	1.965
+++ modules/user/user.module	24 Feb 2009 20:56:20 -0000
@@ -535,59 +535,6 @@ function user_password($length = 10) {
 }
 
 /**
- * Determine the permissions for one or more roles.
- *
- * @param $roles
- *   An array whose keys are the role IDs of interest, such as $user->roles.
- * @param $reset
- *   Optional parameter - if TRUE data in the static variable is rebuilt.
- *
- * @return
- *   An array indexed by role ID. Each value is an array whose keys are the
- *   permission strings for the given role ID.
- */
-function user_role_permissions($roles = array(), $reset = FALSE) {
-  static $stored_permissions = array();
-
-  if ($reset) {
-    // Clear the data cached in the static variable.
-    $stored_permissions = array();
-  }
-
-  $role_permissions = $fetch = array();
-
-  if ($roles) {
-    foreach ($roles as $rid => $name) {
-      if (isset($stored_permissions[$rid])) {
-        $role_permissions[$rid] = $stored_permissions[$rid];
-      }
-      else {
-        // Add this rid to the list of those needing to be fetched.
-        $fetch[] = $rid;
-        // Prepare in case no permissions are returned.
-        $stored_permissions[$rid] = array();
-      }
-    }
-
-    if ($fetch) {
-      // Get from the database permissions that were not in the static variable.
-      // Only role IDs with at least one permission assigned will return rows.
-      $result = db_query("SELECT rid, permission FROM {role_permission} WHERE rid IN (:fetch)", array(':fetch' => $fetch));
-
-      foreach ($result as $row) {
-        $stored_permissions[$row->rid][$row->permission] = TRUE;
-      }
-      foreach ($fetch as $rid) {
-        // For every rid, we know we at least assigned an empty array.
-        $role_permissions[$rid] = $stored_permissions[$rid];
-      }
-    }
-  }
-
-  return $role_permissions;
-}
-
-/**
  * Determine whether the user has a given privilege.
  *
  * @param $string
@@ -626,7 +573,7 @@ function user_access($string, $account =
   // To reduce the number of SQL queries, we cache the user's permissions
   // in a static variable.
   if (!isset($perm[$account->uid])) {
-    $role_permissions = user_role_permissions($account->roles, $reset);
+    $role_permissions = user_role_get_permissions(array_keys($account->roles), $reset);
 
     $perms = array();
     foreach ($role_permissions as $one_role) {
@@ -2784,3 +2731,167 @@ function _user_forms(&$edit, $account, $
   return empty($groups) ? FALSE : $groups;
 }
 
+/**
+ * Get the role ID of a role name.
+ *
+ * @param $role_name
+ *   The name of the role.
+ * @return
+ *   An integer of the role's ID, or FALSE if the role was not found.
+ */
+function user_role_get_id($role_name) {
+  return db_query("SELECT rid FROM {role} WHERE name = :name", array(':name' => $role_name))->fetchField();
+}
+
+/**
+ * Save changes to a user role or add a new role.
+ *
+ * @param $role
+ *   The role object to modify or add. If $role->rid is unspecified, a new
+ *   role will be created.
+ * @param $permissions
+ *   An optional array of permissions to assign to the role.
+ * @return
+ *   The role ID of the role.
+ */
+function user_role_save($role, array $permissions = NULL) {
+  $role = (object) $role;
+
+  // Save the role to the database.
+  drupal_write_record('role', $role, !empty($role->rid) ? 'rid' : array());
+
+  if (isset($permissions)) {
+    // Assign the permissions to the role.
+    user_role_set_permissions($role->rid, $permissions);
+  }
+
+  return $role->rid;
+}
+
+/**
+ * Delete a user role.
+ *
+ * @param $role
+ *   A string with the role name, or an integer with the role ID.
+ * @return
+ *   TRUE on success, FALSE otherwise.
+ */
+function user_role_delete($role) {
+  $rid = $role;
+  if (!is_numeric($role)) {
+    // Fetch the role ID if $role is a role name.
+    $rid = user_role_get_id($role);
+  }
+
+  if (!$rid) {
+    return FALSE;
+  }
+
+  db_delete('role')->condition('rid', $rid)->execute();
+  db_delete('role_permission')->condition('rid', $rid)->execute();
+  db_delete('user_role')->condition('rid', $rid)->execute();
+
+  // Clear the user access cache.
+  user_access(NULL, NULL, TRUE);
+
+  return TRUE;
+}
+
+/**
+ * Determine the permissions for one or more roles.
+ *
+ * @param $roles
+ *   An array of role IDs.
+ * @param $reset
+ *   Optional parameter - if TRUE data in the static variable is rebuilt.
+ * @return
+ *   An array indexed by role ID. Each value is an array whose keys are the
+ *   permission strings for the given role ID.
+ */
+function user_role_get_permissions(array $roles = array(), $reset = FALSE) {
+  static $stored_permissions = array();
+
+  if ($reset) {
+    // Clear the data cached in the static variable.
+    $stored_permissions = array();
+  }
+
+  $role_permissions = $fetch = array();
+
+  if ($roles) {
+    foreach ($roles as $rid) {
+      if (isset($stored_permissions[$rid])) {
+        $role_permissions[$rid] = $stored_permissions[$rid];
+      }
+      else {
+        // Add this rid to the list of those needing to be fetched.
+        $fetch[] = $rid;
+        // Prepare in case no permissions are returned.
+        $stored_permissions[$rid] = array();
+      }
+    }
+
+    if ($fetch) {
+      // Get from the database permissions that were not in the static variable.
+      // Only role IDs with at least one permission assigned will return rows.
+      $result = db_query("SELECT rid, permission FROM {role_permission} WHERE rid IN (:fetch)", array(':fetch' => $fetch));
+
+      foreach ($result as $row) {
+        $stored_permissions[$row->rid][$row->permission] = TRUE;
+      }
+      foreach ($fetch as $rid) {
+        // For every rid, we know we at least assigned an empty array.
+        $role_permissions[$rid] = $stored_permissions[$rid];
+      }
+    }
+  }
+
+  return $role_permissions;
+}
+
+/**
+ * Assign an array of permissions to a role.
+ *
+ * @param $role
+ *   A string with the role name, or an integer with the role ID.
+ * @param $permissions
+ *   An array of permissions strings.
+ * @param $merge
+ *   A boolean if TRUE, will only add permissions instead of clearing all
+ *   existing permissions and then adding permissions.
+ * @return
+ *   TRUE on success, FALSE otherwise.
+ */
+function user_role_set_permissions($role, array $permissions = array(), $merge = FALSE) {
+  $rid = $role;
+  if (!is_numeric($role)) {
+    // Fetch the role ID if $role is a role name.
+    $rid = user_role_get_id($role);
+  }
+
+  if (!$rid) {
+    return FALSE;
+  }
+
+  if (!$merge) {
+    // Delete existing permissions for the role.
+    db_delete('role_permission')
+      ->condition('rid', $rid)
+      ->execute();
+  }
+
+  // Assign the new permissions for the role.
+  foreach ($permissions as $permission_string) {
+    db_merge('role_permission')
+      ->key(array(
+        'rid' => $rid,
+        'permission' => $permission_string,
+      ))
+      ->execute();
+  }
+
+  // Clear the user access cache.
+  user_access(NULL, NULL, TRUE);
+
+  return TRUE;
+}
Index: modules/user/user.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.test,v
retrieving revision 1.29
diff -u -p -r1.29 user.test
--- modules/user/user.test	22 Feb 2009 17:55:30 -0000	1.29
+++ modules/user/user.test	24 Feb 2009 20:56:21 -0000
@@ -699,55 +699,98 @@ class UserPictureTestCase extends Drupal
   }
 }
 
-
-class UserPermissionsTestCase extends DrupalWebTestCase {
-  protected $admin_user;
-  protected $rid;
-
+/**
+ * Test the user role and permission functionality.
+ */
+class UserRolesPermissionsFunctionalTest extends DrupalWebTestCase {
   function getInfo() {
     return array(
-      'name' => t('Role permissions'),
-      'description' => t('Verify that role permissions can be added and removed via the permissions page.'),
-      'group' => t('User')
+      'name' => t('User roles and permissions'),
+      'description' => t('Test the user roles and permissions functionality.'),
+      'group' => t('User'),
     );
   }
 
-  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);
-  }
-
   /**
-   * Change user permissions and check user_access().
+   * Test user role and permission interfaces.
    */
-  function testUserPermissionChanges() {
-    $this->drupalLogin($this->admin_user);
-    $rid = $this->rid;
-    $account = $this->admin_user;
+  function testRolesAndPermissions() {
+    $test_user = $this->drupalCreateUser();
+    $admin_user = $this->drupalCreateUser(array('administer permissions', 'administer users'));
+    $this->drupalLogin($admin_user);
+    $perm = 'access user profiles';
+
+    // Add a new user role and fetch it's role ID.
+    $role = $this->randomName(4, 'role_');
+    $this->drupalPost('admin/user/roles', array('name' => $role), t('Add role'));
+    $this->assertText(t('The role has been added.'));
+    $role_id = user_role_get_id($role);
+
+    // Try to add another role with duplicate names.
+    $this->drupalPost('admin/user/roles', array('name' => 'anonymous user'), t('Add role'));
+    $this->assertRaw(t('The role name %role already exists. Please choose another role name.', array('%role' => 'anonymous user')), t('Could not add a duplicate role name.'));
+    $this->drupalPost('admin/user/roles/edit/'. $role_id, array('name' => 'anonymous user'), t('Save role'));
+    $this->assertRaw(t('The role name %role already exists. Please choose another role name.', array('%role' => 'anonymous user')), t('Could not add a duplicate role name.'));
+
+    // Try to add an invalid role name.
+    $this->drupalPost('admin/user/roles', array('name' => ' '), t('Add role'));
+    $this->assertText(t('You must specify a valid role name.'));
+
+    // Try to edit the locked anonymous and authenticated user roles.
+    foreach (array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID) as $invalid_rid) {
+      $this->drupalGet('admin/user/roles/edit/' . $invalid_rid);
+      $this->assertEqual(url('admin/user/roles', array('absolute' => TRUE)), $this->getUrl(), t('Cannot edit locked user role.'));
+    }
+
+    // Rename the new role.
+    $this->clickLink(t('edit role'), 0);
+    $role = $this->randomName(4, 'role_');
+    $this->drupalPost(NULL, array('name' => $role), t('Save role'));
+    $this->assertText(t('The role has been renamed.'));
+    $this->assertEqual($role_id, user_role_get_id($role), t('Role ID unchanged.'));
 
-    // Add a permission.
-    $this->assertFalse(user_access('administer nodes', $account, TRUE), t('User does not have "administer nodes" permission.'));
+    // Add a permission to the new role.
+    $this->clickLink(t('edit permissions'), 2);
     $edit = array();
-    $edit[$rid . '[administer nodes]'] = TRUE;
+    $edit[$role_id . '[' . $perm . ']'] = TRUE;
     $this->drupalPost('admin/user/permissions', $edit, t('Save permissions'));
-    $this->assertText(t('The changes have been saved.'), t('Successful save message displayed.'));
-    $this->assertTrue(user_access('administer nodes', $account, TRUE), t('User now has "administer nodes" permission.'));
+    $this->assertText(t('The changes have been saved.'));
 
-    // Remove a permission.
-    $this->assertTrue(user_access('access user profiles', $account, TRUE), t('User has "access user profiles" permission.'));
+    // Assign a user to the new role.
+    $this->drupalGet('user/' . $test_user->uid . '/edit');
+    $this->assertFieldByXPath('//input[@type="checkbox" and @name="roles[' . DRUPAL_AUTHENTICATED_RID . ']" and @disabled="disabled" and @checked="checked"]', TRUE, t('The authenticated user role checkbox is checked but disabled.'));
     $edit = array();
-    $edit[$rid . '[access user profiles]'] = FALSE;
+    // For some reason, the FormAPI will show an 'invalid choice selected'
+    // without the following line, so it is needed to complete the POST.
+    $edit['roles[' . DRUPAL_AUTHENTICATED_RID . ']'] = FALSE;
+    $edit['roles[' . $role_id . ']'] = TRUE;
+    $this->drupalPost(NULL, $edit, t('Save'));
+    $this->assertText(t('The changes have been saved.'));
+
+    // Test that the user has the assigned role permissions.
+    $test_user = user_load($test_user->uid);
+    $this->assertTrue(user_access($perm, $test_user, TRUE), t('User has "access user profiles" permission.'));
+
+    // Delete the role and reload the user since it's roles have changed.
+    $this->drupalPost('admin/user/roles/edit/' . $role_id, array(), t('Delete role'));
+    $this->assertText(t('The role has been deleted.'));
+    $test_user = user_load($test_user->uid);
+
+    // Test that the user no longer has the assigned role permission.
+    $this->assertFalse(user_access($perm, $test_user, TRUE), t('User does not have "access user profiles" permission.'));
+    $this->assertFalse(user_access($perm, drupal_anonymous_user()), t('Anonymous user does not have "access user profiles" permission.'));
+
+    // Assign a permission to the anonymous and authenticated user role.
+    $edit = array();
+    $edit[DRUPAL_ANONYMOUS_RID . '[' . $perm . ']'] = TRUE;
+    $edit[DRUPAL_AUTHENTICATED_RID . '[' . $perm . ']'] = TRUE;
     $this->drupalPost('admin/user/permissions', $edit, t('Save permissions'));
-    $this->assertText(t('The changes have been saved.'), t('Successful save message displayed.'));
-    $this->assertFalse(user_access('access user profiles', $account, TRUE), t('User no longer has "access user profiles" permission.'));
-  }
+    $this->assertText(t('The changes have been saved.'));
 
+    // Test that both anonymous and authenticated users have the permission.
+    $this->assertTrue(user_access($perm, $test_user, TRUE), t('User has "access user profiles" permission.'));
+    $this->assertTrue(user_access($perm, drupal_anonymous_user()), t('Anonymous user has "access user profiles" permission.'));
+  }
 }
 
 class UserAdminTestCase extends DrupalWebTestCase {
Index: profiles/default/default.profile
===================================================================
RCS file: /cvs/drupal/drupal/profiles/default/default.profile,v
retrieving revision 1.37
diff -u -p -r1.37 default.profile
--- profiles/default/default.profile	3 Feb 2009 12:30:14 -0000	1.37
+++ profiles/default/default.profile	24 Feb 2009 20:56:21 -0000
@@ -90,7 +90,11 @@ function default_profile_task_list() {
  *   modify the $task, otherwise discarded.
  */
 function default_profile_tasks(&$task, $url) {
-  
+
+  // Enable default permissions.
+  user_role_set_permissions(DRUPAL_ANONYMOUS_RID, array('access content'));
+  user_role_set_permissions(DRUPAL_AUTHENTICATED_RID, array('access content', 'access comments', 'post comments', 'post comments without approval'));
+
   // Enable 3 standard blocks.
   db_query("INSERT INTO {block} (module, delta, theme, status, weight, region, pages, cache) VALUES ('%s', '%s', '%s', %d, %d, '%s', '%s', %d)", 'user', 'login', 'garland', 1, 0, 'left', '', -1);
   db_query("INSERT INTO {block} (module, delta, theme, status, weight, region, pages, cache) VALUES ('%s', '%s', '%s', %d, %d, '%s', '%s', %d)", 'user', 'navigation', 'garland', 1, 0, 'left', '', -1);
