You are here

taxonomy_access.module in Taxonomy Access Control 5

Allows administrators to specify how each category (in the taxonomy) can be used by various roles.

File

taxonomy_access.module
View source
<?php

// Based on original taxonomy_access.module made by pyromanfo

/**
 * @file
 * Allows administrators to specify how each category (in the taxonomy) can be used by various roles.
 */

/**
 * Implementation of hook_help
 */
function taxonomy_access_help($section) {
  global $tac_user_roles;
  switch ($section) {

    // Creates the header content for the taxonomy_access settings page dependent upon
    // whether the node is enabled or disabled.
    // Note: the form that appears on this page is not generated by this fucntion.
    case 'admin/settings/taxonomy_access':
      $message = 'Some new options coming soon...';
      return $message;
    case 'admin/help#taxonomy_access':
      $message = '<p>' . t('The Taxonomy Access Control module allows users to specify how each category can be used by various roles.') . '</p>';
      $message .= '<p>' . t('Permissions can be set differently to each USER ROLES. Be aware that setting Taxonony Access permissions works ONLY WITHIN ONE USER ROLE. <br /><em>(For users with multiple user roles, see section "GOOD TO KNOW" below.)</em>') . '</p>';
      $message .= '<p>' . t('On the category permissions page for each role, each category displays a list of the terms within it, each with five types of permission: <em>View, Update, Delete, Create</em> and <em>List</em>:') . '</p>';
      $message .= '<ul>';
      $message .= '<li>' . t('<strong>VIEW</strong> enables the user to access content (nodes) with given term.') . '</li>';
      $message .= '<li>' . t('<strong>UPDATE, DELETE</strong> enables the user to Update/Delete <u>ALL</u> nodes with the given term. <br><em>(These two permissions are <u>administrator permissions</u>, that should be given ONLY to e.g. content administrators.)</em>') . '</li>';
      $message .= '<li>' . t('<strong>CREATE</strong> enables the user to set that term when adding a new node or when editing a node.') . '</li>';
      $message .= '<li>' . t('<strong>LIST</strong> enables the user to view the name of the given term below the title of a node or in category lists.  It also controls whether a user can access the taxonomy page for the given term.  (e.g. "taxonomy/term/*")') . '</li>';
      $message .= '</ul>';
      $message .= '<p>' . t('VIEW, UPDATE, and DELETE control the node access system.  LIST and CREATE control if a user can view and select a given term.  (Note: In previous versions of Taxonomy Access Control, there was no LIST permission; its functionality was controlled by the VIEW permission.)') . '</p>';
      $message .= '<p>' . t('<strong>VIEW, UPDATE and DELETE have three options for each term: <u>A</u>llow, <u>I</u>gnore, and <u>D</u>eny.</strong>  Indicate which rights each role should have for each term.') . '</p>';
      $message .= '<p>' . t('<strong>CREATE and LIST have only two options for each term:  YES (selected) or NO (deselected).</strong>  Indicate what each role should be allowed to do with each term.') . '</p>';
      $message .= '<p>' . t('<strong>IMPORTANT NOTE:</strong><br><u>The DENY directives are processed after the ALLOW directives. (DENY overrides ALLOW.)</u>  So, if a multicategory node is in Categories "A" and "B" and a user has ALLOW permissions for VIEW in Category "A" and DENY permissions for VIEW in Category "B", then the user will NOT be permitted to VIEW the node. (DENY overrides ALLOW.)<br><u>Access is denied by default.</u> So, if a multicategory node is in Categories "C" and "D" and a user has IGNORE permissions for VIEW in both Category "C" and "D", then the user will NOT be permitted to VIEW the node.<br>(If you are familiar with Apache mod_access, this permission system works similar to directive: <em>ORDER ALLOW, DENY</em>)') . '</p>';
      $message .= '<p>' . t('<strong>Allow/Ignore/Deny All</strong> or <strong>Select/Deselect All:</strong><br>Beside each vocabulary title there are dropdowns containing the options that can be set for individual terms.  Selecting one of these options using the dropdown effectively <u>selects that option for ALL of the individual terms inside that vocabulary when the options are saved.</u><br>Selecting "--" does not make any automatic changes to the permission of the terms in that vocabulary; only manual changes that you make will be saved.<br>NOTE:  This does <u>not</u> change the "Default" option (described below).') . '</p>';
      $message .= '<p>' . t('<strong>Default:</strong><br>This option, just underneath the vocabulary title, <u>sets the permission that will automatically be given</u> to the role, <u>for any new terms</u> that are added within the vocabulary.  This includes terms that are added via free tagging.') . '</p>';
      $message .= '<p><strong>' . t('GOOD TO KNOW:') . '</strong></p>';
      $message .= '<p>' . t('<strong>Users with multiple user roles:</strong> Allow/Ignore/Deny options are interpreted <u>only within one user role</u>. When a user belongs to multiple user roles, then <u>user gets access if ANY of his user roles</u> has the access granted. <br />In this case, permissions for the given user are calculated, so that the <u>permissions of ALL of his user roles are "OR-ed" together</u>. Meaning that Allow will take precedence over Deny. This is different from how node access permissions (for multi-category nodes) are handled within ONE USER ROLE, as noted above.') . '</p>';
      $message .= '<p>' . t('<br><strong>Input formats:</strong>  <u>Node editing/deleting is blocked</u>, even when user has <em>UPDATE/DELETE</em> permission to the node, <u>when user is not allowed to use a filter format</u> that the node was saved at.') . '</p>';
      $message .= '<p>&nbsp;</p>';
      return $message;
    default:
      if (strpos($section, 'admin/user/taxonomy_access') === 0) {
        $rid = arg(3);
        if (isset($tac_user_roles[$rid]) && $tac_user_roles[$rid]) {
          $output = t('<p><strong>Vocabulary Settings:</strong> Each vocabulary displays a list of the terms within it, each with five types of permission: <em>View, Update, Delete, Create</em> and <em>List</em>.</p><p>For a detailed description of these permissions and how to use them, see <a href="@taxonomy_access_help">Taxonomy Access Control help</a>. If you are new to Taxonomy Access Control, it is very important that you read the help page.</p>', array(
            '@taxonomy_access_help' => url('admin/help/taxonomy_access'),
          ));
          return $output;
        }
        else {
          return '<p>' . t('In this area you will define the permissions that each <a href="@role">user role</a> has for each category.  Each category can have <em>View, Update, Delete, Create</em> and <em>List</em> permissions set for each user role.', array(
            '@role' => url('admin/user/roles'),
          )) . '</p>';
        }
      }
  }
}

