--- taxonomy_access.module	Sun Oct 31 10:41:54 2004
+++ taxonomy_access.module_new	Sun Nov 14 14:52:44 2004
@@ -1,4 +1,38 @@
-<?  
+<?
+
+function taxonomy_access_help($section) {
+  switch ($section) {
+  	
+  	// Brief message for administrators to explain what this module does
+  	case 'admin/user/configure/category':
+      return 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 view, update and delete permissions set for each user role.  /*The "Default Permissions" are the permissions applied to all categories for this role.  The <a href="%default">Default</a> role allows you to set the default category permissions for all roles.', array('%role' => url('admin/user/configure/role'), '%default' => url('admin/user/configure/category/0*/')));
+      
+    case 'admin/modules#description': 
+      return t('Allows users to specify which categories and viewed by various roles.');
+      
+    // 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':																							
+    
+      $taxonomy_access_enabled = variable_get('taxonomy_access_enabled', FALSE);
+      $message = '';
+      
+      // header message if module is enabled
+      if ($taxonomy_access_enabled) { 
+        $message .= 'The module is currently enabled properly.<br/>';
+        $message .= 'To disable the module properly, select \'disable\' here and save the configuration before disabling it in the modules page.<br/>';
+      }
+      
+      // header message if module is not enabled
+      else {
+      $message .= 'The module is currently disabled, but must also be disabled in the %module_page_link in order for it to be properly disabled.<br/>To properly enable this module, select \'enable\' here and save the configuration.<br/>';
+      $message = t($message, array('%module_page_link' => l(t('modules admin page'), 'admin/modules', array('title' => t('View more.')))   ));
+      }
+      return $message;
+  }
+}
+
 function taxonomy_access_menu()  {
   $access = user_access('administer users');
 
@@ -7,29 +41,64 @@
     'type' => MENU_LOCAL_TASK);
   return $items;
 }
