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:23:11 -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:23:12 -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:23:13 -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 promated 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:23:13 -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; + } + } + } +}