/**
 * Implementation of hook_enable().
 *
 * Need to housekeep term_access databases for new/deleted terms/users to ensure they are up-to-date.
 */
function taxonomy_access_enable() {
  _taxonomy_access_update_db();
}

/**
 * Implementation of hook_disable().
 *
 * Need to force a rebuild of the node access table when TAC is disabled
 * to ensure that its entries are removed from the table.
 */
function taxonomy_access_disable() {
  taxonomy_access_disabling(TRUE);
  $node_grants = module_implements('node_grants');

  // http://drupal.org/node/270202
  if (count($node_grants) == 1 && array_shift($node_grants) == 'taxonomy_access') {
    db_query("DELETE FROM {node_access}");

    // Not using any node_access modules. Add the default grant.
    db_query("INSERT INTO {node_access} VALUES (0, 0, 'all', 1, 0, 0)");
  }
  else {
    node_access_rebuild();
  }
}

/**
 * Simple function to make sure we don't respond with grants when disabling
 * ourselves.
 */
function taxonomy_access_disabling($set = NULL) {
  static $disabling = false;
  if ($set !== NULL) {
    $disabling = $set;
  }
  return $disabling;
}

/**
 * Implementation of hook_node_grants()
 * Gives access to taxonomies based on the taxonomy_access table
 */
function taxonomy_access_node_grants($user, $op) {
  return array(
    'term_access' => array_keys(is_array($user->roles) ? $user->roles : array(
      1 => 'anonymous user',
    )),
  );
}

/**
 * Implementation of hook_node_access_records().
 */
