diff --git a/taxonomy_access.admin.inc b/taxonomy_access.admin.inc
index bd2ac47..f203dca 100644
--- a/taxonomy_access.admin.inc
+++ b/taxonomy_access.admin.inc
@@ -21,10 +21,10 @@ function taxonomy_access_admin() {
   $roles = _taxonomy_access_user_roles();
   $active_rids = db_query(
     'SELECT rid FROM {taxonomy_access_default} WHERE vid = :vid',
-    array(':vid' => TAXONOMY_ACCESS_GLOBAL_DEFAULT))
-    ->fetchCol();
+    array(':vid' => TAXONOMY_ACCESS_GLOBAL_DEFAULT)
+  )->fetchCol();
 
-  $header = array(t('Role'), t('Status'), array('data' => t('Operations'), 'colspan' => 2));
+  $header = array(t('Role'), t('Status'), t('Operations'));
   $rows = array();
 
   foreach ($roles as $rid => $name) {
@@ -34,23 +34,16 @@ function taxonomy_access_admin() {
     if (in_array($rid, $active_rids)) {
       // Add edit operation link for active roles.
       $row[] = array('data' => t('Enabled'));
-      $row[] = array('data' => l(t("edit access rules"), TAXONOMY_ACCESS_CONFIG . "/role/$rid/edit"));
 
-      // Add disable operation link for custom roles.
-      if ($rid != DRUPAL_ANONYMOUS_RID && $rid != DRUPAL_AUTHENTICATED_RID) {
-        $row[] = array('data' => l(t("disable access control"), TAXONOMY_ACCESS_CONFIG . "/role/$rid/delete"));
-      }
-      else {
-        $row[] = array('data' => '');
-      }
     }
     else {
       // Add enable link for unconfigured roles.
       $row[] = array('data' => t('Disabled'));
-      $row[] = array('data' => '');
-      $row[] = array('data' => l(t("enable access control"), TAXONOMY_ACCESS_CONFIG . "/role/$rid/edit"));
     }
-
+    $row[] = array('data' => l(
+      t("Configure"),
+      TAXONOMY_ACCESS_CONFIG . "/role/$rid/edit",
+      array('attributes' => array('class' => array('module-link', 'module-link-configure')))));
     $rows[] = $row;
   }
 
@@ -71,26 +64,21 @@ function taxonomy_access_admin() {
  * @param int $rid
  *   The role ID to disable.
  *
- * @see taxonomy_access_admin_delete_role_submit()
+ * @see taxonomy_access_role_delete_confirm_submit()
  * @see taxonomy_access_menu()
+ * @ingroup forms
  */
-function taxonomy_access_admin_delete_role($form, &$form_state, $rid) {
+function taxonomy_access_role_delete_confirm($form, &$form_state, $rid) {
   $roles = _taxonomy_access_user_roles();
-  $role_global_default = db_query(
-    'SELECT grant_view, grant_update, grant_delete
-     FROM {taxonomy_access_default}
-     WHERE vid = :vid AND rid = :rid', array(':vid' => TAXONOMY_ACCESS_GLOBAL_DEFAULT, ':rid' => $rid))
-    ->fetchAssoc();
-  if ($role_global_default) {
+  if (taxonomy_access_role_enabled($rid)) {
     $form['rid'] = array(
       '#type' => 'value',
       '#value' => $rid,
     );
-
     return confirm_form($form,
-      t('Are you sure you want to delete all taxonomy access rules for the role %role?',
+      t("Are you sure you want to delete all taxonomy access rules for the role %role?",
         array('%role' => $roles[$rid])),
-      TAXONOMY_ACCESS_CONFIG,
+      TAXONOMY_ACCESS_CONFIG . '/role/%/edit',
       t('This action cannot be undone.'),
       t('Delete all'),
       t('Cancel'));
@@ -98,14 +86,9 @@ function taxonomy_access_admin_delete_role($form, &$form_state, $rid) {
 }
 
 /**
- * Form submission handler for taxonomy_access_admin_delete_role.
- *
- * @param int $rid
- *   The role ID to disable.
- *
- * @see taxonomy_access_admin_delete_role()
+ * Form submission handler for taxonomy_role_delete_confirm().
  */
-function taxonomy_access_admin_delete_role_submit($form, &$form_state) {
+function taxonomy_access_role_delete_confirm_submit($form, &$form_state) {
   $roles = _taxonomy_access_user_roles();
   $rid = $form_state['values']['rid'];
   if (is_numeric($rid)
@@ -121,32 +104,57 @@ function taxonomy_access_admin_delete_role_submit($form, &$form_state) {
 }
 
 /**
- * Assembles a row of grant options for a term or default on the admin form.
+ * Generates a URL to enable a role with a token for CSRF protection.
  *
- * @param array $grants
- *   (optional) An array of grants to use as form defaults.  Defaults to NULL.
+ * @param int $rid
+ *   The role ID.
+ *
+ * @return string
+ *   The full URL for the request path.
  */
-function taxonomy_access_admin_build_row(array $grants = NULL) {
-  $form['#tree'] = TRUE;
-  foreach (array('view', 'update', 'delete') as $grant) {
-    $form[$grant] = array(
-      '#type' => 'select',
-      '#options' => array(
-        TAXONOMY_ACCESS_NODE_ALLOW => 'Allow',
-        TAXONOMY_ACCESS_NODE_IGNORE => 'Ignore',
-        TAXONOMY_ACCESS_NODE_DENY => 'Deny'),
-      '#default_value' => is_string($grants['grant_' . $grant]) ? $grants['grant_' . $grant] : TAXONOMY_ACCESS_NODE_IGNORE,
-      '#required' => TRUE,
-    );
+function taxonomy_access_enable_role_url($rid) {
+  // Create a query array with a token to validate the sumbission.
+  $query = drupal_get_destination();
+  $query['token'] = drupal_get_token($rid);
+
+  // Build and return the URL with the token and destination.
+  return url(
+    TAXONOMY_ACCESS_CONFIG . "/role/$rid/enable",
+    array('query' => $query)
+  );
+}
+
+/**
+ * Page callback: Enables a role if the proper token is provided.
+ *
+ * Path: TAXONOMY_ACCESS_CONFIG . /role/%/enable
+ *
+ * @param int $rid
+ *   The role ID.
+ */
+function taxonomy_access_enable_role_validate($rid) {
+  $rid = intval($rid);
+  // If a valid token is not provided, return a 403.
+  $query = drupal_get_query_parameters();
+  if (empty($query['token']) || !drupal_valid_token($query['token'], $rid)) {
+    return MENU_ACCESS_DENIED;
   }
-  foreach (array('create', 'list') as $grant) {
-    $form[$grant] = array(
-      '#type' => 'select',
-      '#options' => array(TAXONOMY_ACCESS_TERM_ALLOW => 'Allow', TAXONOMY_ACCESS_TERM_DENY => 'Deny'),
-      '#default_value' => is_string($grants['grant_' . $grant]) ? $grants['grant_' . $grant] : TAXONOMY_ACCESS_TERM_DENY,
-    );
+  // Return a 404 for the anonymous or authenticated roles.
+  if (in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
+    return MENU_NOT_FOUND;
   }
-  return $form;
+  // Return a 404 for invalid role IDs.
+  $roles = _taxonomy_access_user_roles();
+  if (empty($roles[$rid])) {
+    return MENU_NOT_FOUND;
+  }
+
+  // If the parameters pass validation, enable the role and complete redirect.
+  if (taxonomy_access_enable_role($rid)) {
+    drupal_set_message(t('Role %name enabled successfully.',
+      array('%name' => $roles[$rid])));
+  }
+  drupal_goto();
 }
 
 /**
@@ -159,308 +167,623 @@ function taxonomy_access_admin_build_row(array $grants = NULL) {
  *
  * @see taxonomy_access_admin_form_submit()
  * @see taxonomy_access_menu()
+ * @ingroup forms
  */
-function taxonomy_access_admin_form($form, $form_state, $rid) {
-  // Disable list filtering while preparing this form.
-  taxonomy_access_disable_list();
-
-  $roles = _taxonomy_access_user_roles();
-
-  // Fetch all default grants.
-  $default_grants = db_query('SELECT * FROM {taxonomy_access_default} WHERE rid = :rid', array(':rid' => $rid))->fetchAllAssoc('vid', PDO::FETCH_ASSOC);
-
-  // If we are adding a role, no global default is set yet, so insert it now.
-  if (empty($default_grants[0]) && isset($rid)) {
-    // Assemble a $row object for Schema API.
-    $row = new stdClass();
-    $row->vid = TAXONOMY_ACCESS_GLOBAL_DEFAULT;
-    $row->rid = $rid;
-
-    // Insert the row with defaults for all grants.
-    drupal_write_record('taxonomy_access_default', $row);
-
-    drupal_set_message(t('Access control for role %role enabled.',
-      array('%role' => $roles[$rid])));
-  }
-
-  // Fetch all grants.
-  $grants = db_query('SELECT * FROM {taxonomy_access_term} WHERE rid = :rid', array(':rid' => $rid))->fetchAllAssoc('tid', PDO::FETCH_ASSOC);
-
-  $form['instructions'] = array(
-    '#markup' => _taxonomy_access_admin_instructions_html(),
-    '#weight' => '20',
-  );
+function taxonomy_access_admin_role($form, $form_state, $rid) {
+  // Always include the role ID in the form.
+  $rid = intval($rid);
   $form['rid'] = array('#type' => 'value', '#value' => $rid);
-  $form['grants'] = $form['selected_terms'] = $form['selected_defaults'] = array('#tree' => TRUE);
-
-  // Global default.
-  $global_defaults = empty($default_grants[0]) ? NULL : $default_grants[0];
-  $form['vocabs'][0]['#title'] = 'Global';
-  $form['grants'][0][0] = taxonomy_access_admin_build_row($global_defaults);
-  $form['selected_defaults'][0] = array(
-     '#type' => 'checkbox',
-     '#disabled' => TRUE,
-     '#title' => t('<em>default</em>'),
-     '#description' => t("can't be disabled without disabling TAC for this role"),
-  );
 
-  foreach (taxonomy_get_vocabularies() as $vid => $vocabulary) {
-    $form['vocabs'][$vid]['#title'] = check_plain($vocabulary->name);
-    if (isset($default_grants[$vid])) {
-      $form['grants'][$vid][0] = taxonomy_access_admin_build_row($default_grants[$vid]);
-      $form['selected_defaults'][$vid] = array(
-         '#type' => 'checkbox',
-         '#title' => t('<em>default</em>'),
+  // For custom roles, allow the user to enable or disable grants for the role.
+  if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
+    $roles = _taxonomy_access_user_roles();
+
+    // If the role is not enabled, return only a link to enable it.
+    if (!taxonomy_access_role_enabled($rid)) {
+      $form['status'] = array(
+        '#markup' => '<p>' . t(
+          'Access control for the %name role is disabled. <a href="@url">Enable @name</a>.',
+          array(
+            '%name' => $roles[$rid],
+            '@name' => $roles[$rid],
+            '@url' => taxonomy_access_enable_role_url($rid))) . '</p>'
       );
+      return $form;
     }
+    // Otherwise, add a link to disable and build the rest of the form.
     else {
-      $add_items[$vocabulary->name]["default $vid"] = t('*default*');
+      $disable_url = url(
+        TAXONOMY_ACCESS_CONFIG . "/role/$rid/delete",
+        array('query' => drupal_get_destination())
+      );
+      $form['status'] = array(
+        '#markup' => '<p>' . t(
+          'Access control for the %name role is enabled. <a href="@url">Disable @name</a>.',
+          array('@name' => $roles[$rid], '%name' => $roles[$rid], '@url' => $disable_url)) . '</p>'
+      );
     }
+  }
 
-    if ($tree = taxonomy_get_tree($vid)) {
-      foreach ($tree as $term) {
-        if (isset($grants[$term->tid])) {
-          $form['grants'][$vid][$term->tid] = taxonomy_access_admin_build_row($grants[$term->tid]);
-          $form['selected_terms'][$term->tid] = array(
-            '#type' => 'checkbox',
-            '#title' => str_repeat('&nbsp;&nbsp;', $term->depth) . check_plain($term->name),
-          );
-        }
-        else {
-          $add_items[$vocabulary->name]["term $term->tid"] = str_repeat('-', $term->depth) . check_plain($term->name);
-        }
-      }
+  // Retrieve role grants and display an administration form.
+  // Disable list filtering while preparing this form.
+  taxonomy_access_disable_list();
+
+  // Fetch all grants for the role.
+  $defaults =
+    db_query(
+      'SELECT vid, grant_view, grant_update, grant_delete, grant_create,
+              grant_list
+       FROM {taxonomy_access_default}
+       WHERE rid = :rid',
+      array(':rid' => $rid))
+    ->fetchAllAssoc('vid', PDO::FETCH_ASSOC);
+
+  $records =
+    db_query(
+      'SELECT ta.tid, td.vid, ta.grant_view, ta.grant_update, ta.grant_delete,
+              ta.grant_create, ta.grant_list
+       FROM {taxonomy_access_term} ta
+       INNER JOIN {taxonomy_term_data} td ON ta.tid = td.tid
+       WHERE rid = :rid',
+      array(':rid' => $rid))
+    ->fetchAllAssoc('tid', PDO::FETCH_ASSOC);
+  $term_grants = array();
+  foreach ($records as $record) {
+    $term_grants[$record['vid']][$record['tid']] = $record;
+  }
+
+  // Add a fieldset for the global default.
+  $form['global_default'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Global default'),
+    '#description' => t('The global default controls access to untagged nodes. It is also used as the default for disabled vocabularies.'),
+    '#collapsible' => TRUE,
+    // Collapse if there are vocabularies configured.
+    '#collapsed' => (sizeof($defaults) > 1),
+  );
+  // Print term grant table.
+  $form['global_default']['grants'] = taxonomy_access_grant_add_table($defaults[TAXONOMY_ACCESS_GLOBAL_DEFAULT], TAXONOMY_ACCESS_VOCABULARY_DEFAULT);
+
+  // Fetch all vocabularies and determine which are enabled for the role.
+  $vocabs = array();
+  $disabled = array();
+  foreach (taxonomy_get_vocabularies() as $vocab) {
+    $vocabs[$vocab->vid] = $vocab;
+    if (!isset($defaults[$vocab->vid])) {
+      $disabled[$vocab->vid] = $vocab->name;
     }
   }
-  // New grant row.
-  if (isset($add_items)) {
-    $form['new']['grants'] = taxonomy_access_admin_build_row();
-    $form['new']['#tree'] = TRUE;
-    $form['new']['item'] = array(
-      '#type' => 'select',
-      '#options' => $add_items,
+
+  // Add a fieldset to enable vocabularies.
+  if (!empty($disabled)) {
+    $form['enable_vocabs'] = array(
+      '#type' => 'fieldset',
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE,
+      '#title' => t('Add vocabulary'),
+      '#attributes' => array('class' => array('container-inline', 'taxonomy-access-add')),
     );
-    $form['new']['recursive'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('with children'),
-      '#description' => t('Add child terms recursively with these values.'),
+    $form['enable_vocabs']['enable_vocab'] = array(
+      '#type' => 'select',
+      '#title' => t('Vocabulary'),
+      '#options' => $disabled,
     );
-    $form['new']['add'] = array(
+    $form['enable_vocabs']['add'] = array(
       '#type' => 'submit',
+      '#submit' => array('taxonomy_access_enable_vocab_submit'),
       '#value' => t('Add'),
     );
   }
 
-  $form['actions'] = array('#type' => 'actions');
-
-  $form['actions']['delete'] = array(
-    '#type' => 'submit',
-    '#value' => t('Delete selected'),
-  );
+  // Add a fieldset for each enabled vocabulary.
+  foreach ($defaults as $vid => $vocab_default) {
+    if (!empty($vocabs[$vid])) {
+      $vocab = $vocabs[$vid];
+      $name = $vocab->machine_name;
+
+      // Fetch unconfigured terms and reorder term records by hierarchy.
+      $sort = array();
+      $add_options = array();
+      if ($tree = taxonomy_get_tree($vid)) {
+        foreach ($tree as $term) {
+          if (empty($term_grants[$vid][$term->tid])) {
+            $add_options["term $term->tid"] = str_repeat('-', $term->depth) . ' ' .check_plain($term->name);
+          }
+          else {
+            $sort[$term->tid] = $term_grants[$vid][$term->tid];
+            $sort[$term->tid]['name'] =  str_repeat('-', $term->depth) . ' ' . check_plain($term->name);
+          }
+        }
+        $term_grants[$vid] = $sort;
+      }
 
+      $grants = array(TAXONOMY_ACCESS_VOCABULARY_DEFAULT => $vocab_default);
+      $grants[TAXONOMY_ACCESS_VOCABULARY_DEFAULT]['name'] = t('Default');
+      if (!empty($term_grants[$vid])) {
+        $grants += $term_grants[$vid];
+      }
+      $form[$name] = array(
+        '#type' => 'fieldset',
+        '#title' => $vocab->name,
+        '#attributes' => array('class' => array('taxonomy-access-vocab')),
+        '#description' => t('The default settings apply to all terms in %vocab that do not have their own below.', array('%vocab' => $vocab->name)),
+        '#collapsible' => TRUE,
+        '#collapsed' => FALSE,
+      );
+      // Term grant table.
+      $form[$name]['grants'] =
+        taxonomy_access_grant_table($grants, $vocab->vid, t('Term'), !empty($term_grants[$vid]));
+      // Fieldset to add a new term if there are any.
+      if (!empty($add_options)) {
+        $form[$name]['new'] = array(
+          '#type' => 'fieldset',
+          '#collapsible' => TRUE,
+          '#collapsed' => TRUE,
+          '#title' => t('Add term'),
+          '#tree' => TRUE,
+          '#attributes' => array('class' => array('container-inline', 'taxonomy-access-add')),
+        );
+        $form[$name]['new'][$vid]['item'] = array(
+          '#type' => 'select',
+          '#title' => t('Term'),
+          '#options' => $add_options,
+        );
+        $form[$name]['new'][$vid]['recursive'] = array(
+          '#type' => 'checkbox',
+          '#title' => t('with descendants'),
+        );
+        $form[$name]['new'][$vid]['grants'] =
+          taxonomy_access_grant_add_table($vocab_default, $vid);
+        $form[$name]['new'][$vid]['add'] = array(
+          '#type' => 'submit',
+          '#name' => $vid,
+          '#submit' => array('taxonomy_access_add_term_submit'),
+          '#value' => t('Add'),
+        );
+      }
+      $disable_url = url(
+        TAXONOMY_ACCESS_CONFIG . "/role/$rid/disable/$vid",
+        array('query' => drupal_get_destination())
+      );
+      $form[$name]['disable'] = array(
+          '#markup' => '<p>' . t(
+            'To disable the %vocab vocabulary, <a href="@url">delete all @vocab access rules</a>.',
+            array('%vocab' => $vocab->name, '@vocab' => $vocab->name, '@url' => $disable_url)) . '</p>'
+      );
+    }
+  }
+  $form['actions'] = array('#type' => 'actions');
   $form['actions']['submit'] = array(
     '#type' => 'submit',
     '#value' => t('Save all'),
+    '#submit' => array('taxonomy_access_save_all_submit'),
   );
+  if (!empty($term_grants)) {
+    $form['actions']['delete'] = array(
+      '#type' => 'submit',
+      '#value' => t('Delete selected'),
+      '#submit' => array('taxonomy_access_delete_selected_submit'),
+    );
+  }
 
   return $form;
 }
 
 /**
- * Renders the permission matrix user form for a given user role.
+ * Generates a grant table for multiple access rules.
  *
- * @return string
- *   String containing rendered HTML form in table.
+ * @param array $rows
+ *   An array of grant row data, keyed by an ID (term, vocab, role, etc.). Each
+ *   row should include the following keys:
+ *   - name: (optional) The label for the row (e.g., a term, vocabulary, or
+ *     role name).
+ *   - view: The View grant value select box for the element.
+ *   - update: The Update grant value select box for the element.
+ *   - delete: The Delete grant value select box for the element.
+ *   - create: The Add tag grant value select box for the element.
+ *   - list: The View tag grant value select box for the element.
+ * @param int $parent_vid
+ *   The parent ID for the table in the form tree structure (typically a
+ *   vocabulary id).
+ * @param string $first_col
+ *   The header for the first column (in the 'name' key for each row).
+ * @param bool $delete
+ *   (optional) Whether to add a deletion checkbox to each row along with a
+ *   "Check all" box in the table header. The checbox is automatically disabled
+ *   for TAXONOMY_ACCESS_VOCABULARY_DEFAULT. Defaults to TRUE.
+ *
+ * @return
+ *   Renderable array containing the table.
+ *
+ * @see taxonomy_access_grant_table()
  */
-function theme_taxonomy_access_admin_form($variables) {
-  $form = $variables['form'];
+function taxonomy_access_grant_table(array $rows, $parent_vid, $first_col, $delete = TRUE) {
+  $header = taxonomy_access_grant_table_header();
+  if ($first_col) {
+    array_unshift(
+      $header,
+      array('data' => $first_col, 'class' => array('taxonomy-access-label'))
+    );
+  }
+  if ($delete) {
+    drupal_add_js('misc/tableselect.js');
+    array_unshift($header, array('class' => array('select-all')));
+  }
+  $table = array(
+    '#type' => 'taxonomy_access_grant_table',
+    '#tree' => TRUE,
+    '#header' => $header,
+  );
+  foreach ($rows as $id => $row) {
+    $table[$parent_vid][$id] = taxonomy_access_admin_build_row($row, 'name', $delete);
+  }
+  // Disable the delete checkbox for the default.
+  if ($delete && isset($table[$parent_vid][TAXONOMY_ACCESS_VOCABULARY_DEFAULT])) {
+    $table[$parent_vid][TAXONOMY_ACCESS_VOCABULARY_DEFAULT]['remove']['#disabled'] = TRUE;
+  }
 
-  $roles = _taxonomy_access_user_roles();
+  return $table;
+}
+
+/**
+ * Generates a grant table for adding access rules with one set of values.
+ *
+ * @param array $rows
+ *   An associative array of access rule data, with the following keys:
+ *   - view: The View grant value select box for the element.
+ *   - update: The Update grant value select box for the element.
+ *   - delete: The Delete grant value select box for the element.
+ *   - create: The Add tag grant value select box for the element.
+ *   - list: The View tag grant value select box for the element.
+ * @param int $id
+ *   The ID for this set (e.g., a vocabulary ID).
+ *
+ * @return
+ *   Renderable array containing the table.
+ *
+ * @see taxonomy_access_grant_table()
+ */
+function taxonomy_access_grant_add_table($row, $id) {
+  $header = taxonomy_access_grant_table_header();
+  $table = array(
+    '#type' => 'taxonomy_access_grant_table',
+    '#tree' => TRUE,
+    '#header' => $header,
+  );
+  $table[$id][TAXONOMY_ACCESS_VOCABULARY_DEFAULT] = taxonomy_access_admin_build_row($row);
+
+  return $table;
+}
+
+/**
+ * Returns a header array for grant form tables.
+ *
+ * @return array
+ *   An array of header cell data for a grant table.
+ */
+function taxonomy_access_grant_table_header() {
   $header = array(
-    array( 'data' => t('Category'), 'colspan' => 2),
-    array( 'data' => t('View')),
-    array( 'data' => t('Update')),
-    array( 'data' => t('Delete')),
-    array( 'data' => t('Add Tag')),
-    array( 'data' => t('View Tag')),
+    array('data' => t('View')),
+    array('data' => t('Update')),
+    array('data' => t('Delete')),
+    array('data' => t('Add Tag')),
+    array('data' => t('View Tag')),
   );
-  $grant_types = array('view', 'update', 'delete', 'create', 'list');
+  foreach ($header as &$cell) {
+    $cell['class'] = array('taxonomy-access-grant');
+  }
+  return $header;
+}
 
+/**
+ * Theme our grant table.
+ *
+ * @todo
+ *   Use a separate theme function for taxonomy_access_grant_add_table() to get
+ *   out of nesting hell?
+ * @todo
+ *   I clearly have no idea what I'm doing here.
+ */
+function theme_taxonomy_access_grant_table($element_data) {
+  $table = array();
+  $table['header'] = $element_data['elements']['#header'];
+  $table['attributes']['class'] = array('taxonomy-access-grant-table');
   $rows = array();
-
-  foreach (array_keys($form['vocabs']) as $vid) {
-    if (is_numeric($vid) AND isset($form['grants'][$vid])) {
-      $row = array(array('data' => '<h3>' . check_plain($form['vocabs'][$vid]['#title']) . '</h3>', 'colspan' => 7));
-      $rows[] = $row;
-      foreach (array_keys($form['grants'][$vid]) as $tid) {
-        if (is_numeric($tid)) {
-          $select_key = $tid? 'selected_terms' : 'selected_defaults';
-          $select_id = $tid? $tid : $vid;
-          $row = array(
-            array('data' => drupal_render($form[$select_key][$select_id]), 'colspan' => 2),
-          );
-          foreach ($grant_types as $grant) {
-            $row[] = array(
-              'data' => drupal_render($form['grants'][$vid][$tid][$grant]),
-              'class' => $grant . '-' . $form['grants'][$vid][$tid][$grant]['#default_value']
-            );
+  foreach (element_children($element_data['elements']) as $element_key) {
+    $child = $element_data['elements'][$element_key];
+    foreach (element_children($child) as $child_key) {
+      $record = $child[$child_key];
+      $row = array();
+      foreach (element_children($record) as $record_key) {
+        $data = array('data' => $record[$record_key]);
+        // If it's the default, add styling.
+        if ($record_key == 'name') {
+          $data['class'] = array('taxonomy-access-label');
+          if ($child_key == TAXONOMY_ACCESS_VOCABULARY_DEFAULT) {
+            $data['class'][] = 'taxonomy-access-default';
           }
-          $rows[] = $row;
         }
+        // Add grant classes to grant cells.
+        elseif (in_array($record_key, array('view', 'update', 'delete', 'create', 'list'))) {
+          $grant_class = $record_key . '-' . $data['data']['#default_value'];
+          $data['class'] = array('taxonomy-access-grant', $grant_class);
+        }
+        $row[] = $data;
       }
+      $rows[] = $row;
     }
   }
-  if (isset($form['new'])) {
-    $row = array(array('data' => '<h3>' . t('New') . '</h3>', 'colspan' => 7));
-    $rows[] = $row;
+  $table['rows'] = $rows;
+  return theme('table', $table);
+}
 
-    $row = array(
-      array(
-        'data' => drupal_render($form['new']['item'])
-        . drupal_render($form['new']['recursive']),
-      ),
-      drupal_render($form['new']['add']),
+/**
+ * Assembles a row of grant options for a term or default on the admin form.
+ *
+ * @param array $grants
+ *   An array of grants to use as form defaults.
+ * @param $label_key
+ *   (optional) Key of the column to use as a label in each grant row. Defaults
+ *   to NULL.
+ */
+function taxonomy_access_admin_build_row(array $grants, $label_key = NULL, $delete = FALSE) {
+  $form['#tree'] = TRUE;
+  if ($delete) {
+    $form['remove'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Delete access rule for @name', array('@name' => $grants[$label_key])),
+      '#title_display' => 'invisible',
     );
-    foreach ($grant_types as $grant) {
-      $row[] = array(
-        'data' => drupal_render($form['new']['grants'][$grant]),
-        'class' => $grant . '-' . $form['new']['grants'][$grant]['#default_value']
-      );
-    }
-    $rows[] = $row;
-    $row = array();
   }
+  if ($label_key) {
+    $form[$label_key] = array(
+      '#type' => 'markup',
+      '#markup' => check_plain($grants[$label_key]),
+    );
+  }
+  foreach (array('view', 'update', 'delete', 'create', 'list') as $grant) {
+    $for = $label_key ? $grants[$label_key] : NULL;
+    $form[$grant] = array(
+      '#type' => 'select',
+      '#title' => _taxonomy_access_grant_field_label($grant, $for),
+      '#title_display' => 'invisible',
+      '#default_value' => is_string($grants['grant_' . $grant]) ? $grants['grant_' . $grant] : TAXONOMY_ACCESS_NODE_IGNORE,
+      '#required' => TRUE,
+    );
+  }
+  foreach (array('view', 'update', 'delete') as $grant) {
+    $form[$grant]['#options'] = array(
+      TAXONOMY_ACCESS_NODE_ALLOW => t('Allow'),
+      TAXONOMY_ACCESS_NODE_IGNORE => t('Ignore'),
+      TAXONOMY_ACCESS_NODE_DENY => t('Deny'),
+    );
+  }
+  foreach (array('create', 'list') as $grant) {
+    $form[$grant]['#options'] = array(
+      TAXONOMY_ACCESS_TERM_ALLOW => t('Allow'),
+      TAXONOMY_ACCESS_TERM_DENY => t('Deny'),
+    );
+  }
+  return $form;
+}
 
+/**
+ * Returns the proper invisible field label for each grant table element.
+ */
+function _taxonomy_access_grant_field_label($grant, $for = NULL) {
+  if ($for) {
+    $label = array('@label', $for);
+    $titles = array(
+      'view' => t('View grant for @label', $label),
+      'update' => t('Update grant for @label', $label),
+      'delete' => t('Delete grant for @label', $label),
+      'create' => t('Add tag grant for @label', $label),
+      'list' => t('View tag grant for @label', $label),
+    );
+  }
+  else {
+    $titles = array(
+      'view' => t('View grant'),
+      'update' => t('Update grant'),
+      'delete' => t('Delete grant'),
+      'create' => t('Add tag grant'),
+      'list' => t('View tag grant'),
+    );
+  }
 
-  $output = '';
-  $output .= theme('table', array('header' => $header, 'rows' => $rows));
-  $output .= drupal_render_children($form);
-
-  return $output;
+ return $titles[$grant];
 }
 
 /**
- * Form submission handler for taxonomy_access_admin_form().
+ * Form submission handler for taxonomy_access_admin_role().
+ *
+ * Processes submissions for the vocabulary 'Add' button.
  */
-function taxonomy_access_admin_form_submit($form, &$form_state) {
-  // Save some typing.
-  $values = $form_state['values'];
-
-  switch ($values['op']) {
+function taxonomy_access_enable_vocab_submit($form, &$form_state) {
+  $rid = $form_state['values']['rid'];
+  $vid = $form_state['values']['enable_vocab'];
+  $roles = _taxonomy_access_user_roles();
+  $vocab = taxonomy_vocabulary_load($vid);
+  if (taxonomy_access_enable_vocab($vid, $rid)) {
+    drupal_set_message(t(
+      'Vocabulary %vocab enabled successfully for the %role role.',
+      array(
+        '%vocab' => $vocab->name,
+        '%role' => $roles[$rid])));
+  }
+  else {
+    drupal_set_message(t('The vocabulary could not be enabled.'), 'error');
+  }
+}
 
-    case t('Delete selected'):
-      _taxonomy_access_admin_form_delete($values['rid'], $values);
-      break;
+/**
+ * Form submission handler for taxonomy_access_admin_role().
+ *
+ * Processes submissions for the term 'Add' button.
+ */
+function taxonomy_access_add_term_submit($form, &$form_state) {
+  $vid = $form_state['clicked_button']['#name'];
+  $new = $form_state['values']['new'][$vid];
+  $rid = $form_state['values']['rid'];
+  list($type, $id) = explode(' ', $new['item']);
+  $rows = array();
 
-    case t('Add'):
-      _taxonomy_access_admin_form_add($values['rid'], $values);
-      break;
+  $rows[$id] =
+    _taxonomy_access_format_grant_record($id, $rid, $new['grants'][$vid][TAXONOMY_ACCESS_VOCABULARY_DEFAULT]);
 
-    case t('Save all'):
-      _taxonomy_access_admin_form_save_all($values['rid'], $values, $form);
-      $form_state['redirect'] = TAXONOMY_ACCESS_CONFIG;
-      break;
+  // If we are adding children recursively, add those as well.
+  if ($new['recursive'] == 1) {
+    $children = _taxonomy_access_get_descendants($id);
+    foreach ($children as $tid) {
+      $rows[$tid] =
+        _taxonomy_access_format_grant_record($tid, $rid, $new['grants'][$vid][TAXONOMY_ACCESS_VOCABULARY_DEFAULT]);
+    }
   }
+
+  // Set the grants for the row or rows.
+  taxonomy_access_set_term_grants($rows);
 }
 
 /**
- * Helper to process the "Delete selected" button on the administration form.
+ * Page callback: Returns a confirmation form to disable a vocabulary.
+ *
+ * Path: TAXONOMY_ACCESS_CONFIG . '/role/%/disable/%taxonomy_vocabulary
  *
- * @param $rid
- *   The role id for the submission.
- * @param $values
- *   The $form_state['values'] from the form submission.
+ * @param int $rid
+ *   The role ID.
+ * @param object $vocab
+ *   The vocabulary object.
+ *
+ * @todo
+ *   Check if vocab is enabled and return a 404 otherwise?
+ *
+ * @see taxonomy_access_menu()
+ * @see taxonomy_access_admin_role().
+ * @see taxonomy_access_disable_vocab_confirm_page()
  */
-function _taxonomy_access_admin_form_delete($rid, $values) {
-  if (isset($values['selected_terms']) && is_array($values['selected_terms'])) {
-    $disable_tids = array();
-    foreach ($values['selected_terms'] as $tid => $enabled) {
-      if ($enabled) {
-        $disable_tids[] = $tid;
-      }
-    }
+function taxonomy_access_disable_vocab_confirm_page($rid, $vocab) {
+  $rid = intval($rid);
 
-    if (!empty($disable_tids)) {
-      taxonomy_access_delete_term_grants($disable_tids, $rid);
-      unset($disable_tids);
-    }
+  // Return a 404 on invalid vid or rid.
+  if (!$vocab->vid || !$rid) {
+    return MENU_NOT_FOUND;
   }
 
-  if (isset($values['selected_defaults']) && is_array($values['selected_defaults'])) {
-    $disable_vids = array();
-    foreach ($values['selected_defaults'] as $vid => $enabled) {
-      if ($enabled) {
-        $disable_vids[] = $vid;
-      }
-    }
+  return drupal_get_form('taxonomy_access_disable_vocab_confirm', $rid, $vocab);
+}
 
-    if (!empty($disable_vids)) {
-      taxonomy_access_delete_default_grants($disable_vids, $rid);
-      unset($disable_tids);
-    }
+/**
+ * Returns a confirmation form for disabling a vocabulary for a role.
+ *
+ * @param int $rid
+ *   The role ID.
+ * @param object $vocab
+ *   The vocabulary object.
+ *
+ * @see taxonomy_access_disable_vocab_confirm_page()
+ * @see taxonomy_access_disable_vocab_confirm_submit()
+ * @ingroup forms
+ */
+function taxonomy_access_disable_vocab_confirm($form, &$form_state, $rid, $vocab) {
+  $roles = _taxonomy_access_user_roles();
+  if (taxonomy_access_role_enabled($rid)) {
+    $form['rid'] = array(
+      '#type' => 'value',
+      '#value' => $rid,
+    );
+    $form['vid'] = array(
+      '#type' => 'value',
+      '#value' => $vocab->vid,
+    );
+    $form['vocab_name'] = array(
+      '#type' => 'value',
+      '#value' => $vocab->name,
+    );
+    return confirm_form($form,
+      t("Are you sure you want to delete all Taxonomy access rules for %vocab in the %role role?",
+        array('%role' => $roles[$rid], '%vocab' => $vocab->name)),
+      TAXONOMY_ACCESS_CONFIG . '/role/%/edit',
+      t('This action cannot be undone.'),
+      t('Delete all'),
+      t('Cancel'));
   }
 }
 
 /**
- * Helper to process the "Add" button on the administration form.
+ * Form submission handler for taxonomy_access_disable_vocab_confirm().
+ *
+ * @param int $rid
+ *   The role ID to disable.
  *
- * @param $rid
- *   The role id for the submission.
- * @param $values
- *   The $form_state['values'] from the form submission.
+ * @todo
+ *   Set a message on invalid $rid or $vid?
  */
-function _taxonomy_access_admin_form_add($rid, $values) {
-  $new = $values['new'];
-  list($type, $id) = explode(' ', $new['item']);
-  $rows = array();
-
-  if ($type == 'term') {
-    $rows[$id] =
-      _taxonomy_access_format_grant_record($id, $rid, $new['grants']);
+function taxonomy_access_disable_vocab_confirm_submit($form, &$form_state) {
+  $roles = _taxonomy_access_user_roles();
+  $rid = intval($form_state['values']['rid']);
+  $vid = intval($form_state['values']['vid']);
+  // Do not proceed for invalid role IDs, and do not allow the global default
+  // to be deleted.
+  if (!$vid || !$rid || empty($roles[$rid])) {
+    return FALSE;
+  }
 
-    // If we are adding children recursively, add those as well.
-    if ($new['recursive'] == 1) {
-      $children = _taxonomy_access_get_descendants($id);
-      foreach ($children as $tid) {
-        $rows[$tid] =
-          _taxonomy_access_format_grant_record($tid, $rid, $new['grants']);
-      }
+  if ($form_state['values']['confirm']) {
+    $form_state['redirect'] = TAXONOMY_ACCESS_CONFIG;
+    if (taxonomy_access_disable_vocab($vid, $rid)) {
+      drupal_set_message(
+        t('All Taxonomy access rules deleted for %vocab in role %role.',
+          array(
+            '%vocab' => $form_state['values']['vocab_name'],
+            '%role' => $roles[$rid])
+         ));
+      return TRUE;
     }
-
-    // Set the grants for the row or rows.
-    taxonomy_access_set_term_grants($rows);
   }
+}
 
-  elseif ($type == 'default') {
-    $default[$id] =
-      _taxonomy_access_format_grant_record($id, $rid, $new['grants'], TRUE);
-
-    // For the vocabulary default, all terms in the vocabulary are children.
-    if ($new['recursive'] == 1) {
-      $children = _taxonomy_access_get_vocabulary_terms($id);
-      foreach ($children as $tid) {
-        $rows[$tid] =
-          _taxonomy_access_format_grant_record($tid, $rid, $new['grants']);
+/**
+ * Form submission handler for taxonomy_access_admin_role().
+ *
+ * Processes submissions for the "Delete selected" button.
+ *
+ * @todo
+ *   The parent form could probably be refactored to make this more efficient
+ *   (by putting these elements in a flat list) but that would require changing
+ *   taxonomy_access_grant_table() and taxonomy_access_build_row().
+ */
+function taxonomy_access_delete_selected_submit($form, &$form_state) {
+  $rid = intval($form_state['values']['rid']);
+  $delete_tids = array();
+  foreach ($form_state['values']['grants'] as $vid => $tids) {
+    foreach ($tids as $tid => $record) {
+      if (!empty($record['remove'])) {
+        $delete_tids[] = $tid;
       }
     }
-
-    // Set the grants for the row or rows.
-    taxonomy_access_set_default_grants($default);
-    taxonomy_access_set_term_grants($rows);
+  }
+  if ($rid) {
+    if (taxonomy_access_delete_term_grants($delete_tids, $rid)) {
+      drupal_set_message(format_plural(
+          sizeof($delete_tids),
+          '1 term access rule was deleted.',
+          '@count term access rules were deleted.'));
+    }
+    else {
+      drupal_set_message(t('The records could not be deleted.'), 'warning');
+    }
   }
 }
-
 /**
- * Helper to process the "Save all" button on the administration form.
+ * Form submission handler for taxonomy_access_admin_form().
  *
- * @param int $rid
- *   The role id for the submission.
- * @param array $values
- *   The $form_state['values'] from the form submission.
- * @param array $form
- *   The $form array for the form.  Used to compare default values.
+ * Processes submissions for the 'Save all' button.
  */
-function _taxonomy_access_admin_form_save_all($rid, $values, $form) {
+function taxonomy_access_save_all_submit($form, &$form_state) {
+  $values = $form_state['values'];
+  $rid = $values['rid'];
+  $vocabs = taxonomy_get_vocabularies();
+
   // Create four lists of records to update.
   $update_terms = array();
   $skip_terms = array();
@@ -468,12 +791,21 @@ function _taxonomy_access_admin_form_save_all($rid, $values, $form) {
   $skip_defaults = array();
 
   foreach ($values['grants'] as $vid => $rows) {
-    foreach ($rows as $tid => $grants) {
+    if ($vid == TAXONOMY_ACCESS_GLOBAL_DEFAULT) {
+      $element = $form['global_default'];
+    }
+    else {
+      $vocab = $vocabs[$vid];
+      $element = $form[$vocab->machine_name];
+    }
+    foreach ($rows as $tid => $row) {
       // Check the default values for this row.
       $defaults = array();
-      foreach ($grants as $grant_name => $value) {
+      $grants = array();
+      foreach (array('view', 'update', 'delete', 'create', 'list') as $grant_name) {
+        $grants[$grant_name] = $row[$grant_name];
         $defaults[$grant_name] =
-          $form['grants'][$vid][$tid][$grant_name]['#default_value'];
+          $element['grants'][$vid][$tid][$grant_name]['#default_value'];
       }
 
       // Proceed if the user changed the row (values differ from defaults).
diff --git a/taxonomy_access.css b/taxonomy_access.css
index 4a86931..4eda7c1 100644
--- a/taxonomy_access.css
+++ b/taxonomy_access.css
@@ -1,11 +1,11 @@
 table.grant_help th {
  vertical-align: top;
-} 
+}
 
 table.grant_help td p {
  margin-top: 0px;
  margin-bottom: 8px;
-} 
+}
 
 table.grant_help em.perm {
   font-weight: bold;
@@ -33,4 +33,62 @@ label.disabled {
 .create-0,
 .list-0 {
  background-color: #fef5f1;
-}
\ No newline at end of file
+}
+.taxonomy-access-grant-table {
+  margin-top: 0.5em;
+  margin-bottom: 0.5em;
+  width: inherit;
+}
+.taxonomy-access-grant-table th.select-all {
+  width: 2em;
+}
+.taxonomy-access-grant-table td.taxonomy-access-label {
+  padding-right: 4em;
+  min-width: 12em;
+}
+.taxonomy-access-grant-table td.taxonomy-access-default {
+  font-style: italic;
+  font-weight: bold;
+}
+.taxonomy-access-grant-table td.taxonomy-access-grant {
+  width: 7em;
+}
+fieldset#edit-vocabs .fieldset-description {
+  display: block;
+}
+
+/**
+ * I apologize, because what follows is a hodgepodge to override the default
+ * fieldset styling in both Seven and Bartik, and might not even work in some
+ * themes.
+ */
+#taxonomy-access-admin-role fieldset {
+  margin-bottom: 0.5em;
+  margin-top: 0.5em;
+}
+
+#taxonomy-access-admin-role fieldset.taxonomy-access-add.collapsible,
+#taxonomy-access-admin-role fieldset.taxonomy-access-add.collapsed {
+  margin-top: 0px;
+  margin-bottom: 1em;
+  border: none;
+  background: none;
+}
+fieldset.taxonomy-access-add > legend {
+  background: none;
+  border: none;
+  font-family: inherit;
+  position: static;
+}
+fieldset.taxonomy-access-add > legend a {
+  color: #0076bc;
+}
+fieldset.taxonomy-access-add  .fieldset-wrapper {
+  margin-top: 0px;
+  padding-top: 0.5em;
+}
+html.js fieldset.collapsible.taxonomy-access-add > legend .fieldset-legend,
+html.js fieldset.collapsed.taxonomy-access-add > legend .fieldset-legend {
+  background: transparent url(images/add.png) no-repeat 0 center;
+  text-transform: none;
+}
diff --git a/taxonomy_access.install b/taxonomy_access.install
index a8dffc5..a91b330 100644
--- a/taxonomy_access.install
+++ b/taxonomy_access.install
@@ -179,6 +179,37 @@ function taxonomy_access_schema() {
 }
 
 /**
+ * Add vocabulary defaults for all configured vocabularies.
+ */
+function taxonomy_access_update_7002() {
+  // Get a list of all vocabularies with any term configurations for each role.
+  $ta_configs = db_query(
+    "SELECT td.vid, ta.rid
+     FROM {taxonomy_access_term} ta
+     INNER JOIN {taxonomy_term_data} td ON ta.tid = td.tid
+     GROUP BY td.vid, ta.rid"
+  )->fetchAll();
+
+  // Get a list of all configured vocabularies.
+  $td_configs = db_query(
+    "SELECT vid, rid
+     FROM {taxonomy_access_default}"
+  )->fetchAll();
+
+  $records = array();
+  $global_defaults = taxonomy_access_global_defaults();
+
+  foreach ($ta_configs as $config) {
+    if (!in_array($config, $td_configs)) {
+      $record = (array) $global_defaults[$config->rid];
+      $records[] = _taxonomy_access_format_grant_record($config->vid, $config->rid, $record, TRUE);
+    }
+  }
+
+  return taxonomy_access_set_default_grants($records);
+}
+
+/**
  * Rename grant realm.
  */
 function taxonomy_access_update_7001() {
diff --git a/taxonomy_access.module b/taxonomy_access.module
index 64b48ee..ddca41e 100644
--- a/taxonomy_access.module
+++ b/taxonomy_access.module
@@ -94,8 +94,25 @@ function taxonomy_access_theme() {
       'render element' => 'form',
       'file' => 'taxonomy_access.admin.inc',
     ),
+    'taxonomy_access_grant_table' => array(
+      'render element' => 'elements',
+      'file' => 'taxonomy_access.admin.inc',
+    ),
   );
 }
+
+/**
+ * Implements hook_element_info().
+ */
+function taxonomy_access_element_info() {
+  return array(
+    'taxonomy_access_grant_table' => array(
+      '#theme' => 'taxonomy_access_grant_table',
+      '#regions' => array('' => array()),
+    ),
+  );
+}
+
 /**
  * Implements hook_menu().
  */
@@ -121,19 +138,32 @@ function taxonomy_access_menu() {
     'title callback' => 'taxonomy_access_role_edit_title',
     'title arguments' => array(5),
     'page callback' => 'drupal_get_form',
-    'page arguments' => array('taxonomy_access_admin_form', 5),
+    'page arguments' => array('taxonomy_access_admin_role', 5),
     'access callback' => 'taxonomy_access_role_edit_access',
     'access arguments' => array(5),
     'file' => 'taxonomy_access.admin.inc',
   );
+  $items[TAXONOMY_ACCESS_CONFIG . '/role/%/enable'] = array(
+    'page callback' => 'taxonomy_access_enable_role_validate',
+    'page arguments' => array(5),
+    'access arguments' => array('administer permissions'),
+    'file' => 'taxonomy_access.admin.inc',
+  );
   $items[TAXONOMY_ACCESS_CONFIG . '/role/%/delete'] = array(
     'page callback' => 'drupal_get_form',
-    'page arguments' => array('taxonomy_access_admin_delete_role', 5),
+    'page arguments' => array('taxonomy_access_role_delete_confirm', 5),
     'access callback' => 'taxonomy_access_role_delete_access',
     'access arguments' => array(5),
     'file' => 'taxonomy_access.admin.inc',
     'type' => MENU_CALLBACK,
   );
+  $items[TAXONOMY_ACCESS_CONFIG . '/role/%/disable/%taxonomy_vocabulary'] = array(
+    'page callback' => 'taxonomy_access_disable_vocab_confirm_page',
+    'page arguments' => array(5, 7),
+    'access arguments' => array('administer permissions'),
+    'file' => 'taxonomy_access.admin.inc',
+    'type' => MENU_CALLBACK,
+  );
   $items['taxonomy_access/autocomplete'] = array(
     'title' => 'Autocomplete taxonomy',
     'page callback' => 'taxonomy_access_autocomplete',
diff --git a/taxonomy_access.test b/taxonomy_access.test
index 45a1474..481e5a4 100644
--- a/taxonomy_access.test
+++ b/taxonomy_access.test
@@ -66,7 +66,7 @@ class TaxonomyAccessTestCase extends DrupalWebTestCase {
         'update' => TAXONOMY_ACCESS_NODE_IGNORE,
         'delete' => TAXONOMY_ACCESS_NODE_IGNORE,
       );
-      $rows[] = _taxonomy_access_format_grant_record(TAXONOMY_ACCESS_NODE_IGNORE, $rid, $ignore, TRUE);
+      $rows[] = _taxonomy_access_format_grant_record(TAXONOMY_ACCESS_GLOBAL_DEFAULT, $rid, $ignore, TRUE);
     }
     taxonomy_access_set_default_grants($rows);
 
@@ -74,12 +74,11 @@ class TaxonomyAccessTestCase extends DrupalWebTestCase {
       $r =
         db_query(
           'SELECT grant_view FROM {taxonomy_access_default}
-          WHERE vid = :vid AND rid = :rid',
+           WHERE vid = :vid AND rid = :rid',
           array(':vid' => TAXONOMY_ACCESS_GLOBAL_DEFAULT, ':rid' => $rid)
         )
         ->fetchField();
-      $this->assertTrue(is_numeric($r) && $r == TAXONOMY_ACCESS_NODE_IGNORE,
-        t("Set global default for role %rid to <em>Ignore</em>", array('%rid' => $rid)));
+      $this->assertTrue(is_numeric($r) && $r == 0, t("Set global default for role %rid to <em>Ignore</em>", array('%rid' => $rid)));
     }
   }
 
@@ -218,39 +217,84 @@ class TaxonomyAccessTestCase extends DrupalWebTestCase {
   }
 
   /**
-   * Asserts that an enable or edit link is found for the role.
+   * Asserts that a status column and "Configure" link is found for the role.
+   *
+   * @param array $statuses
+   *   An associative array of role statuses, keyed by role ID. Each item
+   *   should be TRUE if the role is enabled, and FALSE otherwise.
+   */
+  function checkRoleConfig(array $statuses) {
+    $roles = _taxonomy_access_user_roles();
+
+    // Log in as the administrator.
+    $this->drupalLogout();
+    $this->drupalLogin($this->users['site_admin']);
+    $this->drupalGet(TAXONOMY_ACCESS_CONFIG);
+
+    foreach ($statuses as $rid => $status) {
+      // Assert that a "Configure" link is available for the role.
+      $this->assertLinkByHref(
+        TAXONOMY_ACCESS_CONFIG . "/role/$rid/edit",
+        0,
+        t('"Configure" link is available for role %rid.', array('%rid' => $rid)));
+    }
+
+    // Retrieve the grant status table.
+    $shown = array();
+    $table = $this->xpath('//table/tbody');
+    $table = reset($table);
+    // SimpleXML has fake arrays so we have to do this to get the data out.
+    foreach ($table->tr as $row) {
+      $tds = array();
+      foreach ($row->td as $value) {
+        $tds[] = (string) $value;
+      }
+      $shown[$tds[0]] = $tds[1];
+    }
+
+    foreach ($statuses as $rid => $status) {
+      // Assert that the form shows the passed status.
+      if ($status) {
+        $this->assertTrue(
+          $shown[$roles[$rid]] == t('Enabled'),
+          format_string('Role %role is enabled.', array('%role' => $rid)));
+      }
+      else {
+        $this->assertTrue(
+          $shown[$roles[$rid]] == t('Disabled'),
+          format_string('Role %role is disabled.', array('%role' => $rid)));
+      }
+
+      // Assert that a "Configure" link is available for the role.
+      $this->assertLinkByHref(
+        TAXONOMY_ACCESS_CONFIG . "/role/$rid/edit",
+        0,
+        t('"Configure" link is available for role %rid.',
+          array('%rid' => $rid)));
+    }
+
+  }
+
+  /**
+   * Asserts that an enable link is or is not found for the role.
    *
    * @param int $rid
    *   The role ID to check.
-   * @param bool $enable
-   *   (optional) Whether the text should be 'edit' (TRUE) or 'enable' (FALSE).
-   *   Defaults to FALSE.
+   * @param bool $found
+   *   Whether the link should be found, or not.
    */
-  function checkRoleEditEnable($rid, $enable = FALSE) {
-    $this->assertLinkByHref(
-      TAXONOMY_ACCESS_CONFIG . "/role/$rid/edit",
-      0,
-      t('Edit link is available for role %rid.', array('%rid' => $rid))
-    );
-
-    if ($enable) {
-      $this->assertRaw(
-        TAXONOMY_ACCESS_CONFIG . "/role/$rid/edit\">enable",
-        t('Link text is "enable" for role %rid.', array('%rid' => $rid))
-      );
-      $this->assertNoRaw(
-        TAXONOMY_ACCESS_CONFIG . "/role/$rid/edit\">edit",
-        t('Link text is not "edit" for role %rid.', array('%rid' => $rid))
+  function checkRoleEnableLink($rid, $found) {
+    if ($found) {
+      $this->assertLinkByHref(
+        TAXONOMY_ACCESS_CONFIG . "/role/$rid/enable",
+        0,
+        t('Enable link is available for role %rid.', array('%rid' => $rid))
       );
     }
     else {
-      $this->assertRaw(
-        TAXONOMY_ACCESS_CONFIG . "/role/$rid/edit\">edit",
-        t('Link text is "edit" for role %rid.', array('%rid' => $rid))
-      );
-      $this->assertNoRaw(
-        TAXONOMY_ACCESS_CONFIG . "/role/$rid/edit\">enable",
-        t('Link text is not "enable" for role %rid.', array('%rid' => $rid))
+      $this->assertNoLinkByHref(
+        TAXONOMY_ACCESS_CONFIG . "/role/$rid/enable",
+        t('Enable link is not available for role %rid.', array('%rid' => $rid))
       );
     }
   }
@@ -261,60 +305,56 @@ class TaxonomyAccessTestCase extends DrupalWebTestCase {
    * @param int $rid
    *   The role ID to check.
    * @param bool $found
-   *   (optional) Whether the link should be found, or not.  Defaults to TRUE.
+   *   Whether the link should be found, or not.
    */
-  function checkRoleDisable($rid, $found = TRUE) {
+  function checkRoleDisableLink($rid, $found) {
     if ($found) {
       $this->assertLinkByHref(
         TAXONOMY_ACCESS_CONFIG . "/role/$rid/delete",
         0,
         t('Disable link is available for role %rid.', array('%rid' => $rid))
       );
-      $this->assertRaw(
-        TAXONOMY_ACCESS_CONFIG . "/role/$rid/delete\">disable",
-        t('Link text is "disable" for role %rid.', array('%rid' => $rid))
-      );
     }
     else {
       $this->assertNoLinkByHref(
         TAXONOMY_ACCESS_CONFIG . "/role/$rid/delete",
         t('Disable link is not available for role %rid.', array('%rid' => $rid))
       );
-      $this->assertNoRaw(
-        TAXONOMY_ACCESS_CONFIG . "/role/$rid/delete\">disable",
-        t('There is no link text "disable" for role %rid.', array('%rid' => $rid))
-      );
     }
   }
 
   /**
-   * Adds a row on the TAC configuration form.
+   * Adds a term row on the role configuration form.
    *
    * @param array &$edit
    *   The form data to post.
    * @param int $vid
-   *   (optional) The vocabulary ID.  Defaults to global default.
-   * @param int $tid
-   *   (optional) The term ID.  Defaults to vocabulary default.
+   *   (optional) The vocabulary ID. Defaults to
+   *   TAXONOMY_ACCESS_GLOBAL_DEFAULT.
+   * @param $int tid
+   *   (optional) The term ID. Defaults to TAXONOMY_ACCESS_VOCABULARY_DEFAULT.
    * @param int $view
-   *   (optional) The view grant value. Defaults to ignore.
+   *   (optional) The view grant value. Defaults to
+   *    TAXONOMY_ACCESS_NODE_IGNORE.
    * @param int $update
-   *   (optional) The update grant value. Defaults to ignore.
+   *   (optional) The update grant value. Defaults to
    * @param int $delete
-   *   (optional) The delete grant value. Defaults to ignore.
+   *   (optional) The delete grant value. Defaults to
+   *   TAXONOMY_ACCESS_NODE_IGNORE.
    * @param int $create
-   *   (optional) The create grant value. Defaults to deny (unchecked).
+   *   (optional) The create grant value. Defaults to
+   *   TAXONOMY_ACCESS_TERM_DENY.
    * @param int $list
-   *   (optional) The list grant value. Defaults to deny (unchecked).
+   *   (optional) The list grant value. Defaults to TAXONOMY_ACCESS_TERM_DENY.
    */
-  function addFormRow(&$edit, $vid = TAXONOMY_ACCESS_GLOBAL_DEFAULT, $tid = TAXONOMY_ACCESS_VOCABULARY_DEFAULT, $view = TAXONOMY_ACCESS_NODE_IGNORE, $update = TAXONOMY_ACCESS_NODE_IGNORE, $delete = TAXONOMY_ACCESS_NODE_IGNORE, $create = TAXONOMY_ACCESS_TERM_DENY, $list = TAXONOMY_ACCESS_TERM_DENY) {
+  function addFormRow(&$edit,  $vid = TAXONOMY_ACCESS_GLOBAL_DEFAULT, $tid = TAXONOMY_ACCESS_VOCABULARY_DEFAULT, $view = TAXONOMY_ACCESS_NODE_IGNORE, $update = TAXONOMY_ACCESS_NODE_IGNORE, $delete = TAXONOMY_ACCESS_NODE_IGNORE, $create = TAXONOMY_ACCESS_TERM_DENY, $list = TAXONOMY_ACCESS_TERM_DENY) {
     $new_value = $tid ? "term $tid" : "default $vid";
-    $edit["new[item]"] = $new_value;
-    $edit["new[grants][view]"] = $view;
-    $edit["new[grants][update]"] = $update;
-    $edit["new[grants][delete]"] = $delete;
-    $edit["new[grants][create]"] = $create;
-    $edit["new[grants][list]"] = $list;
+    $edit["new[$vid][item]"] = $new_value;
+    $edit["new[$vid][grants][$vid][0][view]"] = $view;
+    $edit["new[$vid][grants][$vid][0][update]"] = $update;
+    $edit["new[$vid][grants][$vid][0][delete]"] = $delete;
+    $edit["new[$vid][grants][$vid][0][create]"] = $create;
+    $edit["new[$vid][grants][$vid][0][list]"] = $list;
   }
 
   /**
@@ -323,21 +363,25 @@ class TaxonomyAccessTestCase extends DrupalWebTestCase {
    * @param array &$edit
    *   The form data to post.
    * @param int $vid
-   *   (optional) The vocabulary ID. Defaults to TAXONOMY_ACCESS_GLOBAL_DEFAULT.
-   * @param int $tid
+   *   (optional) The vocabulary ID. Defaults to
+   *   TAXONOMY_ACCESS_GLOBAL_DEFAULT.
+   * @param $int tid
    *   (optional) The term ID. Defaults to TAXONOMY_ACCESS_VOCABULARY_DEFAULT.
    * @param int $view
-   *   (optional) The view grant value. Defaults to ignore.
+   *   (optional) The view grant value. Defaults to
+   *    TAXONOMY_ACCESS_NODE_IGNORE.
    * @param int $update
-   *   (optional) The update grant value. Defaults to ignore.
+   *   (optional) The update grant value. Defaults to
    * @param int $delete
-   *   (optional) The delete grant value. Defaults to ignore.
+   *   (optional) The delete grant value. Defaults to
+   *   TAXONOMY_ACCESS_NODE_IGNORE.
    * @param int $create
-   *   (optional) The create grant value. Defaults to deny (unchecked).
+   *   (optional) The create grant value. Defaults to
+   *   TAXONOMY_ACCESS_TERM_DENY.
    * @param int $list
-   *   (optional) The list grant value. Defaults to deny (unchecked).
+   *   (optional) The list grant value. Defaults to TAXONOMY_ACCESS_TERM_DENY.
    */
-  function configureFormRow(&$edit, $vid = TAXONOMY_ACCESS_GLOBAL_DEFAULT, $tid = TAXONOMY_ACCESS_VOCABULARY_DEFAULT, $view = TAXONOMY_ACCESS_NODE_IGNORE, $update = TAXONOMY_ACCESS_NODE_IGNORE, $delete = TAXONOMY_ACCESS_NODE_IGNORE, $create = TAXONOMY_ACCESS_TERM_DENY, $list = TAXONOMY_ACCESS_TERM_DENY) {
+  function configureFormRow(&$edit,  $vid = TAXONOMY_ACCESS_GLOBAL_DEFAULT, $tid = TAXONOMY_ACCESS_VOCABULARY_DEFAULT, $view = TAXONOMY_ACCESS_NODE_IGNORE, $update = TAXONOMY_ACCESS_NODE_IGNORE, $delete = TAXONOMY_ACCESS_NODE_IGNORE, $create = TAXONOMY_ACCESS_TERM_DENY, $list = TAXONOMY_ACCESS_TERM_DENY) {
     $edit["grants[$vid][$tid][view]"] = $view;
     $edit["grants[$vid][$tid][update]"] = $update;
     $edit["grants[$vid][$tid][delete]"] = $delete;
@@ -458,28 +502,8 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
     return $this->drupalCreateNode($settings);
   }
 
-  /**
-   * Checks for the proper display of admin links for anon. and auth. roles.
-   */
-  function checkConfigAnonAuthLinks() {
-    $this->drupalGet(TAXONOMY_ACCESS_CONFIG);
-    $this->checkRoleEditEnable(DRUPAL_ANONYMOUS_RID);
-    $this->checkRoleEditEnable(DRUPAL_AUTHENTICATED_RID);
-    $this->checkRoleDisable(DRUPAL_ANONYMOUS_RID, FALSE);
-    $this->checkRoleDisable(DRUPAL_AUTHENTICATED_RID, FALSE);
-  }
-
-  /**
-   * Checks for the proper display of admin links for custom roles.
-   */
-  function checkConfigCustomRoleLinks() {
-    foreach ($this->user_roles as $role) {
-      $this->checkRoleEditEnable($role->rid, TRUE);
-      $this->checkRoleDisable($role->rid, FALSE);
-    }
-  }
-
 /*
+@todo
 - check anon and auth forms
 - add recursive for vocab and for term
 - change multiple
@@ -498,11 +522,11 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
     // Visit all nodes as anonymous and verify that access is denied.
     foreach ($this->articles as $key => $article) {
       $this->drupalGet('node/' . $article->nid);
-      $this->assertResponse(403, t("Access to %name article (nid %nid) is denied for anonymous users.", array('%name' => $key, '%nid' => $article->nid)));
+      $this->assertResponse(403, t("Access to %name article (nid %nid) is denied.", array('%name' => $key, '%nid' => $article->nid)));
     }
     foreach ($this->pages as $key => $page) {
       $this->drupalGet('node/' . $page->nid);
-      $this->assertResponse(403, t("Access to %name page (nid %nid) is denied for anonymous users.", array('%name' => $key, '%nid' => $page->nid)));
+      $this->assertResponse(403, t("Access to %name page (nid %nid) is denied.", array('%name' => $key, '%nid' => $page->nid)));
     }
 
     // Log in as the regular_user.
@@ -511,21 +535,21 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
     // Visit all nodes and verify that access is denied.
     foreach ($this->articles as $key => $article) {
       $this->drupalGet('node/' . $article->nid);
-      $this->assertResponse(403, t("Access to %name article (nid %nid) is denied for a regular user.", array('%name' => $key, '%nid' => $article->nid)));
+      $this->assertResponse(403, t("Access to %name article (nid %nid) is denied.", array('%name' => $key, '%nid' => $article->nid)));
     }
     foreach ($this->pages as $key => $page) {
       $this->drupalGet('node/' . $page->nid);
-      $this->assertResponse(403, t("Access to %name page (nid %nid) is denied for a regular user.", array('%name' => $key, '%nid' => $page->nid)));
+      $this->assertResponse(403, t("Access to %name page (nid %nid) is denied.", array('%name' => $key, '%nid' => $page->nid)));
     }
 
     // Log in as the administrator.
     $this->drupalLogin($this->users['site_admin']);
 
     // Confirm that only edit links are available for anon. and auth.
-    $this->checkConfigAnonAuthLinks();
-
-    // Confirm that only enable links are available for our four custom roles.
-    $this->checkConfigCustomRoleLinks();
+    $this->checkRoleConfig(array(
+      DRUPAL_ANONYMOUS_RID => TRUE,
+      DRUPAL_AUTHENTICATED_RID => TRUE,
+    ));
   }
 
   /**
@@ -543,8 +567,7 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
     // Use the admin form to give anonymous view allow in the global default.
     $this->drupalGet(TAXONOMY_ACCESS_CONFIG . '/role/' . DRUPAL_ANONYMOUS_RID . '/edit');
     $edit = array();
-    $this->configureFormRow($edit, TAXONOMY_ACCESS_GLOBAL_DEFAULT,
-      TAXONOMY_ACCESS_VOCABULARY_DEFAULT, TAXONOMY_ACCESS_NODE_ALLOW);
+    $this->configureFormRow($edit, TAXONOMY_ACCESS_GLOBAL_DEFAULT, TAXONOMY_ACCESS_VOCABULARY_DEFAULT, TAXONOMY_ACCESS_NODE_ALLOW);
     $this->drupalPost(NULL, $edit, 'Save all');
 
     // Log out.
@@ -553,11 +576,11 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
     // Visit each node and verify that access is allowed.
     foreach ($this->articles as $key => $article) {
       $this->drupalGet('node/' . $article->nid);
-      $this->assertResponse(200, t("Access to %name article (nid %nid) is allowed for anonymous when the global default is allow.", array('%name' => $key, '%nid' => $article->nid)));
+      $this->assertResponse(200, t("Access to %name article (nid %nid) is allowed.", array('%name' => $key, '%nid' => $article->nid)));
     }
     foreach ($this->pages as $key => $page) {
       $this->drupalGet('node/' . $page->nid);
-      $this->assertResponse(200, t("Access to %name page (nid %nid) is allowed for anonymous when the global default is allow.", array('%name' => $key, '%nid' => $page->nid)));
+      $this->assertResponse(200, t("Access to %name page (nid %nid) is allowed.", array('%name' => $key, '%nid' => $page->nid)));
     }
 
     // Add some specific configurations programmatically.
@@ -585,8 +608,7 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
     // Use the admin form to give anonymous view deny in the global default.
     $this->drupalGet(TAXONOMY_ACCESS_CONFIG . '/role/' . DRUPAL_ANONYMOUS_RID . '/edit');
     $edit = array();
-    $this->configureFormRow($edit, TAXONOMY_ACCESS_GLOBAL_DEFAULT,
-      TAXONOMY_ACCESS_VOCABULARY_DEFAULT, TAXONOMY_ACCESS_NODE_DENY);
+    $this->configureFormRow($edit, TAXONOMY_ACCESS_GLOBAL_DEFAULT, TAXONOMY_ACCESS_VOCABULARY_DEFAULT, TAXONOMY_ACCESS_NODE_DENY);
     $this->drupalPost(NULL, $edit, 'Save all');
 
     // Log out.
@@ -595,7 +617,7 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
     // Visit each artile and verify that access is denied.
     foreach ($this->articles as $key => $article) {
       $this->drupalGet('node/' . $article->nid);
-      $this->assertResponse(403, t("Access to %name article (nid %nid) is denied for anonymous after granting access to some terms and setting the global default to deny.", array('%name' => $key, '%nid' => $article->nid)));
+      $this->assertResponse(403, t("Access to %name article (nid %nid) is denied.", array('%name' => $key, '%nid' => $article->nid)));
     }
 
     // Visit each page.
@@ -607,12 +629,12 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
         case ($key == 'no_tags'):
         // If the page is tagged with v2t2, access should be denied.
         case (strpos($key, 'v2t2') !== FALSE):
-          $this->assertResponse(403, t("Access to %name page (nid %nid) is denied for anonymous after granting access to some terms and setting the global default to deny.", array('%name' => $key, '%nid' => $page->nid)));
+          $this->assertResponse(403, t("Access to %name page (nid %nid) is denied.", array('%name' => $key, '%nid' => $page->nid)));
           break;
 
         // Otherwise, access should be allowed.
         default:
-          $this->assertResponse(200, t("Access to %name page (nid %nid) is allowed for anonymous after granting access to some terms and setting the global default to deny.", array('%name' => $key, '%nid' => $page->nid)));
+          $this->assertResponse(200, t("Access to %name page (nid %nid) is allowed.", array('%name' => $key, '%nid' => $page->nid)));
           break;
       }
     }
@@ -622,7 +644,8 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
    * Tests configuring vocabulary defaults.
    *
    * Verifies that:
-   * - Access is updated correctly when the vocabulary default is added.
+   * - Access is updated correctly when the vocabulary default is added and
+   *   configured.
    * - Access is updated correctly when there is a specific term configuration
    *   in the vocabulary.
    * - Access is updated correctly when multiple defaults are changed.
@@ -632,11 +655,23 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
     // Log in as the administrator.
     $this->drupalLogin($this->users['site_admin']);
 
-    // Use the admin form to give anonymous view allow for the v1 default.
+    // Enable the vocabulary.
     $this->drupalGet(TAXONOMY_ACCESS_CONFIG . '/role/' . DRUPAL_ANONYMOUS_RID . '/edit');
+    // @todo
+    //   - Ensure that all vocabularies are options in the "Add" fieldset.
     $edit = array();
-    $this->addFormRow($edit, $this->vocabs['v1']->vid, TAXONOMY_ACCESS_VOCABULARY_DEFAULT, TAXONOMY_ACCESS_NODE_ALLOW);
-    $this->drupalPost(NULL, $edit, 'Add');
+    $edit['enable_vocab'] = $this->vocabs['v1']->vid;
+    $this->drupalPost(NULL, $edit, t('Add'));
+
+    // @todo
+    //   - Ensure that the vocabulary is removed from the "Add" fieldset.
+    //   - Ensure that the fieldset for the vocabulary appears.
+    //   - Ensure that no other fieldsets or rows appear.
+
+    // Give anonymous view allow for the v1 default.
+    $edit = array();
+    $this->configureFormRow($edit, $this->vocabs['v1']->vid, TAXONOMY_ACCESS_VOCABULARY_DEFAULT, TAXONOMY_ACCESS_NODE_ALLOW);
+    $this->drupalPost(NULL, $edit, 'Save all');
 
     // Log out.
     $this->drupalLogout();
@@ -647,19 +682,19 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
 
       // If the page is tagged with a v1 term, access should be allowed.
       if (strpos($key, 'v1') !== FALSE) {
-        $this->assertResponse(200, t("Access to %name page (nid %nid) is allowed for anonymous users after setting the v1 default to allow.", array('%name' => $key, '%nid' => $page->nid)));
+        $this->assertResponse(200, t("Access to %name page (nid %nid) is allowed.", array('%name' => $key, '%nid' => $page->nid)));
       }
       // Otherwise, access should be denied.
       else {
-        $this->assertResponse(403, t("Access to %name page (nid %nid) is denied for anonymous users after setting the v1 default to allow.", array('%name' => $key, '%nid' => $page->nid)));
+        $this->assertResponse(403, t("Access to %name page (nid %nid) is denied.", array('%name' => $key, '%nid' => $page->nid)));
       }
     }
 
-    // Add a specific configuration for v2t1 programmatically.
+    // Programmatically enable v2 and add a specific configuration for v2t1.
+    taxonomy_access_enable_vocab($this->vocabs['v2']->vid, DRUPAL_ANONYMOUS_RID);
     $term_config = _taxonomy_access_format_grant_record(
       $this->terms['v2t1']->tid, DRUPAL_ANONYMOUS_RID, array('view' => TAXONOMY_ACCESS_NODE_IGNORE)
     );
-
     taxonomy_access_set_term_grants(array($term_config));
 
     // Log in as the administrator.
@@ -668,12 +703,13 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
     // Use the admin form to give anonymous view deny for the v2 default.
     $this->drupalGet(TAXONOMY_ACCESS_CONFIG . '/role/' . DRUPAL_ANONYMOUS_RID . '/edit');
     $edit = array();
-    $this->addFormRow($edit, $this->vocabs['v2']->vid, TAXONOMY_ACCESS_VOCABULARY_DEFAULT, TAXONOMY_ACCESS_NODE_DENY);
-    $this->drupalPost(NULL, $edit, 'Add');
+    $this->configureFormRow($edit, $this->vocabs['v2']->vid, TAXONOMY_ACCESS_VOCABULARY_DEFAULT, TAXONOMY_ACCESS_NODE_DENY);
+    $this->drupalPost(NULL, $edit, 'Save all');
+
+    $this->drupalGet(TAXONOMY_ACCESS_CONFIG . '/role/' . DRUPAL_ANONYMOUS_RID . '/edit');
 
     // Log out.
     $this->drupalLogout();
-
     // Visit each page and verify whether access is allowed or denied.
     foreach ($this->pages as $key => $page) {
       $this->drupalGet('node/' . $page->nid);
@@ -681,17 +717,17 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
       switch (TRUE) {
         // If the page is tagged with v2t2, the v2 default is inherited: Deny.
         case (strpos($key, 'v2t2') !== FALSE):
-          $this->assertResponse(403, t("Access to %name page (nid %nid) is denied for anonymous users after setting the v2 default to deny.", array('%name' => $key, '%nid' => $page->nid)));
+          $this->assertResponse(403, t("Access to %name page (nid %nid) is denied.", array('%name' => $key, '%nid' => $page->nid)));
           break;
 
         // Otherwise, if the page is tagged with v1, it's allowed.
         case (strpos($key, 'v1') !== FALSE):
-          $this->assertResponse(200, t("Access to %name page (nid %nid) is allowed for anonymous users after setting the v2 default to deny.", array('%name' => $key, '%nid' => $page->nid)));
+          $this->assertResponse(200, t("Access to %name page (nid %nid) is allowed.", array('%name' => $key, '%nid' => $page->nid)));
           break;
 
         // Access should be denied by default.
         default:
-          $this->assertResponse(403, t("Access to %name page (nid %nid) is denied for anonymous users after setting the v2 default to deny.", array('%name' => $key, '%nid' => $page->nid)));
+          $this->assertResponse(403, t("Access to %name page (nid %nid) is denied.", array('%name' => $key, '%nid' => $page->nid)));
           break;
       }
     }
@@ -716,18 +752,18 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
       switch (TRUE) {
         // If the page is tagged with a v1 term, access should be denied.
         case (strpos($key, 'v1') !== FALSE):
-          $this->assertResponse(403, t("Access to %name page (nid %nid) is denied for anonymous users after changing the default configuration for v1 and v2.", array('%name' => $key, '%nid' => $page->nid)));
+          $this->assertResponse(403, t("Access to %name page (nid %nid) is denied.", array('%name' => $key, '%nid' => $page->nid)));
           break;
 
         // Otherwise, if the page is tagged with v2t2, the default is
         // inherited and access should be allowed.
         case (strpos($key, 'v2t2') !== FALSE):
-          $this->assertResponse(200, t("Access to %name page (nid %nid) is allowed for anonymous users after changing the default configuration for v1 and v2.", array('%name' => $key, '%nid' => $page->nid)));
+          $this->assertResponse(200, t("Access to %name page (nid %nid) is allowed.", array('%name' => $key, '%nid' => $page->nid)));
           break;
 
         // Access should be denied by default.
         default:
-          $this->assertResponse(403, t("Access to %name page (nid %nid) is denied for anonymous users after changing the default configuration for v1 and v2.", array('%name' => $key, '%nid' => $page->nid)));
+          $this->assertResponse(403, t("Access to %name page (nid %nid) is denied.", array('%name' => $key, '%nid' => $page->nid)));
           break;
       }
     }
@@ -735,11 +771,11 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
     // Log in as the administrator.
     $this->drupalLogin($this->users['site_admin']);
 
-    // Use the form to delete the v1 default.
+    // Use the admin form to disable v1.
     $this->drupalGet(TAXONOMY_ACCESS_CONFIG . '/role/' . DRUPAL_ANONYMOUS_RID . '/edit');
-    $edit = array();
-    $edit["selected_defaults[{$this->vocabs['v1']->vid}]"] = 1;
-    $this->drupalPost(NULL, $edit, 'Delete selected');
+    $this->clickLink(t('delete all v1 access rules'));
+    $this->assertText("Are you sure you want to delete all Taxonomy access rules for v1", t('Disable form for vocabulary loaded.'));
+    $this->drupalPost(NULL, array(), 'Delete all');
 
     // Log out.
     $this->drupalLogout();
@@ -750,11 +786,11 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
 
       // If the page is tagged with v2t2, access should be allowed.
       if (strpos($key, 'v2t2') !== FALSE) {
-        $this->assertResponse(200, t("Access to %name page (nid %nid) is allowed for anonymous users after deleting the v1 default.", array('%name' => $key, '%nid' => $page->nid)));
+        $this->assertResponse(200, t("Access to %name page (nid %nid) is allowed.", array('%name' => $key, '%nid' => $page->nid)));
       }
       // Otherwise, access should be denied.
       else {
-        $this->assertResponse(403, t("Access to %name page (nid %nid) is denied for anonymous users after deleting the v1 default.", array('%name' => $key, '%nid' => $page->nid)));
+        $this->assertResponse(403, t("Access to %name page (nid %nid) is denied.", array('%name' => $key, '%nid' => $page->nid)));
       }
     }
   }
@@ -772,9 +808,12 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
     // Log in as the administrator.
     $this->drupalLogin($this->users['site_admin']);
 
-    // Use the admin form to give anonymous view allow for v1t1.
+    // Use the admin form to enable v1 and give anonymous view allow for v1t1.
     $this->drupalGet(TAXONOMY_ACCESS_CONFIG . '/role/' . DRUPAL_ANONYMOUS_RID . '/edit');
     $edit = array();
+    $edit['enable_vocab'] = $this->vocabs['v1']->vid;
+    $this->drupalPost(NULL, $edit, t('Add'));
+    $edit = array();
     $this->addFormRow($edit, $this->vocabs['v1']->vid, $this->terms['v1t1']->tid, TAXONOMY_ACCESS_NODE_ALLOW);
     $this->drupalPost(NULL, $edit, 'Add');
 
@@ -787,20 +826,16 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
 
       // If the page is tagged with v1t1, access should be allowed.
       if (strpos($key, 'v1t1') !== FALSE) {
-        $this->assertResponse(200, t("Access to %name page (nid %nid) is allowed for anonymous users after granting allow for v1t1.", array('%name' => $key, '%nid' => $page->nid)));
+        $this->assertResponse(200, t("Access to %name page (nid %nid) is allowed.", array('%name' => $key, '%nid' => $page->nid)));
       }
       // Otherwise, access should be denied.
       else {
-        $this->assertResponse(403, t("Access to %name page (nid %nid) is denied for anonymous users after granting allow for v1t1.", array('%name' => $key, '%nid' => $page->nid)));
+        $this->assertResponse(403, t("Access to %name page (nid %nid) is denied.", array('%name' => $key, '%nid' => $page->nid)));
       }
     }
 
-    // Add a vocabulary default for v2 programmatically.
-    $default_config = _taxonomy_access_format_grant_record(
-      $this->vocabs['v2']->vid, DRUPAL_ANONYMOUS_RID, array('view' => TAXONOMY_ACCESS_NODE_IGNORE), TRUE
-    );
-
-    taxonomy_access_set_default_grants(array($default_config));
+    // Enable v2 programmatically.
+    taxonomy_access_enable_vocab($this->vocabs['v2']->vid, DRUPAL_ANONYMOUS_RID);
 
     // Log in as the administrator.
     $this->drupalLogin($this->users['site_admin']);
@@ -821,17 +856,17 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
       switch (TRUE) {
         // If the page is tagged with v2t1, access should be denied.
         case (strpos($key, 'v2t1') !== FALSE):
-          $this->assertResponse(403, t("Access to %name page (nid %nid) is denied for anonymous users after configuring v2t1 for deny.", array('%name' => $key, '%nid' => $page->nid)));
+          $this->assertResponse(403, t("Access to %name page (nid %nid) is denied.", array('%name' => $key, '%nid' => $page->nid)));
           break;
 
         // Otherwise, if the page is tagged with v1t1, it's allowed.
         case (strpos($key, 'v1t1') !== FALSE):
-          $this->assertResponse(200, t("Access to %name page (nid %nid) is allowed for anonymous users after configuring v2t1 for deny.", array('%name' => $key, '%nid' => $page->nid)));
+          $this->assertResponse(200, t("Access to %name page (nid %nid) is allowed.", array('%name' => $key, '%nid' => $page->nid)));
           break;
 
         // Access should be denied by default.
         default:
-          $this->assertResponse(403, t("Access to %name page (nid %nid) is denied for anonymous users after configuring v2t1 for deny.", array('%name' => $key, '%nid' => $page->nid)));
+          $this->assertResponse(403, t("Access to %name page (nid %nid) is denied.", array('%name' => $key, '%nid' => $page->nid)));
           break;
       }
     }
@@ -860,17 +895,17 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
       switch (TRUE) {
         // If the page is tagged with v1t1, access should be denied.
         case (strpos($key, 'v1t1') !== FALSE):
-          $this->assertResponse(403, t("Access to %name page (nid %nid) is denied for anonymous users after reconfiguring v1t1 and v2t1.", array('%name' => $key, '%nid' => $page->nid)));
+          $this->assertResponse(403, t("Access to %name page (nid %nid) is denied.", array('%name' => $key, '%nid' => $page->nid)));
           break;
 
         // Otherwise, if the page is tagged with v2t1, it's allowed.
         case (strpos($key, 'v2t1') !== FALSE):
-          $this->assertResponse(200, t("Access to %name page (nid %nid) is allowed for anonymous users after reconfiguring v1t1 and v2t1.", array('%name' => $key, '%nid' => $page->nid)));
+          $this->assertResponse(200, t("Access to %name page (nid %nid) is allowed.", array('%name' => $key, '%nid' => $page->nid)));
           break;
 
         // Access should be denied by default.
         default:
-          $this->assertResponse(403, t("Access to %name page (nid %nid) is denied for anonymous users after reconfiguring v1t1 and v2t1.", array('%name' => $key, '%nid' => $page->nid)));
+          $this->assertResponse(403, t("Access to %name page (nid %nid) is denied.", array('%name' => $key, '%nid' => $page->nid)));
           break;
       }
     }
@@ -881,7 +916,7 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
     // Use the form to delete the v2t1 configuration.
     $this->drupalGet(TAXONOMY_ACCESS_CONFIG . '/role/' . DRUPAL_ANONYMOUS_RID . '/edit');
     $edit = array();
-    $edit["selected_terms[{$this->terms['v2t1']->tid}]"] = 1;
+    $edit["grants[{$this->vocabs['v2']->vid}][{$this->terms['v2t1']->tid}][remove]"] = 1;
     $this->drupalPost(NULL, $edit, 'Delete selected');
 
     // Log out.
@@ -892,12 +927,15 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
       $this->drupalGet('node/' . $page->nid);
 
       // Access to all pages should be denied.
-      $this->assertResponse(403, t("Access to %name page (nid %nid) is denied for anonymous users after deleting all term configurations.", array('%name' => $key, '%nid' => $page->nid)));
+      $this->assertResponse(403, t("Access to %name page (nid %nid) is denied.", array('%name' => $key, '%nid' => $page->nid)));
     }
   }
 
   /**
    * Tests adding a term configuration with children.
+   *
+   * @todo
+   *   Check that node access is updated for these as well.
    */
   public function testTermWithChildren() {
     // Create some additional taxonomy terms in a hierarchy:
@@ -938,10 +976,12 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
     // Log in as the administrator.
     $this->drupalLogin($this->users['site_admin']);
 
+    // Enable v1 programmatically.
+    taxonomy_access_enable_vocab($this->vocabs['v1']->vid, DRUPAL_ANONYMOUS_RID);
     // Use the admin form to give anonymous view allow for v1t1 and children.
     $this->drupalGet(TAXONOMY_ACCESS_CONFIG . '/role/' . DRUPAL_ANONYMOUS_RID . '/edit');
     $edit = array();
-    $edit["new[recursive]"] = 1;
+    $edit["new[{$this->vocabs['v1']->vid}][recursive]"] = 1;
     $this->addFormRow($edit, $this->vocabs['v1']->vid, $this->terms['v1t1']->tid, TAXONOMY_ACCESS_NODE_ALLOW);
     $this->drupalPost(NULL, $edit, 'Add');
 
@@ -955,29 +995,46 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
     $rid = $this->user_roles['regular_user']->rid;
     $name = $this->user_roles['regular_user']->name;
 
-    // Log in as the administrator.
-    $this->drupalLogout();
-    $this->drupalLogin($this->users['site_admin']);
+    // Check that the role is disabled by default.
+    $this->checkRoleConfig(array(
+      DRUPAL_ANONYMOUS_RID => TRUE,
+      DRUPAL_AUTHENTICATED_RID => TRUE,
+      $rid => FALSE,
+    ));
 
-    // Enable TAC for the regular_user role.
+    // Test enabling the role.
     $this->drupalGet(TAXONOMY_ACCESS_CONFIG . "/role/$rid/edit");
-    $this->assertRaw("<title>Access rules for $name", t('Role configuration for regular_user (%name) enabled successfully.', array('%name' => $name)));
+
+    // Check that there is:
+    // - An enable link
+    // - No disable link
+    // @todo
+    //   - No grant tables.
+    $this->checkRoleEnableLink($rid, TRUE);
+    $this->checkRoleDisableLink($rid, FALSE);
+
+    // Enable the role and check that there is:
+    // - A disable link
+    // - No enable link
+    // @todo
+    //   - A global default table (with correct values?)
+    //   - An "Add vocabulary" fieldset.
+    //   - No vocabulary fieldsets or term data.
+    $this->clickLink(format_string('Enable @name', array('@name' => $name)));
+    $this->checkRoleEnableLink($rid, FALSE);
+    $this->checkRoleDisableLink($rid, TRUE);
 
     // Update the global default to allow view.
     $edit = array();
-    $this->configureFormRow($edit, TAXONOMY_ACCESS_GLOBAL_DEFAULT,
-      TAXONOMY_ACCESS_VOCABULARY_DEFAULT, TAXONOMY_ACCESS_NODE_ALLOW);
+    $this->configureFormRow($edit, TAXONOMY_ACCESS_GLOBAL_DEFAULT, TAXONOMY_ACCESS_VOCABULARY_DEFAULT, TAXONOMY_ACCESS_NODE_ALLOW);
     $this->drupalPost(NULL, $edit, 'Save all');
 
-    // Confirm that the edit and disable links appear.
-    $this->checkRoleEditEnable($rid);
-    $this->checkRoleDisable($rid);
-
-    // Confirm that the anon. and auth. links are unaffected.
-    $this->checkConfigAnonAuthLinks();
-
-    // Confirm that there is only one disable link.
-    $this->assertUniqueText('disable', t('There is only one disable link.'));
+    // Confirm that all three roles are enabled.
+    $this->checkRoleConfig(array(
+      DRUPAL_ANONYMOUS_RID => TRUE,
+      DRUPAL_AUTHENTICATED_RID => TRUE,
+      $rid => TRUE,
+    ));
 
     // Check that the role is configured.
     $r =
@@ -996,40 +1053,54 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
     // Visit each node and verify that access is allowed.
     foreach ($this->articles as $key => $article) {
       $this->drupalGet('node/' . $article->nid);
-      $this->assertResponse(200, t("Access to %name article (nid %nid) is allowed for a regular user after granting allow in the global default.", array('%name' => $key, '%nid' => $article->nid)));
+      $this->assertResponse(200, t("Access to %name article (nid %nid) is allowed.", array('%name' => $key, '%nid' => $article->nid)));
     }
     foreach ($this->pages as $key => $page) {
       $this->drupalGet('node/' . $page->nid);
-      $this->assertResponse(200, t("Access to %name page (nid %nid) is allowed for a regular user after granting allow in the global default.", array('%name' => $key, '%nid' => $page->nid)));
+      $this->assertResponse(200, t("Access to %name page (nid %nid) is allowed.", array('%name' => $key, '%nid' => $page->nid)));
     }
 
     // Log in as the administrator.
     $this->drupalLogout();
     $this->drupalLogin($this->users['site_admin']);
 
-    // Disable the role.
-    $this->drupalGet(TAXONOMY_ACCESS_CONFIG);
-    $this->clickLink('disable access control');
+    // Test disabling the role.
+    $this->drupalGet(TAXONOMY_ACCESS_CONFIG . "/role/$rid/edit");
+    $this->clickLink(t('Disable @name', array('@name' => $name)));
     $this->assertText("Are you sure you want to delete all taxonomy access rules for the role $name", t('Disable form for role loaded.'));
     $this->drupalPost(NULL, array(), 'Delete all');
 
     // Confirm that a confirmation message appears.
     $this->assertText("All taxonomy access rules deleted for role $name", t('Confirmation message found.'));
 
+    // Check that there is:
+    // - An enable link
+    // - No disable link
+    // @todo
+    //   - No grant tables.
+    $this->checkRoleEnableLink($rid, TRUE);
+    $this->checkRoleDisableLink($rid, FALSE);
+
     // Confirm edit/enable/disable links are in their original state.
-    $this->checkConfigAnonAuthLinks();
-    $this->checkConfigCustomRoleLinks();
+    $this->checkRoleConfig(array(
+      DRUPAL_ANONYMOUS_RID => TRUE,
+      DRUPAL_AUTHENTICATED_RID => TRUE,
+      $rid => FALSE,
+    ));
 
     // Check that the role is no longer configured.
     $r =
       db_query(
         'SELECT grant_view FROM {taxonomy_access_default}
-         WHERE vid = :vid AND rid = :rid',
-        array(':vid' => TAXONOMY_ACCESS_GLOBAL_DEFAULT, ':rid' => $rid)
+         WHERE rid = :rid',
+        array(':rid' => $rid)
       )
-      ->fetchField();
+      ->fetchAll();
     $this->assertTrue(empty($r), t('All records removed for role %role.', array('%role' => $name)));
 
+    // @todo
+    //   - Add a term configuration and make sure that gets deleted too.
+
     // Log in as the regular_user.
     $this->drupalLogout();
     $this->drupalLogin($this->users['regular_user']);
@@ -1037,11 +1108,11 @@ class TaxonomyAccessConfigTest extends TaxonomyAccessTestCase {
     // Visit all nodes and verify that access is again denied.
     foreach ($this->articles as $key => $article) {
       $this->drupalGet('node/' . $article->nid);
-      $this->assertResponse(403, t("Access to %name article (nid %nid) is denied for a regular user after disabling the role.", array('%name' => $key, '%nid' => $article->nid)));
+      $this->assertResponse(403, t("Access to %name article (nid %nid) is denied.", array('%name' => $key, '%nid' => $article->nid)));
     }
     foreach ($this->pages as $key => $page) {
       $this->drupalGet('node/' . $page->nid);
-      $this->assertResponse(403, t("Access to %name page (nid %nid) is denied for a regular user after disabling the role.", array('%name' => $key, '%nid' => $page->nid)));
+      $this->assertResponse(403, t("Access to %name page (nid %nid) is denied.", array('%name' => $key, '%nid' => $page->nid)));
     }
   }
 }
@@ -1301,9 +1372,9 @@ class TaxonomyAccessTermGrantTest extends TaxonomyAccessTestCase {
 
     // Grant all rows view, update, and delete.
     foreach ($grant_combos as $combo) {
-      $combo['view'] = TAXONOMY_ACCESS_TERM_ALLOW;
-      $combo['update'] = TAXONOMY_ACCESS_TERM_ALLOW;
-      $combo['delete'] = TAXONOMY_ACCESS_TERM_ALLOW;
+      $combo['view'] = TAXONOMY_ACCESS_NODE_ALLOW;
+      $combo['update'] = TAXONOMY_ACCESS_NODE_ALLOW;
+      $combo['delete'] = TAXONOMY_ACCESS_NODE_ALLOW;
     }
 
     // Each vocabulary will have four parent terms in the same fashion:
