1<?php 2 3namespace Drupal\Tests\node\Kernel; 4 5use Drupal\Core\Database\Database; 6use Drupal\Core\Language\LanguageInterface; 7use Drupal\field\Entity\FieldConfig; 8use Drupal\language\Entity\ConfigurableLanguage; 9use Drupal\user\Entity\User; 10use Drupal\field\Entity\FieldStorageConfig; 11 12/** 13 * Tests node_access and select queries with node_access tag functionality with 14 * multiple languages with node_access_test_language which is language-aware. 15 * 16 * @group node 17 */ 18class NodeAccessLanguageAwareTest extends NodeAccessTestBase { 19 20 /** 21 * Enable language and a language-aware node access module. 22 * 23 * @var array 24 */ 25 public static $modules = ['language', 'node_access_test_language']; 26 27 /** 28 * A set of nodes to use in testing. 29 * 30 * @var \Drupal\node\NodeInterface[] 31 */ 32 protected $nodes = []; 33 34 /** 35 * A user with permission to bypass access content. 36 * 37 * @var \Drupal\user\UserInterface 38 */ 39 protected $adminUser; 40 41 /** 42 * A normal authenticated user. 43 * 44 * @var \Drupal\user\UserInterface 45 */ 46 protected $webUser; 47 48 protected function setUp() { 49 parent::setUp(); 50 51 // Create the 'private' field, which allows the node to be marked as private 52 // (restricted access) in a given translation. 53 $field_storage = FieldStorageConfig::create([ 54 'field_name' => 'field_private', 55 'entity_type' => 'node', 56 'type' => 'boolean', 57 'cardinality' => 1, 58 ]); 59 $field_storage->save(); 60 61 FieldConfig::create([ 62 'field_storage' => $field_storage, 63 'bundle' => 'page', 64 'widget' => [ 65 'type' => 'options_buttons', 66 ], 67 'settings' => [ 68 'on_label' => 'Private', 69 'off_label' => 'Not private', 70 ], 71 ])->save(); 72 73 // After enabling a node access module, the access table has to be rebuild. 74 node_access_rebuild(); 75 76 // Create a normal authenticated user. 77 $this->webUser = $this->drupalCreateUser(['access content']); 78 79 // Load the user 1 user for later use as an admin user with permission to 80 // see everything. 81 $this->adminUser = User::load(1); 82 83 // Add Hungarian and Catalan. 84 ConfigurableLanguage::createFromLangcode('hu')->save(); 85 ConfigurableLanguage::createFromLangcode('ca')->save(); 86 87 // The node_access_test_language module allows individual translations of a 88 // node to be marked private (not viewable by normal users). 89 90 // Create six nodes: 91 // 1. Four Hungarian nodes with Catalan translations 92 // - One with neither language marked as private. 93 // - One with only the Hungarian translation private. 94 // - One with only the Catalan translation private. 95 // - One with both the Hungarian and Catalan translations private. 96 // 2. Two nodes with no language specified. 97 // - One public. 98 // - One private. 99 $this->nodes['both_public'] = $node = $this->drupalCreateNode([ 100 'body' => [[]], 101 'langcode' => 'hu', 102 'field_private' => [['value' => 0]], 103 ]); 104 $translation = $node->addTranslation('ca'); 105 $translation->title->value = $this->randomString(); 106 $translation->field_private->value = 0; 107 $node->save(); 108 109 $this->nodes['ca_private'] = $node = $this->drupalCreateNode([ 110 'body' => [[]], 111 'langcode' => 'hu', 112 'field_private' => [['value' => 0]], 113 ]); 114 $translation = $node->addTranslation('ca'); 115 $translation->title->value = $this->randomString(); 116 $translation->field_private->value = 1; 117 $node->save(); 118 119 $this->nodes['hu_private'] = $node = $this->drupalCreateNode([ 120 'body' => [[]], 121 'langcode' => 'hu', 122 'field_private' => [['value' => 1]], 123 ]); 124 $translation = $node->addTranslation('ca'); 125 $translation->title->value = $this->randomString(); 126 $translation->field_private->value = 0; 127 $node->save(); 128 129 $this->nodes['both_private'] = $node = $this->drupalCreateNode([ 130 'body' => [[]], 131 'langcode' => 'hu', 132 'field_private' => [['value' => 1]], 133 ]); 134 $translation = $node->addTranslation('ca'); 135 $translation->title->value = $this->randomString(); 136 $translation->field_private->value = 1; 137 $node->save(); 138 139 $this->nodes['no_language_public'] = $this->drupalCreateNode([ 140 'field_private' => [['value' => 0]], 141 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, 142 ]); 143 $this->nodes['no_language_private'] = $this->drupalCreateNode([ 144 'field_private' => [['value' => 1]], 145 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, 146 ]); 147 } 148 149 /** 150 * Tests node access and node access queries with multiple node languages. 151 */ 152 public function testNodeAccessLanguageAware() { 153 // The node_access_test_language module only grants view access. 154 $expected_node_access = ['view' => TRUE, 'update' => FALSE, 'delete' => FALSE]; 155 $expected_node_access_no_access = ['view' => FALSE, 'update' => FALSE, 'delete' => FALSE]; 156 157 // When both Hungarian and Catalan are marked as public, access to the 158 // Hungarian translation should be granted with the default entity object or 159 // when the Hungarian translation is specified explicitly. 160 $this->assertNodeAccess($expected_node_access, $this->nodes['both_public'], $this->webUser); 161 $this->assertNodeAccess($expected_node_access, $this->nodes['both_public']->getTranslation('hu'), $this->webUser); 162 // Access to the Catalan translation should also be granted. 163 $this->assertNodeAccess($expected_node_access, $this->nodes['both_public']->getTranslation('ca'), $this->webUser); 164 165 // When Hungarian is marked as private, access to the Hungarian translation 166 // should be denied with the default entity object or when the Hungarian 167 // translation is specified explicitly. 168 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['hu_private'], $this->webUser); 169 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['hu_private']->getTranslation('hu'), $this->webUser); 170 // Access to the Catalan translation should be granted. 171 $this->assertNodeAccess($expected_node_access, $this->nodes['hu_private']->getTranslation('ca'), $this->webUser); 172 173 // When Catalan is marked as private, access to the Hungarian translation 174 // should be granted with the default entity object or when the Hungarian 175 // translation is specified explicitly. 176 $this->assertNodeAccess($expected_node_access, $this->nodes['ca_private'], $this->webUser); 177 $this->assertNodeAccess($expected_node_access, $this->nodes['ca_private']->getTranslation('hu'), $this->webUser); 178 // Access to the Catalan translation should be granted. 179 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['ca_private']->getTranslation('ca'), $this->webUser); 180 181 // When both translations are marked as private, access should be denied 182 // regardless of the entity object specified. 183 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['both_private'], $this->webUser); 184 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['both_private']->getTranslation('hu'), $this->webUser); 185 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['both_private']->getTranslation('ca'), $this->webUser); 186 187 // When no language is specified for a private node, access to every node 188 // translation is denied. 189 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['no_language_private'], $this->webUser); 190 191 // When no language is specified for a public node, access should be 192 // granted. 193 $this->assertNodeAccess($expected_node_access, $this->nodes['no_language_public'], $this->webUser); 194 195 // Query the node table with the node access tag in several languages. 196 $connection = Database::getConnection(); 197 // Query with no language specified. The fallback (hu) will be used. 198 $select = $connection->select('node', 'n') 199 ->fields('n', ['nid']) 200 ->addMetaData('account', $this->webUser) 201 ->addTag('node_access'); 202 $nids = $select->execute()->fetchAllAssoc('nid'); 203 204 // Three nodes should be returned: 205 // - Node with both translations public. 206 // - Node with only the Catalan translation marked as private. 207 // - No language node marked as public. 208 $this->assertCount(3, $nids, 'db_select() returns 3 nodes when no langcode is specified.'); 209 $this->assertArrayHasKey($this->nodes['both_public']->id(), $nids); 210 $this->assertArrayHasKey($this->nodes['ca_private']->id(), $nids); 211 $this->assertArrayHasKey($this->nodes['no_language_public']->id(), $nids); 212 213 // Query with Hungarian (hu) specified. 214 $select = $connection->select('node', 'n') 215 ->fields('n', ['nid']) 216 ->addMetaData('account', $this->webUser) 217 ->addMetaData('langcode', 'hu') 218 ->addTag('node_access'); 219 $nids = $select->execute()->fetchAllAssoc('nid'); 220 221 // Two nodes should be returned: the node with both translations public, and 222 // the node with only the Catalan translation marked as private. 223 $this->assertCount(2, $nids, 'Query returns 2 nodes when the hu langcode is specified.'); 224 $this->assertArrayHasKey($this->nodes['both_public']->id(), $nids); 225 $this->assertArrayHasKey($this->nodes['ca_private']->id(), $nids); 226 227 // Query with Catalan (ca) specified. 228 $select = $connection->select('node', 'n') 229 ->fields('n', ['nid']) 230 ->addMetaData('account', $this->webUser) 231 ->addMetaData('langcode', 'ca') 232 ->addTag('node_access'); 233 $nids = $select->execute()->fetchAllAssoc('nid'); 234 235 // Two nodes should be returned: the node with both translations public, and 236 // the node with only the Hungarian translation marked as private. 237 $this->assertCount(2, $nids, 'Query returns 2 nodes when the hu langcode is specified.'); 238 $this->assertArrayHasKey($this->nodes['both_public']->id(), $nids); 239 $this->assertArrayHasKey($this->nodes['hu_private']->id(), $nids); 240 241 // Query with German (de) specified. 242 $select = $connection->select('node', 'n') 243 ->fields('n', ['nid']) 244 ->addMetaData('account', $this->webUser) 245 ->addMetaData('langcode', 'de') 246 ->addTag('node_access'); 247 $nids = $select->execute()->fetchAllAssoc('nid'); 248 249 // There are no nodes with German translations, so no results are returned. 250 $this->assertTrue(empty($nids), 'Query returns an empty result when the de langcode is specified.'); 251 252 // Query the nodes table as admin user (full access) with the node access 253 // tag and no specific langcode. 254 $select = $connection->select('node', 'n') 255 ->fields('n', ['nid']) 256 ->addMetaData('account', $this->adminUser) 257 ->addTag('node_access'); 258 $nids = $select->execute()->fetchAllAssoc('nid'); 259 260 // All nodes are returned. 261 $this->assertCount(6, $nids, 'Query returns all nodes.'); 262 263 // Query the nodes table as admin user (full access) with the node access 264 // tag and langcode de. 265 $select = $connection->select('node', 'n') 266 ->fields('n', ['nid']) 267 ->addMetaData('account', $this->adminUser) 268 ->addMetaData('langcode', 'de') 269 ->addTag('node_access'); 270 $nids = $select->execute()->fetchAllAssoc('nid'); 271 272 // Even though there is no German translation, all nodes are returned 273 // because node access filtering does not occur when the user is user 1. 274 $this->assertCount(6, $nids, 'Query returns all nodes.'); 275 } 276 277} 278