function taxonomy_access_node_access_records($node) {
  if (taxonomy_access_disabling() || !isset($node->nid)) {
    return;
  }
  $grants = array();
  $where = "WHERE n.nid = " . $node->nid;
  $result = db_query("SELECT n.nid, ta.rid, BIT_OR(ta.grant_view) AS grant_view, BIT_OR(ta.grant_update) AS grant_update, BIT_OR(ta.grant_delete) AS grant_delete FROM {term_node} n INNER JOIN {term_access} ta ON n.tid = ta.tid {$where} GROUP BY n.nid, ta.rid");
  while ($row = db_fetch_object($result)) {
    if ($row) {
      $grant_view = $row->grant_view == 1 ? 1 : 0;
      $grant_update = $row->grant_update == 1 ? 1 : 0;
      $grant_delete = $row->grant_delete == 1 ? 1 : 0;
      $grants[] = array(
        'realm' => 'term_access',
        'gid' => $row->rid,
        'grant_view' => $grant_view,
        'grant_update' => $grant_update,
        'grant_delete' => $grant_delete,
        'priority' => 0,
      );
    }
  }

  // Determine if node doesn't belong to a category
  $where = "AND n.nid = " . $node->nid;
  $result = db_query("SELECT n.nid, ta.* FROM {node} n LEFT JOIN {term_node} t ON t.nid=n.nid LEFT JOIN {term_access} ta ON ta.tid = 0 WHERE t.nid IS NULL {$where}");
  while ($row = db_fetch_object($result)) {
    if ($row) {
      $grant_view = $row->grant_view == 1 ? 1 : 0;
      $grant_update = $row->grant_update == 1 ? 1 : 0;
      $grant_delete = $row->grant_delete == 1 ? 1 : 0;
      $grants[] = array(
        'realm' => 'term_access',
        'gid' => $row->rid,
        'grant_view' => $grant_view,
        'grant_update' => $grant_update,
        'grant_delete' => $grant_delete,
        'priority' => 0,
      );
    }
  }
  return $grants;
}

/**
 * Implementation of hook_menu
 */
function taxonomy_access_menu($may_cache) {
  $items = array();
  if ($may_cache) {
    $items[] = array(
      'path' => 'admin/settings/taxonomy_access',
      'title' => t('Taxonomy Access: Settings'),
      //        'description' => t('Configure Taxonomy Access Control'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        '_taxonomy_access_admin_settings',
      ),
      'access' => user_access('administer site configuration'),
    );
    $items[] = array(
      'path' => 'admin/user/taxonomy_access',
      'title' => t('Taxonomy Access: Permissions'),
      'description' => t('Sophisticated access control for content items based on category'),
      'callback' => '_taxonomy_access_permissions_page',
      'access' => user_access('administer access control'),
    );
  }
  else {
    if (arg(0) == 'admin') {
      $path = drupal_get_path('module', 'taxonomy_access');
      include_once $path . '/taxonomy_access_admin.inc';
      drupal_add_css($path . '/admin.css');
    }
  }
  return $items;
}

/**
 * Implementation of hook_form_alter()
 */
function taxonomy_access_form_alter($form_id, &$form) {
  if ($form['#id'] == 'node-form' && is_numeric($form['nid']['#value'])) {
    $form['tac_protected_terms'] = array(
      '#type' => 'value',
      '#value' => taxonomy_access_preserve_terms($form['nid']['#value']),
    );
  }
}

/**
 * Updates permissions for a role for a term
 * @param $tid
 *   The term to add the permission for.
 * @param $role
 *   The role to add the permission to.
 *   Can be the name or the role id or blank for all term permissions.
 * @param $grants
 *   A hash of the grants in the form of $grants['perm'] = boolean
 *   A value of 1 will grant the permission for this user and term.
**/
function taxonomy_access_grant_update($tid, $role = null, $grants = null) {
  if (!isset($tid)) {
    return FALSE;
  }
  if (isset($role) && !is_numeric($role)) {
    $role = db_result(db_query("SELECT rid FROM {role} WHERE name='{$role}'"));
  }
  $ta_sql = "INSERT INTO {term_access} (tid";
  $ta_sql_values = " VALUES ({$tid}";
  if (isset($role)) {
    $ta_sql .= ",rid";
    $ta_sql_values .= ",{$role}";
  }
  $sql = "";
  if (isset($grants)) {
    foreach ($grants as $perm => $value) {
      $sql .= ",grant_{$perm}";
      $ta_sql_values .= is_array($value) ? "," . $value[0] : ",{$value}";
    }
    $sql .= ")";
    $ta_sql_values .= ")";
  }
  else {
    $sql .= ")";
    $ta_sql_values .= ")";
  }
  $ta_sql .= $sql . $ta_sql_values;
  db_query("DELETE FROM {term_access} WHERE tid=%d AND rid=%d", $tid, isset($role) ? $role : 0);
  db_query($ta_sql);

  // insert into term_access
}

