Index: modules/node/node.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.api.php,v
retrieving revision 1.20
diff -u -p -r1.20 node.api.php
--- modules/node/node.api.php	12 May 2009 23:19:13 -0000	1.20
+++ modules/node/node.api.php	15 May 2009 23:29:43 -0000
@@ -109,6 +109,38 @@ function hook_node_access_records($node)
 }
 
 /**
+ * Alter permissions for a node to be written to the database.
+ *
+ * Node access modules establish rules for access to content.
+ * Before node access records are saved to the database, this hook
+ * is called and allows modules to modify the grants array by reference.
+ * This hook allows developers to write custom modules which alter the
+ * ways that multiple node access modules interact. The preferred use of 
+ * this hook is in a module that bridges multiple node access modules
+ * with a configurable behavior, as shown in the example by the variable
+ * 'example_alter_node_access'.
+ *
+ * @param &$grants
+ *   The $grants array returned by hook_node_access_records.
+ * @param $node
+ *   The node for which the grants were acquired.
+ *
+ * @ingroup node_access
+ */
+function hook_node_access_records_alter(&$grants, $node) {
+  // Check to see if we have enabled complex behavior.
+  if (variable_get('example_alter_node_access', FALSE) && !empty($grants)) {
+    // Our module removes the author grant.
+    foreach ($grants as $gid => $grant) {
+      if ($grant['realm'] == 'example_author') {
+        unset($grants[$gid]);
+      }
+    }
+    sort($grants);
+  }
+}
+
+/**
  * Add mass node operations.
  *
  * This hook enables modules to inject custom operations into the mass operations
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1047
diff -u -p -r1.1047 node.module
--- modules/node/node.module	14 May 2009 08:23:15 -0000	1.1047
+++ modules/node/node.module	15 May 2009 23:29:45 -0000
@@ -2467,7 +2467,7 @@ function node_access_view_all_nodes() {
       $grants = db_or();
       foreach (node_access_grants('view') as $realm => $gids) {
         foreach ($gids as $gid) {
-          $or->condition(db_and()
+          $grants->condition(db_and()
             ->condition('gid', $gid)
             ->condition('realm', $realm)
           );
@@ -2539,6 +2539,12 @@ function node_query_node_access_alter(Qu
  * called by modules whenever something other than a node_save causes
  * the permissions on a node to change.
  *
+ * After the default grants have been loaded, we allow modules to alter
+ * the grants array by reference. This hook allows for complex business
+ * logic to be applied when integrating multiple node access modules.
+ *
+ * @see hook_node_access_records()
+ *
  * This function is the only function that should write to the node_access
  * table.
  *
@@ -2547,11 +2553,14 @@ function node_query_node_access_alter(Qu
  */
 function node_access_acquire_grants($node) {
   $grants = module_invoke_all('node_access_records', $node);
+  // Let modules alter the grants.
+  drupal_alter('node_access_records', $grants, $node);
+  // If no grants are set, then use the default grant.
   if (empty($grants)) {
     $grants[] = array('realm' => 'all', 'gid' => 0, 'grant_view' => 1, 'grant_update' => 0, 'grant_delete' => 0);
   }
   else {
-    // retain grants by highest priority
+    // Retain grants by highest priority.
     $grant_by_priority = array();
     foreach ($grants as $g) {
       $grant_by_priority[intval($g['priority'])][] = $g;
Index: modules/node/node.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.test,v
retrieving revision 1.24
diff -u -p -r1.24 node.test
--- modules/node/node.test	6 May 2009 19:56:21 -0000	1.24
+++ modules/node/node.test	15 May 2009 23:29:45 -0000
@@ -685,6 +685,56 @@ class NodeRSSContentTestCase extends Dru
 }
 
 /**
+ * Test case to verify hook_node_access_records_alter functionality.
+ */
+class NodeAccessRecordsAlterUnitTest extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => t('Node access records alter'),
+      'description' => t('Test hook_node_access_records_alter when acquiring grants.'),
+      'group' => t('Node'),
+    );
+  }
+
+  function setUp() {
+    // Enable dummy module that implements hook_node_grants, hook_node_access_records, and hook_node_access_records_alter.
+    parent::setUp('node_test');
+  }
+
+  function testGrantAlter() {
+    // Create an article node.
+    $node1 = $this->drupalCreateNode(array('type' => 'article'));
+    $this->assertTrue(node_load($node1->nid), t('Article node created.'));
+
+    // Check to see if grants added by node_test_node_access_records made it in.
+    $records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = %d', $node1->nid)->fetchAll();
+    $this->assertEqual(count($records), 1, t('Returned the correct number of rows.'));
+    $this->assertEqual($records[0]->realm, 'article_realm', t('Grant with article_realm acquired for node without alteration.'));
+    $this->assertEqual($records[0]->gid, 1, t('Grant with gid = 1 acquired for node without alteration.'));
+
+    // Create an unpromoted page node.
+    $node2 = $this->drupalCreateNode(array('type' => 'page', 'promote' => 0));
+    $this->assertTrue(node_load($node1->nid), t('Unpromoted page node created.'));
+
+    // Check to see if grants added by node_test_node_access_records made it in.
+    $records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = %d', $node2->nid)->fetchAll();
+    $this->assertEqual(count($records), 1, t('Returned the correct number of rows.'));
+    $this->assertEqual($records[0]->realm, 'page_realm', t('Grant with page_realm acquired for node without alteration.'));
+    $this->assertEqual($records[0]->gid, 1, t('Grant with gid = 1 acquired for node without alteration.'));
+    
+    // Create a promoted page node.
+    $node3 = $this->drupalCreateNode(array('type' => 'page', 'promote' => 1));
+    $this->assertTrue(node_load($node3->nid), t('Promoted page node created.'));
+
+    // Check to see if grant added by node_test_node_access_records got altered by node_test_node_access_records_alter.
+    $records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = %d', $node3->nid)->fetchAll();   
+    $this->assertEqual(count($records), 1, t('Returned the correct number of rows.'));
+    $this->assertEqual($records[0]->realm, 'alter_realm', t('Altered grant with alter_realm acquired for node.'));
+    $this->assertEqual($records[0]->gid, 2, t('Altered grant with gid = 2 acquired for node.'));
+  }
+}
+
+/**
  * Test case to check node save related functionality, including import-save
  */
 class NodeSaveTestCase extends DrupalWebTestCase {
Index: modules/node/tests/node_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/tests/node_test.module,v
retrieving revision 1.2
diff -u -p -r1.2 node_test.module
--- modules/node/tests/node_test.module	3 May 2009 10:11:34 -0000	1.2
+++ modules/node/tests/node_test.module	15 May 2009 23:29:45 -0000
@@ -33,3 +33,60 @@ function node_test_node_view($node, $tea
     );
   }
 }
