Index: modules/node/node.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.api.php,v
retrieving revision 1.13
diff -u -p -r1.13 node.api.php
--- modules/node/node.api.php	17 Mar 2009 12:41:54 -0000	1.13
+++ modules/node/node.api.php	16 Apr 2009 02:11:01 -0000
@@ -109,6 +109,36 @@ 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.
+ *
+ * @ingroup node_access
+ */
+function hook_node_access_records_alter(&$grants) {
+  // 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.1033
diff -u -p -r1.1033 node.module
--- modules/node/node.module	26 Mar 2009 13:31:25 -0000	1.1033
+++ modules/node/node.module	16 Apr 2009 02:11:03 -0000
@@ -2525,6 +2525,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.
  *
@@ -2533,11 +2539,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);
+  // 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.20
diff -u -p -r1.20 node.test
--- modules/node/node.test	15 Apr 2009 13:28:08 -0000	1.20
+++ modules/node/node.test	16 Apr 2009 02:11:03 -0000
@@ -664,3 +664,40 @@ class NodeRSSContentTestCase extends Dru
     $this->assertText($test_text, t('Extra node content appears in RSS feed.'));
   }
 }
+
+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 a page node.
+    $node2 = $this->drupalCreateNode(array('type' => 'page'));
+    $this->assertTrue(node_load($node2->nid), t('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', $node2->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.'));
+  }
+}
Index: modules/node/tests/node_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/tests/node_test.module,v
retrieving revision 1.1
diff -u -p -r1.1 node_test.module
--- modules/node/tests/node_test.module	2 Apr 2009 20:47:54 -0000	1.1
+++ modules/node/tests/node_test.module	16 Apr 2009 02:11:03 -0000
@@ -16,3 +16,56 @@ function node_test_node_view($node, $tea
     '#weight' => 10,
   );
 }
+
+/*
+ * Implementation of hook_node_grants().
+ */
+function node_test_node_grants($account, $op) {
+  // This dummy hook needs to be here or else node_access_write_grants will write nothing.
+  return array();
+}
+
+/**
+ * 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) {
+  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') {
+        $grants[$key]['realm'] = 'alter_realm';
+        $grants[$key]['gid'] = 2;
+      }
+    }
+  }
+}