/**
 * Updates default permissions for a role for a vocabulary
 * @param $vid
 *   The vocab to add the permission for.
 * @param $role
 *   The role to add the permission to.
 *   Can be the name or the role id or blank for all term permissions.
 * @param $grants
 *   A hash of the grants in the form of $grants['perm'] = boolean
 *   A value of 1 will grant the permission for this user and term.
**/
function taxonomy_access_defaults_update($vid, $role = null, $grants = null) {
  if (!isset($vid)) {
    return FALSE;
  }
  if (isset($role) && !is_numeric($role)) {
    $role = db_result(db_query("SELECT rid FROM {role} WHERE name='{$role}'"));
  }
  $ta_sql = "INSERT INTO {term_access_defaults} (vid";
  $ta_sql_values = " VALUES ({$vid}";
  if (isset($role)) {
    $ta_sql .= ",rid";
    $ta_sql_values .= ",{$role}";
  }
  $sql = "";
  if (isset($grants)) {
    foreach ($grants as $perm => $value) {
      $sql .= ",grant_{$perm}";
      $ta_sql_values .= ",{$value}";
    }
    $sql .= ")";
    $ta_sql_values .= ")";
  }
  else {
    $sql .= ")";
    $ta_sql_values .= ")";
  }
  $ta_sql .= $sql . $ta_sql_values;
  db_query("DELETE FROM {term_access_defaults} WHERE vid=%d AND rid=%d", $vid, isset($role) ? $role : 0);
  db_query($ta_sql);

  // insert into term_access_defaults
}

/**
 * Updates permissions for a role for all the terms in a vocabulary
 * @param $vid
 *   The vocabulary to search for terms to add the permission for.
 * @param $role
 *   The role to add the permission to.
 *   Can be the name or the role id or blank for all term permissions.
 * @param $grants
 *   A hash of the grants in the form of $grants['perm'] = boolean
 *   A value of 1 will grant the permission for this user and term.
**/
function taxonomy_access_grant_vocab_update($vid, $rid = null, $edit = null) {
  $tree = taxonomy_get_tree($vid);
  $grant_types = array(
    'view',
    'update',
    'delete',
    'create',
    'list',
  );
  $vgrants = $edit[$vid]['vocab'];
  $grants = array();
  foreach ($tree as $term) {
    foreach ($grant_types as $grant) {
      if (in_array($vgrants[$grant], array(
        0,
        1,
        2,
      ))) {
        $grants[$grant] = $vgrants[$grant];
      }
      else {
        $grants[$grant] = $edit[$term->vid]['term'][$term->tid][$grant];
      }
    }
    taxonomy_access_grant_update($term->tid, $rid, $grants);
  }
}

/**
 * Gets permissions for a given role
 * @param $role
 *   The role to retrieve the permissions for.
 *   Can be the name or the role id or blank for all term permissions.
 * @return
 *   A two dimensional hash of the form $grants[tid][grant] where
 *   tid is the term id and
 *   grant is the permission (i.e. 'view','delete',ect.)
 *   this entry in the hash is true if permission is granted, false otherwise
**/
function taxonomy_access_get_grants($role) {
  if (!isset($role)) {
    return false;
  }
  if (isset($role) && !is_numeric($role)) {
    $role = db_result(db_query("SELECT rid FROM {role} WHERE name='{$role}'"));
  }
  $result = db_query("SELECT * FROM {term_access} WHERE rid='{$role}'");
  $grants = array();
  while ($grant = db_fetch_array($result)) {
    $tid = $grant['tid'];
    foreach ($grant as $key => $grant_val) {
      if (strpos($key, 'grant_') !== FALSE) {
        $grant_name = '';
        $grant_name = str_replace('grant_', '', $key);
        if (!isset($grants[$tid][$grant_name]) || !$grants[$tid][$grant_name]) {

          // If there's conflicting DB rules, take the most lenient
          $grants[$tid][$grant_name] = $grant_val;
        }
      }
    }
  }
  return $grants;
}

/**
 * Gets default permissions for a given role
 * @param $role
 *   The role to retrieve the permissions for.
 *   Can be the name or the role id or blank for all term permissions.
 * @return
 *   A two dimensional hash of the form $grants[vid][grant] where
 *   vid is the vocab id and
 *   grant is the permission (i.e. 'view','delete',ect.)
 *   this entry in the hash is true if permission is granted, false otherwise
**/
function taxonomy_access_get_default_grants($role) {
  if (!isset($role)) {
    return false;
  }
  if (isset($role) && !is_numeric($role)) {
    $role = db_result(db_query("SELECT rid FROM {role} WHERE name='{$role}'"));
  }
  $result = db_query("SELECT * FROM {term_access_defaults} WHERE rid='{$role}'");
  $grants = array();
  while ($grant = db_fetch_array($result)) {
    $vid = $grant['vid'];
    foreach ($grant as $key => $grant_val) {
      if (strpos($key, 'grant_') !== FALSE) {
        $grant_name = '';
        $grant_name = str_replace('grant_', '', $key);
        if (!isset($grants[$vid][$grant_name]) || !$grants[$vid][$grant_name]) {

          // If there's conflicting DB rules, take the most lenient
          $grants[$vid][$grant_name] = $grant_val;
        }
      }
    }
  }
  return $grants;
}

