Index: taxonomy_access.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/taxonomy_access/taxonomy_access.module,v
retrieving revision 1.107
diff -u -r1.107 taxonomy_access.module
--- taxonomy_access.module	23 Feb 2008 20:48:48 -0000	1.107
+++ taxonomy_access.module	6 Dec 2010 06:37:20 -0000
@@ -34,7 +34,7 @@
       $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;
-      
+
       // TODO: update help
     /*    default:
       if (strpos($path,'admin/user/taxonomy_access') === 0) {
@@ -75,34 +75,73 @@
 }
 
 /**
+ * populate and cache parents for a term and cache it
+ */
+function _taxonomy_access_get_parents($tid) {
+  static $parents = array();
+
+  if ( ! isset($parents[$tid]) ) {
+    $parents[$tid] = array();
+    foreach ( taxonomy_get_parents_all($tid) as $p ) {
+      $parents[$tid][] = $p->tid;
+    }
+  }
+  return $parents[$tid];
+}
+
+/**
  * Implementation of hook_node_access_records().
  * TODO: change query to behave properly when no term or vocab record is present
  */
 function taxonomy_access_node_access_records($node) {
-  $grants = array();
+  static $assigned_tids; // list of permission-assigned terms
+
+  if ( ! isset($assigned_tids) ) {
+    $assigned_tids = array();
+    $result = db_query('SELECT DISTINCT(tid) FROM {term_access} ORDER BY tid');
+    while( $row = db_fetch_array($result) ) {
+      $assigned_tids[] = (int) $row['tid'];
+    }
+  }
+
+  // populate parents list
+  $parents = array();
+  foreach ( $node->taxonomy as $term ) {
+    if ( is_object($term) ) {
+      $parents = array_merge($parents, _taxonomy_access_inherit_get_parents($term->tid));
+    }
+    else {
+      foreach ( (array) $term as $tid ) {
+        $parents = array_merge($parents, _taxonomy_access_inherit_get_parents($tid));
+      }
+    }
+  }
+
+  $grants  = array();
 
   if (is_array($node->taxonomy) AND count($node->taxonomy)) {
     // TODO: how does deny/ignore work with this?
-    $result = db_query('SELECT tadg.rid, 
-                       BIT_OR(COALESCE( ta.grant_view, tad.grant_view, tadg.grant_view )) AS grant_view, 
-                       BIT_OR(COALESCE( ta.grant_update, tad.grant_update, tadg.grant_update )) AS grant_update, 
+    $list = join(',', array_intersect($parents, $assigned_tids));
+    $result = db_query('SELECT tadg.rid,
+                       BIT_OR(COALESCE( ta.grant_view, tad.grant_view, tadg.grant_view )) AS grant_view,
+                       BIT_OR(COALESCE( ta.grant_update, tad.grant_update, tadg.grant_update )) AS grant_update,
                        BIT_OR(COALESCE( ta.grant_delete, tad.grant_delete, tadg.grant_delete )) AS grant_delete
                        FROM {term_node} tn
-                       INNER JOIN {term_data} t ON t.tid = tn.tid
+                       INNER JOIN {term_data} t ON ( t.tid = tn.tid'. ( ! empty($list) ? ' OR t.tid IN ('. $list .')' : '' ) .' )
                        INNER JOIN {term_access_defaults} tadg ON tadg.vid = 0
                        LEFT JOIN {term_access_defaults} tad ON tad.vid = t.vid AND tad.rid = tadg.rid
                        LEFT JOIN {term_access} ta ON ta.tid = t.tid AND ta.rid = tadg.rid
-                       WHERE tn.nid = %d
-                       GROUP BY tadg.rid', $node->nid);
+                       WHERE tn.vid = %d
+                       GROUP BY tadg.rid', $node->vid);
   }
   else {
     // Use the default for nodes with no category
-    $result = db_query('SELECT n.nid, tadg.rid AS rid, tadg.grant_view AS grant_view, tadg.grant_update AS grant_update, tadg.grant_delete AS grant_delete 
-                       FROM {node} n INNER JOIN {term_access_defaults} tadg ON tadg.vid = 0 
+    $result = db_query('SELECT n.nid, tadg.rid AS rid, tadg.grant_view AS grant_view, tadg.grant_update AS grant_update, tadg.grant_delete AS grant_delete
+                       FROM {node} n INNER JOIN {term_access_defaults} tadg ON tadg.vid = 0
                        WHERE n.nid = %d', $node->nid);
-  }  
+  }
   // A bit of explanation for future generations regarding the deny/allow override behavior:
-  // only a value of '1' is considered an 'allow'. Ignore (really a weak deny) is '0', deny is '2' ('10' in binary). 
+  // only a value of '1' is considered an 'allow'. Ignore (really a weak deny) is '0', deny is '2' ('10' in binary).
   // Allow always gets through the BIT_OR above as '1', unless a deny is present, in which case the value will be '3' ('11' in binary)
 
   while ($row = db_fetch_array($result)) {
@@ -176,7 +215,7 @@
 function taxonomy_access_form_alter(&$form, &$form_state, $form_id) {
   //TODO: Move control of "create" op here
   //TODO: look at feasability to eliminate _restore_terms and _preserve_terms by simply setting the '#access' attribute for those terms
-  if ($form['#id'] == 'node-form' && is_numeric($form['nid']['#value'])) { 
+  if ($form['#id'] == 'node-form' && is_numeric($form['nid']['#value'])) {
     $form['tac_protected_terms'] = array(
       '#type' => 'value',
       '#value' => taxonomy_access_preserve_terms($form['#node'])
@@ -249,7 +288,7 @@
  * Provide default values for term_access_defaults
  */
 function _taxonomy_access_defaults($vid, $rid) {
-  if ($rid == 1 || $rid == 2) 
+  if ($rid == 1 || $rid == 2)
     return array($vid, $rid, 1, 0, 0, 1, 1);
   else
     return array($vid, $rid, 0, 0, 0, 0, 0);
@@ -273,7 +312,7 @@
     if (!isset($taxonomy_access_sql_clause[$op][$field]))  {
       if (isset($user) && is_array($user->roles)) {
         $rids = array_keys($user->roles);
-      } 
+      }
       else {
         $rids[] = 1;
       }
@@ -300,6 +339,18 @@
         }
       }
 
+      // also get children, if not configured
+      $children = array();
+      foreach ( $tids as $t ) {
+        $sql = db_query('SELECT t.* FROM {term_data} t INNER JOIN term_hierarchy h ON h.tid = t.tid WHERE parent = %d ORDER BY weight, name', $t);
+        while( $c = db_fetch_object($sql) ) {
+          if ( ! in_array((int)$c->tid, $tids) ) {
+            $children[] = (int) $c->tid;
+          }
+        }
+      }
+      $tids = array_merge($tids, $children);
+
       $clause[$op]['tid'] = isset($tids) ? implode("','", $tids) : '';
       $clause[$op]['vid'] = isset($vids) ? implode("','", $vids) : '';
       $taxonomy_access_sql_clause = $clause;
@@ -316,19 +367,19 @@
 }
 
 /**
- * used to preserve terms deleted by taxonomy_node_delete() 
+ * 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()
- */  
+ */
  // TODO: should be possible to replace this with #access per term-field
 function taxonomy_access_preserve_terms($node) {
   $nid = $node->nid;
 
   // prepare/cache return value
   static $tids = array();
-  
+
   // a valid numeric nid is required
   if (!is_numeric($nid)) {
     return array();
@@ -338,14 +389,14 @@
   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($node);
 
   // 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 
+  // 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)) {
@@ -354,14 +405,14 @@
       $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() 
+ * 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
  *