+
+/*
+ * Implementation of hook_node_grants().
+ */
+function node_test_node_grants($account, $op) {
+  // Give everyone full grants so we don't break other tests.
+  return array(
+    'article_realm' => array(1),
+    'page_realm' => array(1),
+    'alter_realm' => array(2),
+  );
+}
+
+/**
+ * Implementation of hook_node_access_records().
+ */
+function node_test_node_access_records($node) {
+  $grants = array();
+  if ($node->type == 'article') {
+    // Create grant in arbitrary article_realm for article nodes.
+    $grants[] = array(
+      'realm' => 'article_realm',
+      'gid' => 1,
+      'grant_view' => 1,
+      'grant_update' => 0,
+      'grant_delete' => 0,
+      'priority' => 0,
+    );
+  }
+  elseif ($node->type == 'page') {
+    // Create grant in arbitrary page_realm for page nodes.
+    $grants[] = array(
+      'realm' => 'page_realm',
+      'gid' => 1,
+      'grant_view' => 1,
+      'grant_update' => 0,
+      'grant_delete' => 0,
+      'priority' => 0,
+    );
+  }
+  return $grants;
+}
+
+/**
+ * Implementation of hook_node_access_records_alter().
+ */
+function node_test_node_access_records_alter(&$grants, $node) {
+  if (!empty($grants)) {
+    foreach ($grants as $key => $grant) {
+      // Alter grant from page_realm to alter_realm and modify the gid.
+      if ($grant['realm'] == 'page_realm' && $node->promote) {
+        $grants[$key]['realm'] = 'alter_realm';
+        $grants[$key]['gid'] = 2;
+      }
+    }
+  }
+}