/**
 * Implementation of hook_nodeapi().
 */
function taxonomy_access_nodeapi(&$node, $op, $arg = 0) {
  switch ($op) {
    case 'submit':

      // When TAC grants 'update' access to edit node,
      // Changing $node->uid back to original creator (changed by node_submit)
      if ($node->nid && !user_access('administer nodes') && node_access('update', $node)) {

        // Populate the "authored by" field.
        $old_node = node_load($node->nid);
        if ($account = user_load(array(
          'name' => $old_node->name,
        ))) {
          $node->uid = $account->uid;
        }
        else {
          $node->uid = 0;
        }
      }
      break;
    case 'update':

      // restore terms that the user shouldn't have access to delete
      taxonomy_access_restore_terms($node->nid, $node->tac_protected_terms);
      break;
  }
}

/**
 * Implementation of hook_taxonomy
 * Hook_taxonomy is called when changes are made to the taxonomy structure
**/
function taxonomy_access_taxonomy($op, $type, $array = NULL) {
  if ($type == 'term') {
    switch ($op) {
      case 'update':

        // don't do anything, nothing on our end has changed
        break;
      case 'insert':

        // add new default entries for the new category, don't have to touch node_access since no posts are in there yet
        foreach (user_roles() as $rid => $role) {
          $grants = db_fetch_object(db_query('SELECT * FROM {term_access_defaults} WHERE vid = %d AND rid= %d', $array['vid'], $rid));
          db_query('DELETE FROM {term_access} WHERE tid = %d AND rid = %d', $array['tid'], $rid);
          db_query('INSERT INTO {term_access} VALUES (%d, %d, %d, %d, %d, %d, %d)', $array['tid'], $rid, $grants->grant_view, $grants->grant_update, $grants->grant_delete, $grants->grant_create, $grants->grant_list);
        }
        break;
      case 'delete':

        // delete everything from term_access and node_access
        db_query('DELETE FROM {term_access} WHERE tid = %d', $array['tid']);
        node_access_rebuild();
        break;
    }
  }
  if ($type == 'vocabulary') {
    switch ($op) {
      case 'update':

        // don't do anything, nothing on our end has changed
        break;
      case 'insert':

        // add default values for the new vocabulary to table 'term_access_defaults'
        foreach (user_roles() as $rid => $role) {
          db_query('DELETE FROM {term_access_defaults} WHERE vid = %d AND rid = %d', $array['vid'], $rid);
          db_query('INSERT INTO {term_access_defaults} VALUES (%d, %d, %d, %d, %d, %d, %d)', _taxonomy_access_defaults($array['vid'], $rid));
        }
        break;
      case 'delete':

        // delete vocabulary from table 'term_access_defaults'
        db_query('DELETE FROM {term_access_defaults} WHERE vid = %d', $array['vid']);
        break;
    }
  }
  return;
}

/**
 * Implementation of hook_user
 * Hook_user is called when a user event occurs to check for new roles.  It would make sense to have a hook_role
 * instead.  However, that hook doesn't exist so we rely on the hook_user to determine if new roles have been added.
**/
function taxonomy_access_user($op, &$edit, &$user, $category = NULL) {
  if ($op == 'update' || $op == 'insert') {

    // Get list of existing roles
    $result = db_query('SELECT rid FROM {role}');
    while ($rids = db_fetch_array($result)) {
      $current_rids[] = $rids['rid'];
    }
    if (!in_array(0, $current_rids)) {
      $current_rids[] = 0;
    }

    // Get list of roles known to exist from term_access_defaults
    $known_rids = array();
    $result = db_query('SELECT DISTINCT rid FROM {term_access_defaults}');
    while ($rids = db_fetch_array($result)) {
      $known_rids[] = $rids['rid'];
    }
    if (!in_array(0, $known_rids)) {
      $known_rids[] = 0;
    }
    if (array_diff($current_rids, $known_rids)) {
      _taxonomy_access_update_db($current_rids, $known_rids);
    }
  }
}

