--- node.module.5.7.orig	2008-04-16 23:37:39.000000000 -0700
+++ node.module	2008-05-09 00:34:33.000000000 -0700
@@ -1120,6 +1120,16 @@ function node_menu($may_cache) {
       'callback arguments' => array('node_configure_rebuild_confirm'),
       'access' => user_access('administer nodes'),
       'type' => MENU_CALLBACK);
+
+    $items[] = array(
+      'path' => 'admin/content/multinode',
+      'title' => t('Multinode user interface'),
+      'description' => t('Interface for configuring multiple node access.'),
+      'callback' => 'drupal_get_form',
+      'callback arguments' => array('node_multinode'),
+      'access' => user_access('administer nodes'),
+      'type' => MENU_NORMAL_ITEM
+     );  
 
     $items[] = array(
       'path' => 'admin/content/types',
@@ -2731,22 +2741,19 @@ function node_access($op, $node = NULL) 
 
   // If the module did not override the access rights, use those set in the
   // node_access table.
-  if ($op != 'create' && $node->nid && $node->status) {
-    $grants = array();
-    foreach (node_access_grants($op) as $realm => $gids) {
-      foreach ($gids as $gid) {
-        $grants[] = "(gid = $gid AND realm = '$realm')";
-      }
-    }
-
-    $grants_sql = '';
-    if (count($grants)) {
-      $grants_sql = 'AND ('. implode(' OR ', $grants) .')';
+  if ($op != 'create' && $node->nid) {
+    $grants_sql = node_access_grants_sql($op, NULL, $user->uid, $node->status);
+    // If the return value is FALSE, then the node status is unpublished and
+    // none of the grants requested an access check be run.  In which case,
+    // we should fall through to the final 'if' statement.
+    if ($grants_sql !== FALSE) {
+      if (!empty($grants_sql)) {
+        $grants_sql .= ' AND';
+      }  
+      $sql = "SELECT COUNT(*) FROM {node_access} WHERE (nid = 0 OR nid = %d) $grants_sql grant_$op >= 1";
+      $result = db_query($sql, $node->nid);
+      return (db_result($result));
     }
-
-    $sql = "SELECT COUNT(*) FROM {node_access} WHERE (nid = 0 OR nid = %d) $grants_sql AND grant_$op >= 1";
-    $result = db_query($sql, $node->nid);
-    return (db_result($result));
   }
 
   // Let authors view their own nodes.
@@ -2793,17 +2800,7 @@ function _node_access_where_sql($op = 'v
     return;
   }
 
-  $grants = array();
-  foreach (node_access_grants($op, $uid) as $realm => $gids) {
-    foreach ($gids as $gid) {
-      $grants[] = "($node_access_alias.gid = $gid AND $node_access_alias.realm = '$realm')";
-    }
-  }
-
-  $grants_sql = '';
-  if (count($grants)) {
-    $grants_sql = 'AND ('. implode(' OR ', $grants) .')';
-  }
+  $grants_sql = node_access_grants_sql('view', $node_access_alias, $uid);
 
   $sql = "$node_access_alias.grant_$op >= 1 $grants_sql";
   return $sql;
@@ -2833,7 +2830,6 @@ function node_access_grants($op, $uid = 
   else {
     $user_object = $user;
   }
-
   return array_merge(array('all' => array(0)), module_invoke_all('node_grants', $user_object, $op));
 }
 
@@ -2844,18 +2840,7 @@ function node_access_view_all_nodes() {
   static $access;
 
   if (!isset($access)) {
-    $grants = array();
-    foreach (node_access_grants('view') as $realm => $gids) {
-      foreach ($gids as $gid) {
-        $grants[] = "(gid = $gid AND realm = '$realm')";
-      }
-    }
-
-    $grants_sql = '';
-    if (count($grants)) {
-      $grants_sql = 'AND ('. implode(' OR ', $grants) .')';
-    }
-
+    $grants_sql = node_access_grants_sql('view');
     $sql = "SELECT COUNT(*) FROM {node_access} WHERE nid = 0 $grants_sql AND grant_view >= 1";
     $result = db_query($sql);
     $access = db_result($result);
@@ -2865,6 +2850,160 @@ function node_access_view_all_nodes() {
 }
 
 /**
+ * Check the logic of node grants and prepare the sql statement.
+ *
+ * @param $op
+ *   The operation that the user is trying to perform.
+ * @param $uid
+ *   The user ID performing the operation. If omitted, the current user is used.
+ * @param $status
+ *   The publication status of the node being checked.  Typically, node_access
+ *   checks are not run for unpublished nodes.  However, some advanced uses
+ *   require that users can act on unpublished nodes.
+ * @return
+ *   There are three possible return values.
+ *   - A sql string containing the appropriate WHERE clauses, grouped together
+ *   according to the logic of hook_node_grants.
+ *   - An empty string, indicating that no extra rules are needed.
+ *   - Boolean FALSE, indicating that all nodes are unpublished and no module
+ *   has explicitly requested an access check.
+ */
+function node_access_grants_sql($op = 'view', $node_access_alias = NULL, $uid = NULL, $status = TRUE) {
+  // Define the grants array.
+  $grants = array();
+  $template = array();
+  // First, acquire all the grants as an array of rules.
+  $rules = node_access_grants($op, $uid);
+
+  // Get whatever is stored in multinode table.
+  // Add to rules
+  if (db_table_exists('multinode_access')) {
+    $result = db_query("SELECT * FROM {multinode_access}");
+    while ($row = db_fetch_object($result)) {
+      foreach ($rules as $realm => $rule_grants) {
+        if ($row->realm == $realm) {
+          $rules[$realm]['group'] = $row->groupname;
+          $rules[$realm]['logic'] = $row->logic;
+          $rules[$realm]['weight'] = $row->weight;
+          $rules[$realm]['check'] = $row->checkstatus;
+        }
+      }
+    }
+  }
+
+  // Prepare the table alias, if needed.
+  if (!empty($node_access_alias)) {
+    $alias = $node_access_alias .'.';
+  }
+  // It may be that only the default rule is active.  In that case, we can skip the
+  // process below.
+  if (count($rules) == 1 && key($rules) == 'all') {
+    $grants_sql .= "AND (". $alias ."realm = 'all' AND ". $alias ."gid = 0)";    
+  }
+  else {
+    // Process the grants into logical groupings.
+    foreach ($rules as $realm => $rule) {
+      // Set the status flag.
+      $status += $rule['check'];  
+      // Set the default clause group.  Then check for options.
+      $group = 'all';
+      if (!empty($rule['group'])) {
+        $group = $rule['group'];
+        $template[$group]['name'] = $rule['group'];
+      }
+      if (!empty($rule['logic'])) {
+        $template[$group]['logic'] = $rule['logic'];
+      }
+      if (!empty($rule['weight'])) {
+        $template[$group]['weight'] = $rule['weight'];
+      }
+      foreach ($rule as $gid) {
+        // Grants ids must be numeric.
+        if (is_numeric($gid)) {
+          $grants[$group][$realm][] = $gid;
+        }  
+      }  
+    }
+    // In most cases, node status must be set to TRUE.  However, there are use-cases where 
+    // we allow access to unpublished nodes.  So we check the status to see if we need to 
+    // continue with the logic or end this IF statement.  
+    if (!$status) {
+      return FALSE;
+    }
+    
+    $grants_sql = '';
+	$subquery = array();
+    $subqueries = 'no';
+
+    // Run throught the $grants, if needed and generate the query SQL.
+    if (count($grants)) {
+      foreach ($grants as $group => $grant) {
+        $clauses = array();
+        foreach ($grant as $key => $value) {
+          foreach ($value as $gid) {
+            $clauses[] = "(". $alias ."realm = '$key' AND ". $alias ."gid = $gid)";
+          }
+        }
+        if ($group == 'all') {
+          $grants_sql .= 'AND ('. implode(' OR ', $clauses) .') ';            
+        }
+        else {
+          // Required elements must go into a subquery.  Will not work prior to MySQL 4.1.x.
+          // Set defaults.
+          $subqueries = 'yes';
+          $weight = 0;
+		  $logic = 'AND';
+
+          if ($template[$group]['weight']) {
+            $weight = $template[$group]['weight'];
+          } 
+
+          if ($template[$group]['logic']) {
+		    $logic = $template[$group]['logic'];
+		  }
+
+          if ($logic == 'OR') {
+            $this_query = "OR ". $alias ."nid IN (SELECT ". $alias ."nid FROM {node_access} ". $node_access_alias ." WHERE ";
+		  } else {
+            $this_query = "AND ". $alias ."nid IN (SELECT ". $alias ."nid FROM {node_access} ". $node_access_alias ." WHERE ";
+          }
+          $this_query = $this_query .'('. implode(' OR ', $clauses) .')) ';
+          
+          $subquery[$this_query] = $weight;
+        }
+      }  
+      if ($subqueries == 'yes') {
+        // Get subqueries from subquery table;
+        // Sort if first by weight.
+		$subquery_phrase = '';
+        asort($subquery);
+		foreach ($subquery as $query=>$weight) {
+          $subquery_phrase .= $query;
+		}
+//
+// Just in case I need to put parenthesis elsewhere
+//
+//        $pattern = '/^AND /';
+//        $replacement = 'AND  (';
+//        $subquery_phrase = preg_replace($pattern, $replacement, $subquery_phrase);
+//        $pattern = '/^OR /';
+//        $replacement = 'OR  (';
+//        $subquery_phrase = preg_replace($pattern, $replacement, $subquery_phrase);
+//        $subquery_phrase = $subquery_phrase . ')';            
+        // Need to add parenthesis after first OR or AND and at the end of the query.
+        $grants_sql .= $subquery_phrase;
+        // Place everything after "AND" within parenthesis
+        $pattern = '/^AND /';
+        $replacement = 'AND  (';
+        $grants_sql = preg_replace($pattern, $replacement, $grants_sql);
+        $grants_sql = $grants_sql . ')';
+	  }
+    }
+  }  
+  return $grants_sql;
+}
+
+/**
  * Implementation of hook_db_rewrite_sql
  */
 function node_db_rewrite_sql($query, $primary_table, $primary_field) {
@@ -3050,3 +3189,100 @@ function node_forms() {
   }
   return $forms;
 }
+
+////////////////////////////////
+////// Multinode Access ////////
+////////////////////////////////
+
+/**
+ * Generate multinode access UI form.
+ */
+function node_multinode($theme = NULL) {
+  global $theme_key, $custom_theme, $user;
+  $uid = $user->uid;
+     
+  // Get whatever is stored in multinode table.
+  $existing = array();
+  $result = db_query("SELECT * FROM {multinode_access}");
+  while ($row = db_fetch_object($result)) {
+    $existing[$row->realm] = $row;
+  }
+
+  // Get all rule realms from node_access table.
+  $result = db_query("SELECT DISTINCT realm FROM {node_access}");
+   while ($row = db_fetch_object($result)) {
+    $rules[] = $row->realm;
+   }    
+
+  // Get all rule realms from modules.  User doing this should have grants in all modules affected.
+  $op = 'view';
+  $mrules = node_access_grants($op, $uid);
+  foreach ($mrules as $realm => $grants) {
+    $rules[] = $realm;
+  }
+  
+  // Get only unique values
+  $rules = array_unique($rules);
+    
+  // Sort the rule realms
+  asort($rules);
+
+  $form['#tree'] = TRUE;
+  foreach ($rules as $realm) {
+    $form[$realm]['checkbox'] = array('#type' => 'checkbox', '#default_value' => (isset($existing[$realm]->realm) ? 1 : 0)); 
+    $form[$realm]['realm'] = array('#type' => 'textfield', '#size' => 25, '#disabled' => TRUE, '#value' => check_plain($realm), '#default_value' => $realm); 
+    $form[$realm]['group'] = array('#type' => 'textfield', '#size' => 5, '#default_value' => (isset($existing[$realm]->groupname) ? $existing[$realm]->groupname : '') ); 
+    $form[$realm]['logic'] = array('#type' => 'select', '#default_value' => (isset($existing[$realm]->logic) ? $existing[$realm]->logic : 'AND'), '#options' => array('AND' => 'AND', 'OR' => 'OR') ); 
+    $form[$realm]['weight'] = array('#type' => 'select', '#default_value' => (isset($existing[$realm]->weight) ? $existing[$realm]->weight : '0'), '#options' => array('0' => '0', '1' => '1', '2' => '2', '3' => '3', '4' => '4', '5' => '5', '6' => '6') ); 
+    $form[$realm]['check'] = array('#type' => 'select', '#default_value' => (isset($existing[$realm]->checkstatus) ? $existing[$realm]->checkstatus : '0'), '#options' => array('0' => '0', '1' => '1') ); 
+  }
+
+  $form['submit'] = array('#type' => 'submit', '#value' => t('Save changes'));
+
+  return $form;
+
+}
+
+/**
+ * Theme function to render the table for the node_multinode 
+ * Multinode access UI
+ */
+function theme_node_multinode($form) {
+  $output .= "\n<div id=\"og-roles-multinode-form\">\n";
+  $output .= '<div id="desc">'. t('Here you can configure multinode access.  You can only modify values here if TAC/OG Integration is ON. If you turn TAC/OG Integration ON, then later wish to turn it off, you must first UNCHECK all items here and <strong>Save changes</strong> (otherwise, multinode access will continue).') ."</div>\n";
+  $header = array(t(''), t('Realm'), t('Group'), t('Logic'), t('Weight'), t('Check'));
+  $rows = array();
+
+  foreach (element_children($form) as $i) {
+    $block = &$form[$i];
+    $rows[] = array(drupal_render($block['checkbox']), drupal_render($block['realm']),
+        drupal_render($block['group']), drupal_render($block['logic']),
+        drupal_render($block['weight']), drupal_render($block['check']),
+      );
+  }
+
+  $output .= theme('table', $header, $rows, array('id' => 'og-roles-multinode-table'));
+
+  $output .= drupal_render($form['submit']);
+
+  $output .= "</div>\n";
+  $output .= drupal_render($form);
+  return $output;
+}
+
+/**
+ * Process multinode form submission.
+ */
+function node_multinode_submit($form_id, $form_values) {
+  // Erase all existing settings
+  db_query ("DELETE FROM {multinode_access}");
+
+  // Insert new settings
+  foreach ($form_values as $block) {
+    if ($block['checkbox'] == 1) {
+      db_query("INSERT INTO {multinode_access} (realm, groupname, logic, weight, checkstatus) VALUES ('%s','%s','%s','%s',%d)", $block['realm'], $block['group'], $block['logic'], $block['weight'], $block['check']);
+	}
+  }
+  drupal_set_message(t('The multinode access settings have been updated.'));
+  cache_clear_all();
+}
