? 309007-grants-alter.patch ? node_access_records_alter_6.patch ? sites/default/files 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 17 May 2009 18:25:42 -0000 @@ -109,6 +109,98 @@ 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'. This variable would + * be a configuration setting for your module. + * + * @see hook_node_access_records() + * @see drupal_alter() + * + * @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) { + // Our module allows editors to tag specific articles as 'preview' + // content using the taxonomy system. If the node being saved + // contains one of the preview terms defined in our variable + // 'example_preview_terms', then only our grants are retained, + // and other grants are removed. Doing so ensures that our rules + // are enforced no matter what priority other grrants are given. + $preview = variable_get('example_preview_terms', array()); + // Check to see if we have enabled complex behavior. + if (!empty($preview)) { + foreach ($preview as $term_id) { + if (isset($node->taxonomy[$term_id])) { + // Our module grants are set in $grants['example']. + $temp = $grants['example']; + // Now remove all module grants but our own. + $grants = array('example' => $temp); + // No need to check additonal terms. + continue; + } + } + } +} + +/** + * Alter the access rules for a user trying to view, edit or delete nodes. + * + * Node access modules establish rules for access to content. + * Before node access rules are enforced on a user, this hook + * is called and allows modules to modify the grants array by reference. + * This hook allows developers to write custom modules that alter the + * ways that multiple node access modules interact. + * + * Developers may use this hook to either add additional grants to a user + * or to remove existing grants. These rules are typically based on either the + * permissions assigned to a user role, or specific attributes of a user + * account. + * + * @see hook_node_grants() + * @see drupal_alter() + * + * @param &$grants + * The $grants array returned by hook_node_grants. + * @param $account + * The user account requesting access to content. + * @param $op + * The operation being performed, 'view', 'update' or 'delete'. + * + * @ingroup node_access + */ +function hook_node_access_grants_alter(&$grants, $account, $op) { + // Our sample module never allows certain roles to edit or delete + // content. Since some other node access modules might allow this + // permission, we expressly remove it by returning an empty $grants + // array for roles specified in our variable setting. + + // Get our list of banned roles. + $restricted = variable_get('example_restricted_roles', array()); + + if ($op != 'view' && !empty($restricted)) { + // Now check the roles for this account against the restrictions. + foreach ($restricted as $role_id) { + if (isset($user->roles[$role_id])) { + $grants = array(); + } + } + } +} + +/** * 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.1048 diff -u -p -r1.1048 node.module --- modules/node/node.module 16 May 2009 15:23:16 -0000 1.1048 +++ modules/node/node.module 17 May 2009 18:25:46 -0000 @@ -2428,6 +2428,10 @@ function _node_access_where_sql($op = 'v * access module should implement hook_node_grants() to provide a grant * list for the user. * + * 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. + * * @param $op * The operation that the user is trying to perform. * @param $account @@ -2443,7 +2447,12 @@ function node_access_grants($op, $accoun $account = $GLOBALS['user']; } - return array_merge(array('all' => array(0)), module_invoke_all('node_grants', $account, $op)); + // Fetch node access grants from other modules. + $grants = module_invoke_all('node_grants', $account, $op); + // Allow modules to alter the assigned grants. + drupal_alter('node_access_grants', $grants, $account, $op); + + return array_merge(array('all' => array(0)), $grants); } /** @@ -2467,7 +2476,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 +2548,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 +2562,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 17 May 2009 18:25:47 -0000 @@ -685,6 +685,70 @@ 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, + // hook_node_grants_alter and hook_node_access_records_alter. + parent::setUp('node_test'); + } + + /** + * Create a node and test the creation of node access rules. + */ + 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, 'test_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, 'test_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 was 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, 'test_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.')); + + // Check to see if we can alter grants with hook_node_grants_alter(). + $operations = array('view', 'update', 'delete'); + // Create a user that is allowed to access content. + $web_user = $this->drupalCreateUser(array('access content')); + foreach ($operations as $op) { + $grants = node_test_node_grants($op, $web_user); + $altered_grants = drupal_alter($grants, $web_user, $op); + $this->assertNotEqual($grants, $altered_grants, t('Altered the %op grant for a user.', array('%op' => $op))); + } + } +} + +/** * 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 17 May 2009 18:25:47 -0000 @@ -33,3 +33,70 @@ 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 node tests. + // Our node access tests asserts three realms of access. + // @see testGrantAlter() + return array( + 'test_article_realm' => array(1), + 'test_page_realm' => array(1), + 'test_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' => 'test_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' => 'test_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 test_page_realm to test_alter_realm and modify the gid. + if ($grant['realm'] == 'test_page_realm' && $node->promote) { + $grants[$key]['realm'] = 'test_alter_realm'; + $grants[$key]['gid'] = 2; + } + } + } +} + +/** + * Implementation of hook_node_grants_alter(). + */ +function node_test_node_grants_alter(&$grants, $account, $op) { + // Return an empty array of grants to prove that we can alter by reference. + $grants = array(); +}