/*
 * Housekeeping for the term_access and term_access_defaults tables
 *
 * Called when the TAC module is enabled to reflect the changes since it was disabled:
 * 1: Check for new/deleted taxonomy terms and make approprate entries into term_access table (according to term_access_defaults)
 * 2: Check for new/deleted user roles and make approprate entries into term_access table
 * 3: Check for new/deleted taxonomy vocabularies and user roles to update term_access_defaults table
 * 4: Force to rebuild the node_access table based on the new changes in the Taxonomy Access system
 */
function _taxonomy_access_update_db($current_rids = NULL, $old_rids = NULL) {

  // BEGIN: term_access table housekeeping
  // Update the term_access table to reflect any changes that may have occured since module was disabled.
  $tids = array();
  $rids = array();

  // Create list of all term and role ids
  $query = db_query('SELECT tid FROM {term_data}');
  while ($result = db_fetch_array($query)) {
    $current_tids[] = $result['tid'];
  }
  $current_tids[] = 0;
  if (!$current_rids) {
    $query = db_query('SELECT rid FROM {role}');
    while ($result = db_fetch_array($query)) {
      $current_rids[] = $result['rid'];
    }

    // $current_rids[] = 0; no default non-roled access
  }
  $current_vids = array();
  $query = db_query('SELECT vid FROM {vocabulary}');
  while ($result = db_fetch_array($query)) {
    $current_vids[] = $result['vid'];
  }

  // Get old list of term and role ids from when term_access was disabled
  $old_tids[] = 0;
  $query = db_query('SELECT DISTINCT(tid) FROM {term_access}');
  while ($result = db_fetch_array($query)) {
    $old_tids[] = $result['tid'];
  }
  if (!$old_rids) {
    $query = db_query('SELECT DISTINCT(rid) FROM {term_access_defaults}');
    while ($result = db_fetch_array($query)) {
      $old_rids[] = $result['rid'];
    }
  }
  $old_vids = array();
  $query = db_query('SELECT DISTINCT(vid) FROM {term_access_defaults}');
  while ($result = db_fetch_array($query)) {
    $old_vids[] = $result['vid'];
  }

  // Get the difference between old and current
  $delete_tids = $delete_rids = $delete_vids = array();
  if (is_array($old_tids)) {
    $delete_tids = array_diff($old_tids, $current_tids);
  }
  if (is_array($old_rids)) {
    $delete_rids = array_diff($old_rids, $current_rids);
  }
  if (is_array($old_vids)) {
    $delete_vids = array_diff($old_vids, $current_vids);
  }

  // Delete all rows with role and term ids that no longer exist from the term_access table
  foreach ($delete_rids as $rid) {
    db_query('DELETE FROM {term_access} WHERE rid = %d', $rid);
  }
  foreach ($delete_tids as $tid) {
    db_query('DELETE FROM {term_access} WHERE tid = %d', $tid);
  }

  // Set permissions for nodes without categories if they aren't already set
  $query = db_query('SELECT tid FROM {term_access} where tid = 0');
  if (!db_result($query) && is_array($current_rids)) {
    foreach ($current_rids as $rid) {
      $query = db_query('SELECT tid FROM {term_access} where tid = 0 AND rid = %d', $rid);
      if (!db_fetch_object($query)) {
        db_query('INSERT INTO {term_access} VALUES (%d, %d, %d, %d, %d, %d, %d)', _taxonomy_access_defaults(0, $rid));
      }
    }
  }

  // Add new role and term ids to term_access table since module was last disabled and assign them default permissions
  $all_rids = $add_tids = $add_rids = $add_vids = array();
  $add_tids = array_diff($current_tids, $old_tids);
  $add_vids = array_diff($current_vids, $old_vids);
  if (is_array($current_rids) && is_array($old_rids)) {
    $add_rids = array_diff($current_rids, $old_rids);
    $all_rids = array_merge($add_rids, $current_rids);
  }

  // Add role permissions for each new taxonomy terms.
  // nysus : Default permissions assume all users can not read content in the new taxonomy
  // pyromanfo : Drupal default is actually view only, as is the case with node_access
  foreach ($add_tids as $tid) {
    if ($tid != 0) {
      foreach ($all_rids as $rid) {
        db_query('DELETE FROM {term_access} WHERE tid = %d AND rid = %d', $tid, $rid);
        db_query('INSERT INTO {term_access} VALUES (%d, %d, %d, %d, %d, %d, %d)', _taxonomy_access_defaults($tid, $rid));
      }
    }
  }

  // Add role permissions for all old taxonomy terms.
  // nysus : Default permissions assume new role does not have access to content in any category
  // pyromanfo : Drupal default is actually view only, as is the case with node_access
  foreach ($current_tids as $tid) {
    if ($tid != 0) {
      foreach ($add_rids as $rid) {
        db_query('DELETE FROM {term_access} WHERE tid = %d AND rid = %d', $tid, $rid);
        db_query('INSERT INTO {term_access} VALUES (%d, %d, %d, %d, %d, %d, %d)', _taxonomy_access_defaults($tid, $rid));
      }
    }
  }

  // END: term_access table housekeeping
  // BEGIN: term_access_defaults housekeeping
  // Setup defaults
  foreach ($delete_rids as $rid) {
    db_query('DELETE FROM {term_access_defaults} WHERE rid = %d', $rid);
  }
  foreach ($delete_vids as $vid) {
    db_query('DELETE FROM {term_access_defaults} WHERE vid = %d', $vid);
  }
  if (count($add_rids)) {
    drupal_set_message(t('Taxonomy Access: Please <a href="@settings">check default permissions</a> of new user role(s).', array(
      '@settings' => url('admin/user/taxonomy_access'),
    )));
    foreach ($current_vids as $vid) {
      foreach ($add_rids as $rid) {
        db_query('DELETE FROM {term_access_defaults} WHERE vid = %d AND rid = %d', $vid, $rid);
        db_query('INSERT INTO {term_access_defaults} VALUES (%d, %d, %d, %d, %d, %d, %d)', _taxonomy_access_defaults($vid, $rid));
      }
    }
  }
  if (count($add_vids)) {
    drupal_set_message(t('Taxonomy Access: Please <a href="@settings">check default permissions</a> of new vocabulary.', array(
      '@settings' => url('admin/user/taxonomy_access'),
    )));
    foreach ($add_vids as $vid) {
      if (is_array($current_rids)) {
        foreach ($current_rids as $rid) {
          db_query('DELETE FROM {term_access_defaults} WHERE vid = %d AND rid = %d', $vid, $rid);
          db_query('INSERT INTO {term_access_defaults} VALUES (%d, %d, %d, %d, %d, %d, %d)', _taxonomy_access_defaults($vid, $rid));
        }
      }
    }
  }

  // END: term_access_defaults housekeeping
  // Force to rebuild node_access_table to reflect the changes made to term_access table
  node_access_rebuild();
}