+
+function taxonomy_access_taxonomy($op, $type, $edit)  {
+	if($type = 'term') {
+		// Using this function is a lazy, expensive way of taking care of changes to the taxonomy data.
+    // More efficient code should be written to handle specific kinds of changes to the taxonomy.
+		_taxonomy_access_update_db();
+	}
+}
  
 function taxonomy_access_admin($edit = array()) {
   $tids = $_POST['tids'];
   $rid = arg(4);
   $output = '';
+  $grant_types = array('view', 'update', 'delete');
+  
+  // Code to handle user supplied changes to permission configuration
   if (isset($edit)) {
-    if (!isset($rid)) return t('Please select a role to update');
-    // Save permissions:
-    foreach ($tids as $tid)  {
-	if (isset($edit[$tid]))
-          foreach($edit[$tid] as $key => $val)  {
-            ($val == 'on') ? $edit[$tid][$key]=1 : $edit[$tid][$key]=0;
-	   }
-      taxonomy_access_grant_update($tid,$rid,$edit[$tid]);
+    if (!isset($rid)) {
+    	return t('Please select a role to update');
+    }
+    
+    // Process input data and use it to make changes to the database.
+    foreach($tids as $tid) {
+      // The $edit array is filled out with zeroes here to
+    	// reduce code complexity when we update the term_access table.
+      if(!array_key_exists($tid, $edit)) {
+        foreach($grant_types as $grant) {
+        	$edit[$tid][$grant] = 0;
+				}
+      }
+      // Enter a '0' for any permission that has been left unchecked by the user
+      // For any permission checked by user, set it to a value of '1'
+      else {
+      	foreach($grant_types as $grant) {
+        	if(!isset($edit[$tid][$grant])) {
+          	$edit[$tid][$grant] = 0;
+          }
+          else {
+          	$edit[$tid][$grant] = 1;
+        	}
+				}
+      }
+      // Make the changes to the term_access table
+      foreach($edit[$tid] as $key => $val)  {
+      	taxonomy_access_grant_update($tid, $rid, $edit[$tid]);
+      }
     }
+    _refresh_node_access_table();
 
     // Clear the cache, as we might have changed the anonymous user's
     // permissions.
     cache_clear_all();
   }
+  
+  // Output the permission matrix user form
   if (isset($rid))  {
     // Get name of role
-    if ($rid == 0) $role_name = t('Default');
+    if ($rid == 0) $role_name = t('no role');
     else $role_name = db_result(db_query('select name from {role} where rid='.$rid));
     $output = '<h2>Permissions for \''.$role_name.'\'</h2><p>';
     // Get all category permissions
@@ -37,12 +106,14 @@
     // Compile category permission overview
     $term_list = array();
     $header = array('Category','View','Update','Delete');
-    // Do the default row
-    $rows[] = array('Default Permissions',
+    
+    // Do the default row (This code has been disabled for now until this feature is reimplemented)
+    $rows[] = array('uncategorized nodes',
            '<input type="hidden" name="tids[]" value="0">'.
            '<input type="checkbox" name="edit[0][view]" '. (($perm[0]['view']) ? 'checked>' : '>'),
            '<input type="checkbox" name="edit[0][update]" '. (($perm[0]['update']) ? 'checked>' : '>'),
            '<input type="checkbox" name="edit[0][delete]" '. (($perm[0]['delete']) ? 'checked>' : '>'));
+
     $vocabs = taxonomy_get_vocabularies();
     foreach($vocabs as $vocab)  {
       $rows[] = array('<b>'.t($vocab->name).'</b>','&nbsp','&nbsp','&nbsp');
@@ -74,7 +145,7 @@
     // Render role/permission overview:
     $header = array("Role","Category Permissions");
     // Default settings for categories
-    $rows[] = array(t('Default'),array('data'=>"<a href=\"admin/user/configure/category/0\">Edit</a>",'align'=>'right'));
+    $rows[] = array(t('no role'),array('data'=>"<a href=\"admin/user/configure/category/0\">Edit</a>",'align'=>'right'));
     foreach ($role_names as $rid => $name) {
      $rows[] = array($name,array('data'=>"<a href=\"admin/user/configure/category/$rid\">Edit</a>", 'align'=>'right'));
     }
@@ -105,53 +176,34 @@
   print theme('page', $output);
 
 }
-function taxonomy_access_help($section)  {
-  switch($section)  {
-    case 'admin/user/configure/category':
-      return 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 view, update and delete permissions set for each user role.  The "Default Permissions" are the permissions applied to all categories for this role.  The <a href="%default">Default</a> role allows you to set the default category permissions for all roles.', array('%role' => url('admin/user/configure/role'), '%default' => url('admin/user/configure/category/0')));
-    case 'admin/modules#description':
-      return t('Allows the user administrator to control access to categories based on user role.');
-  }
-}
+
 /**
  * Implementation of hook_node_grants()
  * Gives access to taxonomies based on the taxonomy_access table
  */
 function taxonomy_access_node_grants($user, $op)  {
-//    if (user_access('administer nodes'))
-//      return array('taxonomy'=>'0');
-
-    $sql = "SELECT tid from {term_access} where (rid=0 OR rid in ('"
-      . implode("','",array_keys($user->roles)) . "')) and grant_". $op . "=1";
-    $result = db_query($sql);
-    $nids = array();
-    $i=0;
-    while (($nid = db_result($result,$i)) != null)  {
-      $nids[] = $nid;
-      $i++;
-    }
-    $nids[] = 0; // catchall
-    return array('taxonomy'=>$nids);
-
+	global $user;
+	return $user->roles ? array('tax_access' => array_keys($user->roles), 'tax_access_author' => array($user->uid)) : array('tax_access_author' => array($user->uid));
 }
 
-/** 
- * Inserts a permission for a role to a term
+/**
+ * Updates taxonomy permissions for a role as set by user in the configuration
  * @param $tid
- *   The term to add the permission for. 
- * @param $role 
+ *   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
+ *   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 true will grant the permission for this user and term.
- *   If omitted, only the permission for 'view' will be granted.
+ *   A value of true will grant the permission for this role and perm.
+ *   A value for false will deny permission for role and perm.
 **/
-function taxonomy_access_grant_add($tid,$role = null,$grants = null)  {
+function taxonomy_access_grant_update($tid, $role = null, $grants = null)  {
   if (!isset($tid)) return false;
-  if (isset($role) && !is_numeric($role))
+  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 = "REPLACE INTO {term_access} (tid";
   $ta_sql_values = " VALUES ($tid";
   if (isset($role)) {
     $ta_sql .= ",rid";
@@ -173,56 +225,6 @@
   $ta_sql .= $sql . $ta_sql_values;
 
   db_query($ta_sql);  // insert into term_access
-
-  // take care of node_access
-  $result = db_query("SELECT DISTINCT(n.nid) from {node} as n INNER JOIN {term_node} as tn 
-			on n.nid=tn.nid where tn.tid=$tid");
-  while ($node = db_fetch_array($result))  {
-    $nid = $node['nid'];
-    db_query("INSERT INTO {node_access} (nid,gid,realm,grant_view,grant_update,grant_delete)
-		VALUES ($nid,$tid,'taxonomy',1,1,1)");
-  }
-
-}
-
-/**
- * Deletes permissions for a role from 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.
-**/
-function taxonomy_access_grant_del($tid,$role = 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 = "DELETE FROM {term_access} where tid=$tid";
-  $na_sql = "DELETE FROM {node_access} where gid=$tid and realm='taxonomy'";
-  if (isset($role))
-    $ta_sql .= " and rid=$role";
-  db_query($ta_sql);
-  db_query($na_sql);
-}
-/**
- * Updates permissions for a role from 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 true will grant the permission for this user and term.
- *   If omitted, only the permission for 'view' will be granted.
-**/
-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'"));
-  taxonomy_access_grant_del($tid,$role);
-  if (isset($grants))
-    taxonomy_access_grant_add($tid,$role,$grants);
 }
 
 /**
@@ -238,9 +240,10 @@
 **/
 function taxonomy_access_get_grants($role)  {
   if (!isset($role)) return false;
-  if (isset($role) && !is_numeric($role))
+  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' OR rid=0");
+  }
+  $result = db_query("SELECT * from {term_access} where rid='$role'");
   $grants = array();
   while ($grant = db_fetch_array($result))  {
     $tid = $grant['tid'];
@@ -262,24 +265,228 @@
 function taxonomy_access_nodeapi(&$node, $op, $arg = 0) {
   switch ($op) {
     case 'delete':
-      // When a node is deleted, delete any relevant permissions.
-      db_query('DELETE FROM {node_access} WHERE nid = %d AND realm = \'taxonomy\'', $node->nid);
+      // When a node is deleted, delete its permissions.
+      db_query('DELETE FROM {node_access} WHERE nid = %d AND realm = \'tax_access\'', $node->nid);
+      db_query('DELETE FROM {node_access} WHERE nid = %d AND realm = \'tax_access_author\'', $node->nid);
       break;
-
+            
+		// Enter new data into node_access to set permissions for the new node
     case 'insert':
-    case 'update':
-      // Clear out any existing permissions for the node, and set new ones.
-      db_query('DELETE FROM {node_access} WHERE nid = %d AND realm = \'taxonomy\'', $node->nid);
-      if ($node->taxonomy) {
-        foreach ($node->taxonomy as $term) {
-          db_query('INSERT IGNORE INTO {node_access} (nid,gid,realm,grant_view,grant_update,grant_delete) 
-			VALUES (%d, %d, \'taxonomy\', %d, %d, %d)', $node->nid, $term,1, 1, 1);
-        }
+      // Determine the category/categories the new node was assigned to
+      $tids = $node->taxonomy;
+       
+      // For nodes not assigned to a category
+      if(!$tids) {
+      	$query = db_query('SELECT * FROM term_access where tid = 0');
+      	while($result = db_fetch_array($query)) {
+      		db_query('REPLACE INTO node_access (nid, gid, realm, grant_view, grant_update, grant_delete) VALUES (%d, %d, \'tax_access\', %d, %d, %d)', $node->nid, $result->rid, $result->grant_view, $result->grant_update, $result->grant_delete);
+      	}
       }
+      // For nodes assigned to a category
+      else {      
+	      // Query the term_access table to get the permissions for the new node's assigned category/categories
+	      // then make appropriate changes to the node_access table.
+	      $result = db_query("SELECT * from term_access where tid in ('" . implode("','",array_values($tids)) . "')");
+	      while($row = db_fetch_object($result)) {
+	      	db_query('REPLACE INTO node_access (nid, gid, realm, grant_view, grant_update, grant_delete) VALUES (%d, %d, \'tax_access\', %d, %d, %d)', $node->nid, $row->rid, $row->grant_view, $row->grant_update, $row->grant_delete);
+	      }
+	    }
+      
+      // Create permission for author
+      taxonomy_access_author($node);
+      break;
+      
+    case 'update':
+    	// using this function is a lazy but wasteful way of taking care of a node update
+    	// more efficient code should be written to handle specific kinds of node changes
+      _taxonomy_access_update_db();
       break;
   }
 }
 
+// Create permission for the author of a node
+function taxonomy_access_author($node) {
+	global $user;
+  if($node->uid == $user->uid) {
+   	db_query('REPLACE INTO node_access (nid, gid, realm, grant_view, grant_update, grant_delete) VALUES (%d, %d, \'tax_access_author\', %d, %d, %d)', $node->nid, $user->uid, 1, 1, 1);
+  }
+}
+	
+
+function taxonomy_access_settings() {
+  if (variable_get('taxonomy_access_settings_changed', FALSE)) {
+    _taxonomy_access_update_db();
+  }
+
+  $formitems = '';
+  $taxonomy_access_enabled = variable_get('taxonomy_access_enabled', FALSE);
+  $formitems .= form_hidden('taxonomy_access_settings_changed', 1);
+  $formitems .= form_radio(t('Enable the taxonomy_access module.'), 'taxonomy_access_enabled', 1,variable_get('taxonomy_access_enabled', FALSE) );
+  $formitems .= form_radio(t('Disable the taxonomy_access module.'), 'taxonomy_access_enabled', 0, !variable_get('taxonomy_access_enabled', FALSE) );
+  
+  return $formitems; 
+}
+
+function taxonomy_access_perm()  {
+  return array('bypass taxonomy access');
+}
+
+function _taxonomy_access_update_db() {
+  variable_set('taxonomy_access_settings_changed', FALSE);
+  
+  if (!variable_get('taxonomy_access_enabled', FALSE)) {
+    // We use the replace statement to avoid inserting a duplicate entry into the database.
+    // Without the DELETE query, this can happen when a site admin has already enabled the modules
+    // from the settings page and goes back to it and resaves the enabled setting.
+    db_query('REPLACE INTO node_access VALUES (0, 0, \'all\', 1, 0, 0)');
+  }
+  else { // the module was just enabled; provide default view access to everyone for nodes that were created
+         // between the previous disabling of the module and the current enabling of the module;
+         // nodes that were created during a previous enabled period are left-alone.
+         // Permissions for those nodes will be the same as when the module was previously enabled.
+    db_query('DELETE from node_access where nid=0 AND gid=0 AND realm=\'all\' AND grant_view=1 AND grant_update=0 AND grant_delete=0');
+    
+    // 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();
+    
+    // Get current list of 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;
+		
+		$query = db_query('SELECT rid FROM role');
+		while($result = db_fetch_array($query)) {
+    	$current_rids[] = $result['rid'];
+    }
+    $current_rids[] = 0;
+    
+    // Get old list of term and role ids from when term_access was disabled
+    $query = db_query('SELECT tid FROM term_access');
+    while($result = db_fetch_array($query)) {
+    	$old_tids[] = $result['tid'];
+    }
+    $old_tids[] = 0;
+    $old_tids = array_unique($old_tids);
+    
+    $query = db_query('SELECT rid FROM term_access');
+		while($result = db_fetch_array($query)) {
+    	$old_rids[] = $result['rid'];
+    }
+    $old_rids[] = 0;
+    $old_rids = array_unique($old_rids);
+    
+    // Get the difference between old and current
+    $delete_tids = array_diff($old_tids, $current_tids);
+    $delete_rids = array_diff($old_rids, $current_rids);
+    
+    // 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
+    $query = db_query('SELECT tid FROM term_access where tid = 0');
+    if(!db_fetch_array($query)) {
+    	foreach($current_rids as $rid) {
+    		db_query('INSERT INTO term_access VALUES (0, %d, 1, 0, 0)', $rid);
+    	}
+    } 
+
+    // Add new role and term ids to term_access table since module was last disabled and assign them default permissions
+    $add_tids = array_diff($current_tids, $old_tids);
+    $add_rids = array_diff($current_rids, $old_rids);
+
+    foreach($add_tids as $add_tid) {
+    	foreach($add_rids as $add_rid) {
+    		db_query('INSERT INTO term_access VALUES (%d, %d, 1, 0, 0)', $add_tid, $add_rid);
+    	}
+    	foreach($current_rids as $current_rid) {
+    		db_query('INSERT IGNORE INTO term_access VALUES (%d, %d, 1, 0, 0)', $add_tid, $current_rid);
+    	}
+    }
+    // END: term_access table housekeeping
+    
+    // BEGIN: node_access table housekeeping
+    // Update the node_access table to reflect any changes that may have occured since module was disabled.
+    
+    // Get current list of all nodes
+    $current_nids = array();
+    $query = db_query('SELECT nid from node');
+    while($result = db_fetch_array($query)) {
+    	$current_nids[] = $result['nid'];
+  	}
+    
+    // Get list of node ids in the node_access table when module was disabled
+    $query = db_query('SELECT nid from node_access WHERE realm = \'tax_access\'');
+    $old_nids = array();
+    while($result = db_fetch_array($query)) {
+    	$old_nids[] = $result['nid'];
+    }
+    $old_nids = array_unique($old_nids);
+    
+    // Get the difference between old and current
+    $delete_nids = array_diff($old_nids, $current_nids);
+    
+    // Delete all node ids that no longer exist from the term_access table
+    foreach($delete_nids as $nid) {
+    	db_query('DELETE from node_access where nid = %d AND realm = \'tax_access\'', $nid);
+    }
+    
+    // Update node_access_table to reflect the changes made to term_access table
+    _refresh_node_access_table();
+  }
+}
+
+// Update the node_access table to reflect any changes made to the term_access table
+function _refresh_node_access_table() {
+	// Load term_access table data into memory and put it into a usable array structure
+	$result_inner = db_query("SELECT * FROM term_access WHERE 1");
+	while($term_access_row = db_fetch_array($result_inner)) {
+		$term_access[] = $term_access_row;
+	}
+	foreach($term_access as $key => $value) {
+		$permissions[$value['tid']][$value['rid']] = array('grant_view' => $value['grant_view'], 'grant_update' => $value['grant_update'], 'grant_delete' => $value['grant_delete']);
+	}
+	
+	// Use the array structure of permissions just we just created to make the updates to the node_access table
+	// for nodes that are in a category.
+	$result_outer = db_query("SELECT term_node.nid, term_node.tid FROM term_node, term_data WHERE term_data.tid = term_node.tid");
+	$category_nids = array();
+	while($row = db_fetch_object($result_outer)) {
+ 		$nid = $row->nid;
+ 		$tid = $row->tid;
+ 		$category_nids[] = $nid; // created now for use later on in code handling nodes without categories
+ 		if(array_key_exists($tid, $permissions)) {
+ 			foreach($permissions[$tid] as $key => $value) {
+			  db_query('REPLACE node_access (nid, gid, realm, grant_view, grant_update, grant_delete) VALUES (%d, %d, \'tax_access\', %d, %d, %d)', $nid, $key, $value['grant_view'], $value['grant_update'], $value['grant_delete']);
+ 			}
+ 		}
+	}
+	
+	// Determine which nodes don't belong to a category
+	$query = db_query("SELECT nid FROM node where 1");
+	while($result = db_fetch_array($query)) {
+	  $all_nids[] = $result['nid'];
+	}
+	$category_nids = array_unique($category_nids);
+	$no_cat = array_diff($all_nids, $category_nids);
+
+	// Update or insert permission data for nodes without categories
+	foreach($no_cat as $nid) {
+	  foreach($permissions[0] as $key => $value) {
+		  db_query('REPLACE node_access (nid, gid, realm, grant_view, grant_update, grant_delete) VALUES (%d, %d, \'tax_access\', %d, %d, %d)', $nid, $key, $value['grant_view'], $value['grant_update'], $value['grant_delete']);
+		}
+	}
+}
+
+
 /**
  * Allows you to determine if the current user or $uid can access this taxonomy for the given perm
 **/
@@ -309,10 +516,6 @@
   if (db_result(db_query($sql)))
     return TRUE;
   else return FALSE;
-}
-
-function taxonomy_access_perm()  {
-  return array('bypass taxonomy access');
 }
 
 ?>
