diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageAwareCombinationTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageAwareCombinationTest.php new file mode 100644 index 0000000..beb97bf --- /dev/null +++ b/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageAwareCombinationTest.php @@ -0,0 +1,355 @@ + 'Node access language aware combination', + 'description' => 'Tests node access functionality with multiple languages and two node access modules.', + 'group' => 'Node', + ); + } + + function setUp() { + parent::setUp(); + + node_access_rebuild(); + + // Clear permissions for authenticated users. + db_delete('role_permission') + ->condition('rid', DRUPAL_AUTHENTICATED_RID) + ->execute(); + + // Add Hungarian and Catalan. + $language = new Language(array( + 'langcode' => 'hu', + )); + language_save($language); + $language = new Language(array( + 'langcode' => 'ca', + )); + language_save($language); + } + + /** + * Tests the node_access() function with multiple node languages. + */ + function testNodeAccessLanguageAwareCombination() { + $web_user = $this->drupalCreateUser(array('access content')); + + $expected_node_access = array('view' => TRUE, 'update' => FALSE, 'delete' => FALSE); + $expected_node_access_no_access = array('view' => FALSE, 'update' => FALSE, 'delete' => FALSE); + + // Creating a Node with default langcode Hungarian and translation Catalan. + // Hungarian language is public, Catalan is public, non language aware + // node_access module public. + $node = $this->drupalCreateNode(array( + 'body' => array('hu' => array(array())), + 'langcode' => 'hu', + 'field_private' => array( + 'hu' => array(0 => array('value' => 0)), + 'ca' => array(0 => array('value' => 0)), + ), + 'private' => FALSE, + )); + $this->assertTrue($node->langcode == 'hu', t('Node created as Hungarian.')); + + $this->assertNodeAccess($expected_node_access, $node, $web_user); + $this->assertNodeAccess($expected_node_access, $node, $web_user, 'hu'); + $this->assertNodeAccess($expected_node_access, $node, $web_user, 'ca'); + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user, 'en'); + + // Creating a Node with default langcode Hungarian and translation Catalan. + // Hungarian language is public, Catalan is public, non language aware + // node_access module private. + // Even the non language aware node_access module (private) prevents access, + // still the settings of the language aware module (field_private) will + // be used. Because it generates grants also when access is granted. + $node = $this->drupalCreateNode(array( + 'body' => array('hu' => array(array())), + 'langcode' => 'hu', + 'field_private' => array( + 'hu' => array(0 => array('value' => 0)), + 'ca' => array(0 => array('value' => 0)), + ), + 'private' => TRUE, + )); + $this->assertTrue($node->langcode == 'hu', t('Node created as Hungarian.')); + + // language aware node_access module Mmdule specificly grants access to + // languages versions of nodes. That is why non language aware node_access + // module cannot prevent access from it. + $this->assertNodeAccess($expected_node_access, $node, $web_user); + $this->assertNodeAccess($expected_node_access, $node, $web_user, 'hu'); + $this->assertNodeAccess($expected_node_access, $node, $web_user, 'ca'); + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user, 'en'); + + // Creating a Node with default langcode Hungarian and translation Catalan. + // Hungarian language is private, Catalan is public, non language aware + // node_access module public. + $node = $this->drupalCreateNode(array( + 'body' => array('hu' => array(array())), + 'langcode' => 'hu', + 'field_private' => array( + 'hu' => array(0 => array('value' => 1)), + 'ca' => array(0 => array('value' => 0)), + ), + 'private' => FALSE, + )); + $this->assertTrue($node->langcode == 'hu', t('Node created as Hungarian.')); + + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user); + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user, 'hu'); + $this->assertNodeAccess($expected_node_access, $node, $web_user, 'ca'); + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user, 'en'); + + // Creating a Node with default langcode Hungarian and translation Catalan. + // Hungarian language is public, Catalan is private, non language aware + // node_access module public. + $node = $this->drupalCreateNode(array( + 'body' => array('hu' => array(array())), + 'langcode' => 'hu', + 'field_private' => array( + 'hu' => array(0 => array('value' => 0)), + 'ca' => array(0 => array('value' => 1)), + ), + 'private' => FALSE, + )); + $this->assertTrue($node->langcode == 'hu', t('Node created as Hungarian.')); + + $this->assertNodeAccess($expected_node_access, $node, $web_user); + $this->assertNodeAccess($expected_node_access, $node, $web_user, 'hu'); + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user, 'ca'); + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user, 'en'); + + // Creating a Node with default langcode Hungarian and translation Catalan. + // Hungarian language is private, Catalan is private, non language aware + // node_access module public. + $node = $this->drupalCreateNode(array( + 'body' => array('hu' => array(array())), + 'langcode' => 'hu', + 'field_private' => array( + 'hu' => array(0 => array('value' => 1)), + 'ca' => array(0 => array('value' => 1)), + ), + 'private' => FALSE, + )); + $this->assertTrue($node->langcode == 'hu', t('Node created as Hungarian.')); + + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user); + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user, 'hu'); + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user, 'ca'); + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user, 'en'); + + // Creating a Node with default langcode Hungarian and translation Catalan. + // Hungarian language is private, Catalan is private, non language aware + // node_access module private. + $node = $this->drupalCreateNode(array( + 'body' => array('hu' => array(array())), + 'langcode' => 'hu', + 'field_private' => array( + 'hu' => array(0 => array('value' => 1)), + 'ca' => array(0 => array('value' => 1)), + ), + 'private' => TRUE, + )); + $this->assertTrue($node->langcode == 'hu', t('Node created as Hungarian.')); + + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user); + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user, 'hu'); + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user, 'ca'); + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user, 'en'); + + } + + /** + * Tests db_select() with a 'node_access' tag and langcode metadata. + */ + function testNodeAccessLanguageAwareQueryTag() { + $web_user = $this->drupalCreateUser(array('access content')); + + // Creating a Node with default langcode Hungarian and translation Catalan. + // Hungarian language is public, Catalan is public, non language aware + // node_access module public. + $node_both_public = $this->drupalCreateNode(array( + 'body' => array('hu' => array(array())), + 'langcode' => 'hu', + 'field_private' => array( + 'hu' => array(0 => array('value' => 0)), + 'ca' => array(0 => array('value' => 0)), + ), + 'private' => FALSE, + )); + + // Creating a Node with default langcode Hungarian and translation Catalan. + // Hungarian language is public, Catalan is public, non language aware + // node_access module private. + // Even the non language aware node_access module (private) prevents access, + // still the settings of the language aware module (field_private) will + // be used. Because it generates grants also when access is granted. + $node_both_public_non_language_aware_private = $this->drupalCreateNode(array( + 'body' => array('hu' => array(array())), + 'langcode' => 'hu', + 'field_private' => array( + 'hu' => array(0 => array('value' => 0)), + 'ca' => array(0 => array('value' => 0)), + ), + 'private' => TRUE, + )); + + // Creating a Node with default langcode Hungarian and translation Catalan. + // Hungarian language is private, Catalan is public, non language aware + // node_access module public. + $node_only_hu_public = $this->drupalCreateNode(array( + 'body' => array('hu' => array(array())), + 'langcode' => 'hu', + 'field_private' => array( + 'hu' => array(0 => array('value' => 0)), + 'ca' => array(0 => array('value' => 1)), + ), + 'private' => FALSE, + )); + + // Creating a Node with default langcode Hungarian and translation Catalan. + // Hungarian language is public, Catalan is private, non language aware + // node_access module public. + $node_only_ca_public = $this->drupalCreateNode(array( + 'body' => array('hu' => array(array())), + 'langcode' => 'hu', + 'field_private' => array( + 'hu' => array(0 => array('value' => 1)), + 'ca' => array(0 => array('value' => 0)), + ), + 'private' => FALSE, + )); + + // Creating a Node with default langcode Hungarian and translation Catalan. + // Hungarian language is private, Catalan is private, non language aware + // node_access module public. + // Even the non language aware node_access module (private) grants access, + // still the settings of the language aware module (field_private) will + // be used. Because the non language aware node_access module (private) + // does only create grants when it is settings is private. + $node_no_public_non_language_aware_public = $this->drupalCreateNode(array( + 'body' => array('hu' => array(array())), + 'langcode' => 'hu', + 'field_private' => array( + 'hu' => array(0 => array('value' => 1)), + 'ca' => array(0 => array('value' => 1)), + ), + 'private' => FALSE, + )); + + // Creating a Node with default langcode Hungarian and translation Catalan. + // Hungarian language is private, Catalan is private, non language aware + // node_access module public. + $node_no_public = $this->drupalCreateNode(array( + 'body' => array('hu' => array(array())), + 'langcode' => 'hu', + 'field_private' => array( + 'hu' => array(0 => array('value' => 1)), + 'ca' => array(0 => array('value' => 1)), + ), + 'private' => TRUE, + )); + + // Query the nodes table as readonly user with Node Access Tag and no + // specific langcode. + $select = db_select('node', 'n') + ->fields('n', array('nid')) + ->addMetaData('account', $web_user) + ->addTag('node_access'); + $nids = $select->execute()->fetchAllAssoc('nid'); + + // Because no langcode is given it will use the fallback language + // (which is hungarian). + $this->assertEqual(count($nids), 3, 'db_select returns 3 nodes'); + $this->assertTrue(array_key_exists($node_both_public->nid, $nids), 'Returned node ID is full public node.'); + $this->assertTrue(array_key_exists($node_only_hu_public->nid, $nids), 'Returned node ID is Hungarian public only node.'); + $this->assertTrue(array_key_exists($node_both_public_non_language_aware_private->nid, $nids), 'Returned node ID is both public non language aware private only node.'); + + // Query the nodes table as readonly user with Node Access Tag and + // Hungarian langcode + $select = db_select('node', 'n') + ->fields('n', array('nid')) + ->addMetaData('account', $web_user) + ->addMetaData('langcode', 'hu') + ->addTag('node_access'); + $nids = $select->execute()->fetchAllAssoc('nid'); + + $this->assertEqual(count($nids), 3, 'db_select returns 3 nodes'); + $this->assertTrue(array_key_exists($node_both_public->nid, $nids), 'Returned node ID is both public node.'); + $this->assertTrue(array_key_exists($node_only_hu_public->nid, $nids), 'Returned node ID is Hungarian public only node.'); + $this->assertTrue(array_key_exists($node_both_public_non_language_aware_private->nid, $nids), 'Returned node ID is both public non language aware private only node.'); + + // Query the nodes table as readonly user with Node Access Tag and + // Catalan langcode + $select = db_select('node', 'n') + ->fields('n', array('nid')) + ->addMetaData('account', $web_user) + ->addMetaData('langcode', 'ca') + ->addTag('node_access'); + $nids = $select->execute()->fetchAllAssoc('nid'); + + $this->assertEqual(count($nids), 3, 'db_select returns 3 nodes'); + $this->assertTrue(array_key_exists($node_both_public->nid, $nids), 'Returned node ID is both public node.'); + $this->assertTrue(array_key_exists($node_only_ca_public->nid, $nids), 'Returned node ID is Catalan public only node.'); + $this->assertTrue(array_key_exists($node_both_public_non_language_aware_private->nid, $nids), 'Returned node ID is both public non language aware private only node.'); + + + // Query the nodes table as readonly User with Node Access Tag and + // langcode de. + $select = db_select('node', 'n') + ->fields('n', array('nid')) + ->addMetaData('account', $web_user) + ->addMetaData('langcode', 'de') + ->addTag('node_access'); + $nids = $select->execute()->fetchAllAssoc('nid'); + + // Because all created nodes are not in German, no Nodes are returned. + $this->assertTrue(empty($nids), 'db_select returns empty result'); + + + // Query the nodes table as User 1 (full access) with Node Access Tag and no + // specific langcode. + $select = db_select('node', 'n') + ->fields('n', array('nid')) + ->addTag('node_access'); + $nids = $select->execute()->fetchAllAssoc('nid'); + + // Both nodes are returned. + $this->assertEqual(count($nids), 6, 'db_select returns all nodes'); + + // Query the nodes table as User 1 (full access) with Node Access Tag and + // langcode de. + $select = db_select('node', 'n') + ->fields('n', array('nid')) + ->addMetaData('langcode', 'de') + ->addTag('node_access'); + $nids = $select->execute()->fetchAllAssoc('nid'); + + // Both nodes are returned because node access tag is not invoked when + // the user is user 1. + $this->assertEqual(count($nids), 6, 'db_select returns all nodes'); + } + +} diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageAwareTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageAwareTest.php new file mode 100644 index 0000000..46d200f --- /dev/null +++ b/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageAwareTest.php @@ -0,0 +1,289 @@ + 'Node access language aware', + 'description' => 'Test node_access and db_select with node_access tag functionality with multiple languages with node_access_test_language which is language aware.', + 'group' => 'Node', + ); + } + + function setUp() { + parent::setUp(); + + node_access_rebuild(); + + // Clear permissions for authenticated users. + db_delete('role_permission') + ->condition('rid', DRUPAL_AUTHENTICATED_RID) + ->execute(); + + // Add Hungarian and Catalan. + $language = new Language(array( + 'langcode' => 'hu', + )); + language_save($language); + $language = new Language(array( + 'langcode' => 'ca', + )); + language_save($language); + } + + /** + * Tests node_access() with multiple node languages. + */ + function testNodeAccessLanguageAware() { + $web_user = $this->drupalCreateUser(array('access content')); + + $expected_node_access = array('view' => TRUE, 'update' => FALSE, 'delete' => FALSE); + $expected_node_access_no_access = array('view' => FALSE, 'update' => FALSE, 'delete' => FALSE); + + + // Creating a Node with default langcode Hungarian and translation Catalan. + // Hungarian language is public, Catalan is public. + $node = $this->drupalCreateNode(array( + 'body' => array('hu' => array(array())), + 'langcode' => 'hu', + 'field_private' => array( + 'hu' => array(0 => array('value' => 0)), + 'ca' => array(0 => array('value' => 0)), + ) + )); + $this->assertTrue($node->langcode == 'hu', t('Node created as Hungarian.')); + + // Tests that access to default language (Hungarian) is granted. + $this->assertNodeAccess($expected_node_access, $node, $web_user); + + // Tests that access to hungarian language is granted. + $this->assertNodeAccess($expected_node_access, $node, $web_user, 'hu'); + + // Tests that access to catalan language is granted. + $this->assertNodeAccess($expected_node_access, $node, $web_user, 'ca'); + + // Tests that access to english language is granted. + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user, 'en'); + + // Creating a Node with default langcode Hungarian and translation Catalan. + // Hungarian language is private, Catalan is public. + $node = $this->drupalCreateNode(array( + 'body' => array('hu' => array(array())), + 'langcode' => 'hu', + 'field_private' => array( + 'hu' => array(0 => array('value' => 1)), + 'ca' => array(0 => array('value' => 0)), + ) + )); + $this->assertTrue($node->langcode == 'hu', t('Node created as Hungarian.')); + + // Tests that access to default language (Hungarian) is not granted. + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user); + + // Tests that access to hungarian language is not granted. + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user, 'hu'); + + // Tests that access to catalan language is granted. + $this->assertNodeAccess($expected_node_access, $node, $web_user, 'ca'); + + // Tests that access to english language is not granted. + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user, 'en'); + + // Creating a Node with default langcode Hungarian and translation Catalan. + // Hungarian language is public, Catalan is private. + $node = $this->drupalCreateNode(array( + 'body' => array('hu' => array(array())), + 'langcode' => 'hu', + 'field_private' => array( + 'hu' => array(0 => array('value' => 0)), + 'ca' => array(0 => array('value' => 1)), + ) + )); + $this->assertTrue($node->langcode == 'hu', t('Node created as Hungarian.')); + + // Tests that access to default language (Hungarian) is granted. + $this->assertNodeAccess($expected_node_access, $node, $web_user); + + // Tests that access to hungarian language is granted. + $this->assertNodeAccess($expected_node_access, $node, $web_user, 'hu'); + + // Tests that access to catalan language is not granted. + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user, 'ca'); + + // Tests that access to english language is granted. + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user, 'en'); + + // Creating a Node with default langcode Hungarian and translation Catalan. + // Hungarian language is private, Catalan is private. + $node = $this->drupalCreateNode(array( + 'body' => array('hu' => array(array())), + 'langcode' => 'hu', + 'field_private' => array( + 'hu' => array(0 => array('value' => 1)), + 'ca' => array(0 => array('value' => 1)), + ) + )); + $this->assertTrue($node->langcode == 'hu', t('Node created as Hungarian.')); + + // Tests that access to default language (Hungarian) is not granted. + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user); + + // Tests that access to hungarian language is not granted. + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user, 'hu'); + + // Tests that access to catalan language is not granted. + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user, 'ca'); + + // Tests that access to english language is granted. + $this->assertNodeAccess($expected_node_access_no_access, $node, $web_user, 'en'); + } + + /** + * Tests db_select() with a 'node_access' tag and langcode metadata. + */ + function testNodeAccessLanguageAwareQueryTag() { + $web_user = $this->drupalCreateUser(array('access content')); + + // Creating a Node with default langcode Hungarian and translation Catalan. + // Hungarian language is public, Catalan is public. + $node_both_public = $this->drupalCreateNode(array( + 'body' => array('hu' => array(array())), + 'langcode' => 'hu', + 'field_private' => array( + 'hu' => array(0 => array('value' => 0)), + 'ca' => array(0 => array('value' => 0)), + ) + )); + + // Creating a Node with default langcode Hungarian and translation Catalan. + // Hungarian language is private, Catalan is public. + $node_only_hu_public = $this->drupalCreateNode(array( + 'body' => array('hu' => array(array())), + 'langcode' => 'hu', + 'field_private' => array( + 'hu' => array(0 => array('value' => 0)), + 'ca' => array(0 => array('value' => 1)), + ) + )); + + // Creating a Node with default langcode Hungarian and translation Catalan. + // Hungarian language is public, Catalan is private. + $node_only_ca_public = $this->drupalCreateNode(array( + 'body' => array('hu' => array(array())), + 'langcode' => 'hu', + 'field_private' => array( + 'hu' => array(0 => array('value' => 1)), + 'ca' => array(0 => array('value' => 0)), + ) + )); + + // Creating a Node with default langcode Hungarian and translation Catalan. + // Hungarian language is private, Catalan is private. + $node_no_public = $this->drupalCreateNode(array( + 'body' => array('hu' => array(array())), + 'langcode' => 'hu', + 'field_private' => array( + 'hu' => array(0 => array('value' => 1)), + 'ca' => array(0 => array('value' => 1)), + ) + )); + + // Query the nodes table as readonly user with Node Access Tag and no + // specific langcode. + $select = db_select('node', 'n') + ->fields('n', array('nid')) + ->addMetaData('account', $web_user) + ->addTag('node_access'); + $nids = $select->execute()->fetchAllAssoc('nid'); + + // Because no langcode is given it will use the fallback language + // (which is hungarian). + $this->assertEqual(count($nids), 2, 'db_select returns 2 nodes'); + $this->assertTrue(array_key_exists($node_both_public->nid, $nids), 'Returned node ID is full public node.'); + $this->assertTrue(array_key_exists($node_only_hu_public->nid, $nids), 'Returned node ID is Hungarian public only node.'); + + // Query the nodes table as readonly user with Node Access Tag and + // Hungarian langcode + $select = db_select('node', 'n') + ->fields('n', array('nid')) + ->addMetaData('account', $web_user) + ->addMetaData('langcode', 'hu') + ->addTag('node_access'); + $nids = $select->execute()->fetchAllAssoc('nid'); + + // Same nodes as witouth langcode should be returned. + $this->assertEqual(count($nids), 2, 'db_select returns 2 nodes'); + $this->assertTrue(array_key_exists($node_both_public->nid, $nids), 'Returned node ID is both public node.'); + $this->assertTrue(array_key_exists($node_only_hu_public->nid, $nids), 'Returned node ID is Hungarian public only node.'); + + // Query the nodes table as readonly user with Node Access Tag and + // Catalan langcode + $select = db_select('node', 'n') + ->fields('n', array('nid')) + ->addMetaData('account', $web_user) + ->addMetaData('langcode', 'ca') + ->addTag('node_access'); + $nids = $select->execute()->fetchAllAssoc('nid'); + + // Only the Catalan and the Full Public node should be returned. + $this->assertEqual(count($nids), 2, 'db_select returns 2 nodes'); + $this->assertTrue(array_key_exists($node_both_public->nid, $nids), 'Returned node ID is both public node.'); + $this->assertTrue(array_key_exists($node_only_ca_public->nid, $nids), 'Returned node ID is Catalan public only node.'); + + + // Query the nodes table as readonly User with Node Access Tag and + // langcode de. + $select = db_select('node', 'n') + ->fields('n', array('nid')) + ->addMetaData('account', $web_user) + ->addMetaData('langcode', 'de') + ->addTag('node_access'); + $nids = $select->execute()->fetchAllAssoc('nid'); + + // Because all created nodes are not in German, no Nodes are returned. + $this->assertTrue(empty($nids), 'db_select returns empty result'); + + + // Query the nodes table as User 1 (full access) with Node Access Tag and no + // specific langcode. + $select = db_select('node', 'n') + ->fields('n', array('nid')) + ->addTag('node_access'); + $nids = $select->execute()->fetchAllAssoc('nid'); + + // Both nodes are returned. + $this->assertEqual(count($nids), 4, 'db_select returns all nodes'); + + // Query the nodes table as User 1 (full access) with Node Access Tag and + // langcode de. + $select = db_select('node', 'n') + ->fields('n', array('nid')) + ->addMetaData('langcode', 'de') + ->addTag('node_access'); + $nids = $select->execute()->fetchAllAssoc('nid'); + + // Both nodes are returned because node access tag is not invoked when + // the user is user 1. + $this->assertEqual(count($nids), 4, 'db_select returns all nodes'); + } + +} diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageTest.php index 6ff6ae0..9fc802c 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageTest.php @@ -24,34 +24,24 @@ class NodeAccessLanguageTest extends NodeTestBase { public static function getInfo() { return array( 'name' => 'Node access language', - 'description' => 'Test node_access functionality with multiple languages.', + 'description' => 'Test node_access and db_select with node_access tag functionality with multiple languages with a test node access module that is not language-aware.', 'group' => 'Node', ); } - /** - * Asserts node_access correctly grants or denies access. - */ - function assertNodeAccess($ops, $node, $account, $langcode = NULL) { - foreach ($ops as $op => $result) { - $msg = t("node_access returns @result with operation '@op', language code @langcode.", array('@result' => $result ? 'true' : 'false', '@op' => $op, '@langcode' => !empty($langcode) ? "'$langcode'" : 'empty')); - $this->assertEqual($result, node_access($op, $node, $account, $langcode), $msg); - } - } - function setUp() { parent::setUp(); + node_access_rebuild(); + // Clear permissions for authenticated users. db_delete('role_permission') ->condition('rid', DRUPAL_AUTHENTICATED_RID) ->execute(); - } - /** - * Runs tests for node_access function with multiple languages. - */ - function testNodeAccess() { + // Enable the private node feature of node_access_test module. + variable_set('node_access_test_private', TRUE); + // Add Hungarian and Catalan. $language = new Language(array( 'langcode' => 'hu', @@ -61,31 +51,190 @@ function testNodeAccess() { 'langcode' => 'ca', )); language_save($language); + } - // Tests the default access provided for a published Hungarian node. + /** + * Tests node_access() with multiple node languages and no private nodes. + */ + function testNodeAccess() { $web_user = $this->drupalCreateUser(array('access content')); - $node = $this->drupalCreateNode(array('body' => array('hu' => array(array())), 'langcode' => 'hu')); - $this->assertTrue($node->langcode == 'hu', 'Node created as Hungarian.'); + $expected_node_access = array('view' => TRUE, 'update' => FALSE, 'delete' => FALSE); - $this->assertNodeAccess($expected_node_access, $node, $web_user); + $expected_node_access_no_access = array('view' => FALSE, 'update' => FALSE, 'delete' => FALSE); + + // Creating a public Node with langcode Hungarian, will be saved as + // the fallback in node access table. + $node_public = $this->drupalCreateNode(array('body' => array('hu' => array(array())), 'langcode' => 'hu', 'private' => FALSE)); + $this->assertTrue($node_public->langcode == 'hu', 'Node created as Hungarian.'); + + // Tests the default access provided for the public Hungarian node. + $this->assertNodeAccess($expected_node_access, $node_public, $web_user); // Tests that Hungarian provided specifically results in the same. - $this->assertNodeAccess($expected_node_access, $node, $web_user, 'hu'); + $this->assertNodeAccess($expected_node_access, $node_public, $web_user, 'hu'); + + // There is no specific Catalan version of this node and Croatian is not + // even set up on the system in this scenario, so the user will not get + // access to these nodes. + $this->assertNodeAccess($expected_node_access_no_access, $node_public, $web_user, 'ca'); + $this->assertNodeAccess($expected_node_access_no_access, $node_public, $web_user, 'hr'); + + // Creating a public Node with no special langcode, like when no language + // Module enabled. + $node_public_no_language = $this->drupalCreateNode(array('private' => FALSE)); + $this->assertTrue($node_public_no_language->langcode == LANGUAGE_NOT_SPECIFIED, 'Node created with not specified language.'); + + // Tests that access provided if requested with no language. + $this->assertNodeAccess($expected_node_access, $node_public_no_language, $web_user); + + // Tests that access not provided if requested with Hungarian language. + $this->assertNodeAccess($expected_node_access_no_access, $node_public_no_language, $web_user, 'hu'); // There is no specific Catalan version of this node and Croatian is not - // even set up on the system in this scenario, so these languages will not - // play a role in the node's permissions. - $this->assertNodeAccess($expected_node_access, $node, $web_user, 'ca'); - $this->assertNodeAccess($expected_node_access, $node, $web_user, 'hr'); + // even set up on the system in this scenario, so the user will not get + // access to these nodes. + $this->assertNodeAccess($expected_node_access_no_access, $node_public_no_language, $web_user, 'ca'); + $this->assertNodeAccess($expected_node_access_no_access, $node_public_no_language, $web_user, 'hr'); // Reset the node access cache and turn on our test node_access() code. drupal_static_reset('node_access'); variable_set('node_access_test_secret_catalan', 1); - // Tests that Hungarian is still accessible. - $this->assertNodeAccess($expected_node_access, $node, $web_user, 'hu'); + // Tests that access provided if requested with no language. + $this->assertNodeAccess($expected_node_access, $node_public_no_language, $web_user); + + // Tests that Hungarian is still not accessible. + $this->assertNodeAccess($expected_node_access_no_access, $node_public_no_language, $web_user, 'hu'); - // Tests that Catalan is not accessible anymore. - $this->assertNodeAccess(array('view' => FALSE, 'update' => FALSE, 'delete' => FALSE), $node, $web_user, 'ca'); + // Tests that Catalan is still not accessible. + $this->assertNodeAccess($expected_node_access_no_access, $node_public_no_language, $web_user, 'ca'); } + + /** + * Tests node_access() with multiple node languages and private nodes. + */ + function testNodeAccessPrivate() { + $web_user = $this->drupalCreateUser(array('access content')); + + $expected_node_access = array('view' => TRUE, 'update' => FALSE, 'delete' => FALSE); + $expected_node_access_no_access = array('view' => FALSE, 'update' => FALSE, 'delete' => FALSE); + + // Creating a private Node with langcode Hungarian, will be saved as + // the fallback in node access table. + $node_public = $this->drupalCreateNode(array('body' => array('hu' => array(array())), 'langcode' => 'hu', 'private' => TRUE)); + $this->assertTrue($node_public->langcode == 'hu', 'Node created as Hungarian.'); + + // Tests the default access is not provided for the private Hungarian node. + $this->assertNodeAccess($expected_node_access_no_access, $node_public, $web_user); + + // Tests that Hungarian provided specifically results in the same. + $this->assertNodeAccess($expected_node_access_no_access, $node_public, $web_user, 'hu'); + + // There is no specific Catalan version of this node and Croatian is not + // even set up on the system in this scenario, so the user will not get + // access to these nodes. + $this->assertNodeAccess($expected_node_access_no_access, $node_public, $web_user, 'ca'); + $this->assertNodeAccess($expected_node_access_no_access, $node_public, $web_user, 'hr'); + + // Creating a private Node with no special langcode, like when no language + // Module enabled. + $node_private_no_language = $this->drupalCreateNode(array('private' => TRUE)); + $this->assertTrue($node_private_no_language->langcode == LANGUAGE_NOT_SPECIFIED, 'Node created with not specified language.'); + + // Tests that access not provided if requested with no language. + $this->assertNodeAccess($expected_node_access_no_access, $node_private_no_language, $web_user); + + // Tests that access not provided if requested with Hungarian language. + $this->assertNodeAccess($expected_node_access_no_access, $node_private_no_language, $web_user, 'hu'); + + // There is no specific Catalan version of this node and Croatian is not + // even set up on the system in this scenario, so the user will not get + // access to these nodes. + $this->assertNodeAccess($expected_node_access_no_access, $node_private_no_language, $web_user, 'ca'); + $this->assertNodeAccess($expected_node_access_no_access, $node_private_no_language, $web_user, 'hr'); + + // Reset the node access cache and turn on our test node_access() code. + drupal_static_reset('node_access'); + variable_set('node_access_test_secret_catalan', 1); + + // Tests that access not provided if requested with no language. + $this->assertNodeAccess($expected_node_access_no_access, $node_private_no_language, $web_user); + + // Tests that Hungarian is still not accessible. + $this->assertNodeAccess($expected_node_access_no_access, $node_private_no_language, $web_user, 'hu'); + + // Tests that Catalan is still not accessible. + $this->assertNodeAccess($expected_node_access_no_access, $node_private_no_language, $web_user, 'ca'); + } + + /** + * Tests db_select() with a 'node_access' tag and langcode metadata. + */ + function testNodeAccessQueryTag() { + $web_user = $this->drupalCreateUser(array('access content')); + + // Creating a private Node with langcode Hungarian, will be saved as + // the fallback in node access table. + $node_private = $this->drupalCreateNode(array('body' => array('hu' => array(array())), 'langcode' => 'hu', 'private' => TRUE)); + $this->assertTrue($node_private->langcode == 'hu', 'Node created as Hungarian.'); + + // Creating a public Node with langcode Hungarian, will be saved as + // the fallback in node access table. + $node_public = $this->drupalCreateNode(array('body' => array('hu' => array(array())), 'langcode' => 'hu', 'private' => FALSE)); + $this->assertTrue($node_public->langcode == 'hu', 'Node created as Hungarian.'); + + // Creating a public Node with no special langcode, like when no language + // Module enabled. + $node_no_language = $this->drupalCreateNode(array('private' => FALSE)); + $this->assertTrue($node_no_language->langcode == LANGUAGE_NOT_SPECIFIED, 'Node created with not specified language.'); + + // Query the nodes table as readonly user with Node Access Tag and no + // specific langcode. + $select = db_select('node', 'n') + ->fields('n', array('nid')) + ->addMetaData('account', $web_user) + ->addTag('node_access'); + $nids = $select->execute()->fetchAllAssoc('nid'); + + // The public node and no language node should be returned. Because no + // langcode is given itwill use the fallback node. + $this->assertEqual(count($nids), 2, 'db_select returns 2 node'); + $this->assertTrue(array_key_exists($node_public->nid, $nids), 'Returned node ID is public node.'); + $this->assertTrue(array_key_exists($node_no_language->nid, $nids), 'Returned node ID is no language node.'); + + // Query the nodes table as readonly User with Node Access Tag and + // langcode de. + $select = db_select('node', 'n') + ->fields('n', array('nid')) + ->addMetaData('account', $web_user) + ->addMetaData('langcode', 'de') + ->addTag('node_access'); + $nids = $select->execute()->fetchAllAssoc('nid'); + + // Because no Nodes are created in German, no Nodes are returned. + $this->assertTrue(empty($nids), 'db_select returns empty result'); + + // Query the nodes table as User 1 (full access) with Node Access Tag and no + // specific langcode. + $select = db_select('node', 'n') + ->fields('n', array('nid')) + ->addTag('node_access'); + $nids = $select->execute()->fetchAllAssoc('nid'); + + // All nodes are returned. + $this->assertEqual(count($nids), 3, 'db_select returns all three nodes.'); + + // Query the nodes table as User 1 (full access) with Node Access Tag and + // langcode de. + $select = db_select('node', 'n') + ->fields('n', array('nid')) + ->addMetaData('langcode', 'de') + ->addTag('node_access'); + $nids = $select->execute()->fetchAllAssoc('nid'); + + // All nodes are returned because node access tag is not invoked when + // the user is user 1. + $this->assertEqual(count($nids), 3, 'db_select returns all three nodes.'); + } + } diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeAccessTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessTest.php index 4f87348..73b3a44 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeAccessTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeAccessTest.php @@ -23,16 +23,6 @@ public static function getInfo() { ); } - /** - * Asserts node_access() correctly grants or denies access. - */ - function assertNodeAccess($ops, $node, $account) { - foreach ($ops as $op => $result) { - $msg = t("node_access returns @result with operation '@op'.", array('@result' => $result ? 'true' : 'false', '@op' => $op)); - $this->assertEqual($result, node_access($op, $node, $account), $msg); - } - } - function setUp() { parent::setUp(); // Clear permissions for authenticated users. @@ -76,4 +66,5 @@ function testNodeAccess() { $node5 = $this->drupalCreateNode(); $this->assertNodeAccess(array('view' => TRUE, 'update' => FALSE, 'delete' => FALSE), $node5, $web_user3); } + } diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeTestBase.php b/core/modules/node/lib/Drupal/node/Tests/NodeTestBase.php index 1bba558..40121eb 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeTestBase.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeTestBase.php @@ -30,4 +30,35 @@ function setUp() { $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article')); } } + + /** + * Asserts that node_access() correctly grants or denies access. + * + * @param array $ops + * An associative array of the expected node access grants for the node + * and account, with each key as the name of an operation (e.g. 'view', + * 'delete') and each value a Boolean indicating whether access to that + * operation should be granted. + * @param \Drupal\node\Plugin\Core\Entity\Node $node + * The node object to check. + * @param \Drupal\user\Plugin\Core\Entity\User $account + * The user account for which to check access. + * @param string|null $langcode + * (optional) The language code indicating which translation of the node + * to check. If NULL, the untranslated (fallback) access is checked. + */ + function assertNodeAccess(array $ops, $node, $account, $langcode = NULL) { + foreach ($ops as $op => $result) { + $msg = format_string( + "node_access() returns @result with operation %op, language code %langcode.", + array( + '@result' => $result ? 'true' : 'false', + '%op' => $op, + '%langcode' => !empty($langcode) ? $langcode : 'empty' + ) + ); + $this->assertEqual($result, node_access($op, $node, $account, $langcode), $msg); + } + } + } diff --git a/core/modules/node/node.api.php b/core/modules/node/node.api.php index c139952..9624dd8 100644 --- a/core/modules/node/node.api.php +++ b/core/modules/node/node.api.php @@ -231,11 +231,16 @@ function hook_node_grants($account, $op) { * of this gid within this realm can edit this node. * - 'grant_delete': If set to 1 a user that has been identified as a member * of this gid within this realm can delete this node. - * - * - * When an implementation is interested in a node but want to deny access to - * everyone, it may return a "deny all" grant: - * + * - langcode: (optional) The language code of a specific translation of the + * node, if any. Modules may add this key to grant different access to + * different translations of a node, such that (e.g.) a particular group + * is granted access to edit the Catalan version of the node, but not the + * Hungarian version. If no value is provided, the langcode is set + * set automatically from the $node parameter and the node's original + * language (if specified) is used as a fallback. + * + * A "deny all" grant may be used to deny all access to a particular node or + * node translation: * @code * $grants[] = array( * 'realm' => 'all', @@ -243,15 +248,14 @@ function hook_node_grants($account, $op) { * 'grant_view' => 0, * 'grant_update' => 0, * 'grant_delete' => 0, - * 'priority' => 1, + * 'langcode' => 'ca', * ); * @endcode - * - * Setting the priority should cancel out other grants. In the case of a - * conflict between modules, it is safer to use hook_node_access_records_alter() - * to return only the deny grant. - * - * Note: a deny all grant is not written to the database; denies are implicit. + * Note that another module node access module could override this by granting + * access to one or more nodes, since grants are additive. To enforce that + * access is denied in a particular case, use hook_node_access_records_alter(). + * Also node that a deny all grant is not written to the database; denies are + * implicit. * * @param Drupal\node\Node $node * The node that has just been saved. @@ -268,8 +272,9 @@ function hook_node_access_records(Drupal\node\Node $node) { // treated just like any other node and we completely ignore it. if ($node->private) { $grants = array(); - // Only published nodes should be viewable to all users. If we allow access - // blindly here, then all users could view an unpublished node. + // Only published Catalan translations of private nodes should be viewable + // to all users. If we fail to check $node->status, all users would be able + // to view an unpublished node. if ($node->status) { $grants[] = array( 'realm' => 'example', @@ -277,6 +282,7 @@ function hook_node_access_records(Drupal\node\Node $node) { 'grant_view' => 1, 'grant_update' => 0, 'grant_delete' => 0, + 'langcode' => 'ca' ); } // For the example_author array, the GID is equivalent to a UID, which @@ -289,6 +295,7 @@ function hook_node_access_records(Drupal\node\Node $node) { 'grant_view' => 1, 'grant_update' => 1, 'grant_delete' => 1, + 'langcode' => 'ca' ); return $grants; diff --git a/core/modules/node/node.install b/core/modules/node/node.install index f9c30a5..3ba3d30 100644 --- a/core/modules/node/node.install +++ b/core/modules/node/node.install @@ -149,6 +149,20 @@ function node_schema() { 'not null' => TRUE, 'default' => 0, ), + 'langcode' => array( + 'description' => 'The {language}.langcode of this node.', + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + ), + 'fallback' => array( + 'description' => 'Boolean indicating whether this record should be used as a fallback if a language condition is not provided.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 1, + ), 'gid' => array( 'description' => "The grant ID a user must possess in the specified realm to gain this row's privileges on the node.", 'type' => 'int', @@ -188,7 +202,7 @@ function node_schema() { 'size' => 'tiny', ), ), - 'primary key' => array('nid', 'gid', 'realm'), + 'primary key' => array('nid', 'gid', 'langcode', 'realm'), 'foreign keys' => array( 'affected_node' => array( 'table' => 'node', @@ -740,6 +754,35 @@ function node_update_8011() { } /** + * Add language.langcode and fallback field to node_access table. + */ +function node_update_8012() { + // Add the langcode field. + $langcode_field = array( + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The {language}.langcode of this node.', + ); + db_add_field('node_access', 'langcode', $langcode_field); + + // Add the fallback field. + $fallback_field = array( + 'description' => 'Boolean indicating whether this record should be used as a fallback if a language condition is not provided.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 1, + ); + db_add_field('node_access', 'fallback', $fallback_field); + + db_drop_primary_key('node_access'); + db_add_primary_key('node_access', array('nid', 'gid', 'langcode', 'realm')); +} + + +/** * @} End of "addtogroup updates-7.x-to-8.x" * The next series of updates should start at 9000. */ diff --git a/core/modules/node/node.module b/core/modules/node/node.module index a6a64ae..22f4931 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -2781,7 +2781,24 @@ function node_access($op, $node, $account = NULL, $langcode = NULL) { // to an empty langcode if a node type was requested. The latter is purely // for caching purposes. if (empty($langcode)) { - $langcode = (is_object($node) && isset($node->nid)) ? $node->langcode : ''; + // Initialize the langcode as an empty string. + $langcode = ''; + if (is_object($node) && isset($node->nid)) { + // Default to the node's default langcode. + $langcode = $node->langcode; + // If the Language module is enabled, try to use the language from + // content negotiation. + if (module_exists('language')) { + // Load languages the node exists in. + $node_translations = $node->getTranslationLanguages(); + // Load the language from content negotiation. + $content_negotiation_langcode = language(LANGUAGE_TYPE_CONTENT)->langcode; + // If there is a translation available, use it. + if (isset($node_translations[$content_negotiation_langcode])) { + $langcode = $content_negotiation_langcode; + } + } + } } // If we've already checked access for this node, user and op, return from @@ -2827,9 +2844,13 @@ function node_access($op, $node, $account = NULL, $langcode = NULL) { $query = db_select('node_access'); $query->addExpression('1'); $query->condition('grant_' . $op, 1, '>='); - $nids = db_or()->condition('nid', $node->nid); + $nids = db_and() + ->condition('nid', $node->nid) + ->condition('langcode', $langcode); if ($node->status) { - $nids->condition('nid', 0); + $nids = db_or() + ->condition($nids) + ->condition('nid', 0); } $query->condition($nids); $query->range(0, 1); @@ -3065,6 +3086,9 @@ function node_query_node_access_alter(AlterableInterface $query) { if (!$op = $query->getMetaData('op')) { $op = 'view'; } + if (!$langcode = $query->getMetaData('langcode')) { + $langcode = FALSE; + } // If $account can bypass node access, or there are no node access modules, // or the operation is 'view' and the $acount has a global view grant (i.e., @@ -3128,6 +3152,13 @@ function node_query_node_access_alter(AlterableInterface $query) { $subquery->condition($grant_conditions); } $subquery->condition('na.grant_' . $op, 1, '>='); + if ($langcode === FALSE) { + $subquery->condition('na.fallback', 1, '='); + } + else { + $subquery->condition('na.langcode', $langcode, '='); + } + $field = 'nid'; // Now handle entities. $subquery->where("$nalias.$field = na.nid"); @@ -3178,11 +3209,8 @@ function node_access_acquire_grants(Node $node, $delete = TRUE) { * @param Drupal\node\Node $node * The node whose grants are being written. * @param $grants - * A list of grants to write. Each grant is an array that must contain the - * following keys: realm, gid, grant_view, grant_update, grant_delete. - * The realm is specified by a particular module; the gid is as well, and - * is a module-defined id to define grant privileges. each grant_* field - * is a boolean value. + * A list of grants to write. See hook_node_access_records() for the + * expected structure of the grants array. * @param $realm * (optional) If provided, read/write grants for that realm only. Defaults to * NULL. @@ -3204,7 +3232,7 @@ function _node_access_write_grants(Node $node, $grants, $realm = NULL, $delete = // Only perform work when node_access modules are active. if (!empty($grants) && count(module_implements('node_grants'))) { - $query = db_insert('node_access')->fields(array('nid', 'realm', 'gid', 'grant_view', 'grant_update', 'grant_delete')); + $query = db_insert('node_access')->fields(array('nid', 'langcode', 'fallback', 'realm', 'gid', 'grant_view', 'grant_update', 'grant_delete')); foreach ($grants as $grant) { if ($realm && $realm != $grant['realm']) { continue; @@ -3212,6 +3240,16 @@ function _node_access_write_grants(Node $node, $grants, $realm = NULL, $delete = // Only write grants; denies are implicit. if ($grant['grant_view'] || $grant['grant_update'] || $grant['grant_delete']) { $grant['nid'] = $node->nid; + if (!isset($grant['langcode'])) { + $grant['langcode'] = $node->langcode; + } + // The record with the original langcode is used as the fallback. + if ($grant['langcode'] == $node->langcode) { + $grant['fallback'] = 1; + } + else { + $grant['fallback'] = 0; + } $query->values($grant); } } diff --git a/core/modules/node/tests/modules/node_access_test_language/node_access_test_language.info b/core/modules/node/tests/modules/node_access_test_language/node_access_test_language.info new file mode 100644 index 0000000..e8f72c5 --- /dev/null +++ b/core/modules/node/tests/modules/node_access_test_language/node_access_test_language.info @@ -0,0 +1,7 @@ +name = "Node module access tests language" +description = "Support module for language-aware node access testing." +package = Testing +version = VERSION +core = 8.x +dependencies[] = options +hidden = TRUE diff --git a/core/modules/node/tests/modules/node_access_test_language/node_access_test_language.module b/core/modules/node/tests/modules/node_access_test_language/node_access_test_language.module new file mode 100644 index 0000000..b5c7880 --- /dev/null +++ b/core/modules/node/tests/modules/node_access_test_language/node_access_test_language.module @@ -0,0 +1,117 @@ +uid); + if ($op == 'view' && user_access('node access language test view', $account)) { + $grants['node_access_language_test'] = array(7888, 7889); + } + + $no_access_uid = state()->get('node_access_language_test.no_access_uid') ?: 0; + if ($op == 'view' && $account->uid == $no_access_uid) { + $grants['node_access_language_all'] = array(0); + } + return $grants; +} + +/** + * Implements hook_node_access_records(). + */ +function node_access_test_language_node_access_records(Node $node) { + $grants = array(); + + // Create grants for each translation of the node. + foreach ($node->getTranslationLanguages() as $langcode => $language) { + if ($node->field_private[$langcode][0]['value']) { + $grants[] = array( + 'realm' => 'node_access_language_test', + 'gid' => 7888, + 'grant_view' => 1, + 'grant_update' => 0, + 'grant_delete' => 0, + 'priority' => 0, + 'langcode' => $langcode, + ); + $grants[] = array( + 'realm' => 'node_access_language_test', + 'gid' => 7889, + 'grant_view' => 1, + 'grant_update' => 0, + 'grant_delete' => 0, + 'priority' => 0, + 'langcode' => $langcode, + ); + // For the author realm, the GID is equivalent to a UID, which + // means there are many many groups of just 1 user. + $grants[] = array( + 'realm' => 'node_access_test_language_author', + 'gid' => $node->uid, + 'grant_view' => 1, + 'grant_update' => 1, + 'grant_delete' => 1, + 'priority' => 0, + 'langcode' => $langcode, + ); + } else { + $grants[] = array('realm' => 'all', 'gid' => 0, 'grant_view' => 1, 'grant_update' => 0, 'grant_delete' => 0, 'langcode' => $langcode); + } + } + return $grants; +} + +/** + * Implements hook_permission(). + * + * Sets up permissions for this module. + */ +function node_access_test_language_permission() { + return array('node access language test view' => array('title' => 'View content')); +} + +/** + * Implements hook_disable(). + */ +function node_access_test_language_enable() { + $field_private = array( + 'field_name' => 'field_private', + 'type' => 'list_boolean', + 'cardinality' => 1, + 'translatable' => TRUE, + 'settings' => array( + 'allowed_values' => array(0 => 'Not private', 1 => 'Private'), + ), + ); + $field_private = field_create_field($field_private); + + $instance = array( + 'field_name' => $field_private['field_name'], + 'entity_type' => 'node', + 'bundle' => 'page', + 'widget' => array( + 'type' => 'options_buttons', + ), + ); + $instance = field_create_instance($instance); +} + +/** + * Implements hook_disable(). + */ +function node_access_test_language_disable() { + field_delete_instance(field_read_instance('node', 'field_private', 'page')); +}