/**
 * Provide default values for term_access_defaults
 */
function _taxonomy_access_defaults($vid, $rid) {
  if ($rid == 1 || $rid == 2) {
    return array(
      $vid,
      $rid,
      1,
      0,
      0,
      1,
      1,
    );
  }
  else {
    return array(
      $vid,
      $rid,
      0,
      0,
      0,
      0,
      0,
    );
  }
}

/**
 * Implementation of hook_db_rewrite_sql()
 */
function taxonomy_access_db_rewrite_sql($query, $table, $field) {
  if (!user_access('administer taxonomy') && ($field == 'vid' || $field == 'tid')) {
    global $user;
    $op = arg(0) == 'node' && (arg(1) == 'add' || arg(2) == 'edit') ? 'create' : 'list';

    // let's cache
    static $taxonomy_access_sql_clause;
    $clause = array();
    if (!isset($taxonomy_access_sql_clause)) {
      $taxonomy_access_sql_clause = array();
    }
    if (!isset($taxonomy_access_sql_clause[$op][$field])) {
      if (isset($user) && is_array($user->roles)) {
        $rids = array_keys($user->roles);
      }
      else {
        $rids[] = 1;
      }
      $sql = db_query("SELECT t.tid, d.vid, BIT_OR(t.grant_{$op}) AS grant_{$op} FROM {term_access} t INNER JOIN {term_data} d ON t.tid = d.tid WHERE t.rid IN ('" . implode("','", $rids) . "') AND grant_{$op} = 1 GROUP BY t.tid, d.vid");
      while ($result = db_fetch_object($sql)) {
        $tids[] = $result->tid;
        $vids[$result->vid] = $result->vid;
      }
      $clause[$op]['tid'] = isset($tids) ? implode("','", $tids) : '';
      $clause[$op]['vid'] = isset($vids) ? implode("','", $vids) : '';
      $taxonomy_access_sql_clause = $clause;
    }
    else {
      $clause[$op][$field] = $taxonomy_access_sql_clause[$op][$field];
    }
    $return['where'] = $clause[$op][$field] ? "{$table}.{$field} IN ('" . $clause[$op][$field] . "')" : "{$table}.{$field} IS NULL";
    return $return;
  }
  else {
    return array();
  }
}

/**
 * used to preserve terms deleted by taxonomy_node_delete() 
 * that the user shouldn't have access to delete.
 * see http://drupal.org/node/92355 and http://drupal.org/node/93086
 *
 * Called by hook_form_alter()
 */
function taxonomy_access_preserve_terms($nid = FALSE) {

  // prepare/cache return value
  static $tids = array();

  // a valid numeric nid is required
  if (!is_numeric($nid)) {
    return array();
  }

  // use cached values if possible
  if (isset($tids[$nid])) {
    return $tids[$nid];
  }

  // get a list of terms this user has access to/over
  // (invokes hook_db_rewrite_sql() to limit access)
  $user_terms = taxonomy_node_get_terms($nid);

  // get a list of all terms this node is in regardless of user's
  // access settings. Don't use db_rewrite_sql() api call here (or
  // any other API call, the taxonomy API functions all use
  // db_rewrite_sql() so we must query the database tables directly)
  $result = db_query('SELECT tid FROM {term_node} WHERE nid = %d', $nid);
  while ($row = db_fetch_array($result)) {

    // only include those terms the current user does not have access to
    if (!isset($user_terms[$row['tid']])) {
      $tids[$nid][$row['tid']] = $row['tid'];
    }
  }

  // return only terms current user does not have access
  // to and therefore need restoring after edit/update
  return $tids[$nid];
}

/**
 * used to restore terms deleted by taxonomy_node_delete() 
 * that the user shouldn't have access to delete.
 * see http://drupal.org/node/92355 and http://drupal.org/node/93086
 *
 * Called by taxonomy_access_nodeapi()
 */
function taxonomy_access_restore_terms($nid, $protected_terms) {
  if (isset($protected_terms)) {
    $terms = $protected_terms;
    if (count($terms)) {
      db_query('DELETE FROM {term_node} WHERE nid = %d AND tid IN (' . implode(',', $terms) . ')', $nid);
      foreach ($terms as $tid) {
        $replace_terms[] = '(' . $nid . ',' . $tid . ')';
      }
      db_query('INSERT INTO {term_node} (nid,tid) VALUES ' . implode(',', $replace_terms));
    }
  }
}

Functions

Namesort descending Description
taxonomy_access_db_rewrite_sql Implementation of hook_db_rewrite_sql()
taxonomy_access_defaults_update Updates default permissions for a role for a vocabulary
taxonomy_access_disable Implementation of hook_disable().
taxonomy_access_disabling Simple function to make sure we don't respond with grants when disabling ourselves.
taxonomy_access_enable Implementation of hook_enable().
taxonomy_access_form_alter Implementation of hook_form_alter()
taxonomy_access_get_default_grants Gets default permissions for a given role
taxonomy_access_get_grants Gets permissions for a given role
taxonomy_access_grant_update Updates permissions for a role for a term
taxonomy_access_grant_vocab_update Updates permissions for a role for all the terms in a vocabulary
taxonomy_access_help Implementation of hook_help
taxonomy_access_menu Implementation of hook_menu
taxonomy_access_nodeapi Implementation of hook_nodeapi().
taxonomy_access_node_access_records Implementation of hook_node_access_records().
taxonomy_access_node_grants Implementation of hook_node_grants() Gives access to taxonomies based on the taxonomy_access table
taxonomy_access_preserve_terms used to preserve terms deleted by taxonomy_node_delete() that the user shouldn't have access to delete. see http://drupal.org/node/92355 and http://drupal.org/node/93086
taxonomy_access_restore_terms used to restore terms deleted by taxonomy_node_delete() that the user shouldn't have access to delete. see http://drupal.org/node/92355 and http://drupal.org/node/93086
taxonomy_access_taxonomy Implementation of hook_taxonomy Hook_taxonomy is called when changes are made to the taxonomy structure
taxonomy_access_user Implementation of hook_user Hook_user is called when a user event occurs to check for new roles. It would make sense to have a hook_role instead. However, that hook doesn't exist so we rely on the hook_user to determine if new roles have been…
_taxonomy_access_defaults Provide default values for term_access_defaults
_taxonomy_access_update_db