Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace Drupal\Tests\node\Functional;
|
Chris@0
|
4
|
Chris@18
|
5 use Drupal\Core\Database\Database;
|
Chris@0
|
6 use Drupal\Core\Language\LanguageInterface;
|
Chris@0
|
7 use Drupal\field\Entity\FieldConfig;
|
Chris@0
|
8 use Drupal\language\Entity\ConfigurableLanguage;
|
Chris@0
|
9 use Drupal\node\Entity\NodeType;
|
Chris@0
|
10 use Drupal\user\Entity\User;
|
Chris@0
|
11 use Drupal\field\Entity\FieldStorageConfig;
|
Chris@0
|
12
|
Chris@0
|
13 /**
|
Chris@0
|
14 * Tests node access functionality with multiple languages and two node access
|
Chris@0
|
15 * modules.
|
Chris@0
|
16 *
|
Chris@0
|
17 * @group node
|
Chris@0
|
18 */
|
Chris@0
|
19 class NodeAccessLanguageAwareCombinationTest extends NodeTestBase {
|
Chris@0
|
20
|
Chris@0
|
21 /**
|
Chris@0
|
22 * Enable language and two node access modules.
|
Chris@0
|
23 *
|
Chris@0
|
24 * @var array
|
Chris@0
|
25 */
|
Chris@0
|
26 public static $modules = ['language', 'node_access_test_language', 'node_access_test'];
|
Chris@0
|
27
|
Chris@0
|
28 /**
|
Chris@0
|
29 * A set of nodes to use in testing.
|
Chris@0
|
30 *
|
Chris@0
|
31 * @var \Drupal\node\NodeInterface[]
|
Chris@0
|
32 */
|
Chris@0
|
33 protected $nodes = [];
|
Chris@0
|
34
|
Chris@0
|
35 /**
|
Chris@0
|
36 * A normal authenticated user.
|
Chris@0
|
37 *
|
Chris@17
|
38 * @var \Drupal\user\UserInterface
|
Chris@0
|
39 */
|
Chris@0
|
40 protected $webUser;
|
Chris@0
|
41
|
Chris@0
|
42 /**
|
Chris@0
|
43 * User 1.
|
Chris@0
|
44 *
|
Chris@17
|
45 * @var \Drupal\user\UserInterface
|
Chris@0
|
46 */
|
Chris@0
|
47 protected $adminUser;
|
Chris@0
|
48
|
Chris@0
|
49 protected function setUp() {
|
Chris@0
|
50 parent::setUp();
|
Chris@0
|
51
|
Chris@0
|
52 node_access_test_add_field(NodeType::load('page'));
|
Chris@0
|
53
|
Chris@0
|
54 // Create the 'private' field, which allows the node to be marked as private
|
Chris@0
|
55 // (restricted access) in a given translation.
|
Chris@0
|
56 $field_storage = FieldStorageConfig::create([
|
Chris@0
|
57 'field_name' => 'field_private',
|
Chris@0
|
58 'entity_type' => 'node',
|
Chris@0
|
59 'type' => 'boolean',
|
Chris@0
|
60 'cardinality' => 1,
|
Chris@0
|
61 ]);
|
Chris@0
|
62 $field_storage->save();
|
Chris@0
|
63
|
Chris@0
|
64 FieldConfig::create([
|
Chris@0
|
65 'field_storage' => $field_storage,
|
Chris@0
|
66 'bundle' => 'page',
|
Chris@0
|
67 'widget' => [
|
Chris@0
|
68 'type' => 'options_buttons',
|
Chris@0
|
69 ],
|
Chris@0
|
70 'settings' => [
|
Chris@0
|
71 'on_label' => 'Private',
|
Chris@0
|
72 'off_label' => 'Not private',
|
Chris@0
|
73 ],
|
Chris@0
|
74 ])->save();
|
Chris@0
|
75
|
Chris@0
|
76 // After enabling a node access module, the access table has to be rebuild.
|
Chris@0
|
77 node_access_rebuild();
|
Chris@0
|
78
|
Chris@0
|
79 // Add Hungarian and Catalan.
|
Chris@0
|
80 ConfigurableLanguage::createFromLangcode('hu')->save();
|
Chris@0
|
81 ConfigurableLanguage::createFromLangcode('ca')->save();
|
Chris@0
|
82
|
Chris@0
|
83 // Create a normal authenticated user.
|
Chris@0
|
84 $this->webUser = $this->drupalCreateUser(['access content']);
|
Chris@0
|
85
|
Chris@0
|
86 // Load the user 1 user for later use as an admin user with permission to
|
Chris@0
|
87 // see everything.
|
Chris@0
|
88 $this->adminUser = User::load(1);
|
Chris@0
|
89
|
Chris@0
|
90 // The node_access_test_language module allows individual translations of a
|
Chris@0
|
91 // node to be marked private (not viewable by normal users), and the
|
Chris@0
|
92 // node_access_test module allows whole nodes to be marked private. (In a
|
Chris@0
|
93 // real-world implementation, hook_node_access_records_alter() might be
|
Chris@0
|
94 // implemented by one or both modules to enforce that private nodes or
|
Chris@0
|
95 // translations are always private, but we want to test the default,
|
Chris@0
|
96 // additive behavior of node access).
|
Chris@0
|
97
|
Chris@0
|
98 // Create six Hungarian nodes with Catalan translations:
|
Chris@0
|
99 // 1. One public with neither language marked as private.
|
Chris@0
|
100 // 2. One private with neither language marked as private.
|
Chris@0
|
101 // 3. One public with only the Hungarian translation private.
|
Chris@0
|
102 // 4. One public with only the Catalan translation private.
|
Chris@0
|
103 // 5. One public with both the Hungarian and Catalan translations private.
|
Chris@0
|
104 // 6. One private with both the Hungarian and Catalan translations private.
|
Chris@0
|
105 $this->nodes['public_both_public'] = $node = $this->drupalCreateNode([
|
Chris@0
|
106 'body' => [[]],
|
Chris@0
|
107 'langcode' => 'hu',
|
Chris@0
|
108 'field_private' => [['value' => 0]],
|
Chris@0
|
109 'private' => FALSE,
|
Chris@0
|
110 ]);
|
Chris@0
|
111 $translation = $node->addTranslation('ca');
|
Chris@0
|
112 $translation->title->value = $this->randomString();
|
Chris@0
|
113 $translation->field_private->value = 0;
|
Chris@0
|
114 $node->save();
|
Chris@0
|
115
|
Chris@0
|
116 $this->nodes['private_both_public'] = $node = $this->drupalCreateNode([
|
Chris@0
|
117 'body' => [[]],
|
Chris@0
|
118 'langcode' => 'hu',
|
Chris@0
|
119 'field_private' => [['value' => 0]],
|
Chris@0
|
120 'private' => TRUE,
|
Chris@0
|
121 ]);
|
Chris@0
|
122 $translation = $node->addTranslation('ca');
|
Chris@0
|
123 $translation->title->value = $this->randomString();
|
Chris@0
|
124 $translation->field_private->value = 0;
|
Chris@0
|
125 $node->save();
|
Chris@0
|
126
|
Chris@0
|
127 $this->nodes['public_hu_private'] = $node = $this->drupalCreateNode([
|
Chris@0
|
128 'body' => [[]],
|
Chris@0
|
129 'langcode' => 'hu',
|
Chris@0
|
130 'field_private' => [['value' => 1]],
|
Chris@0
|
131 'private' => FALSE,
|
Chris@0
|
132 ]);
|
Chris@0
|
133 $translation = $node->addTranslation('ca');
|
Chris@0
|
134 $translation->title->value = $this->randomString();
|
Chris@0
|
135 $translation->field_private->value = 0;
|
Chris@0
|
136 $node->save();
|
Chris@0
|
137
|
Chris@0
|
138 $this->nodes['public_ca_private'] = $node = $this->drupalCreateNode([
|
Chris@0
|
139 'body' => [[]],
|
Chris@0
|
140 'langcode' => 'hu',
|
Chris@0
|
141 'field_private' => [['value' => 0]],
|
Chris@0
|
142 'private' => FALSE,
|
Chris@0
|
143 ]);
|
Chris@0
|
144 $translation = $node->addTranslation('ca');
|
Chris@0
|
145 $translation->title->value = $this->randomString();
|
Chris@0
|
146 $translation->field_private->value = 1;
|
Chris@0
|
147 $node->save();
|
Chris@0
|
148
|
Chris@0
|
149 $this->nodes['public_both_private'] = $node = $this->drupalCreateNode([
|
Chris@0
|
150 'body' => [[]],
|
Chris@0
|
151 'langcode' => 'hu',
|
Chris@0
|
152 'field_private' => [['value' => 1]],
|
Chris@0
|
153 'private' => FALSE,
|
Chris@0
|
154 ]);
|
Chris@0
|
155 $translation = $node->addTranslation('ca');
|
Chris@0
|
156 $translation->title->value = $this->randomString();
|
Chris@0
|
157 $translation->field_private->value = 1;
|
Chris@0
|
158 $node->save();
|
Chris@0
|
159
|
Chris@0
|
160 $this->nodes['private_both_private'] = $node = $this->drupalCreateNode([
|
Chris@0
|
161 'body' => [[]],
|
Chris@0
|
162 'langcode' => 'hu',
|
Chris@0
|
163 'field_private' => [['value' => 1]],
|
Chris@0
|
164 'private' => TRUE,
|
Chris@0
|
165 ]);
|
Chris@0
|
166 $translation = $node->addTranslation('ca');
|
Chris@0
|
167 $translation->title->value = $this->randomString();
|
Chris@0
|
168 $translation->field_private->value = 1;
|
Chris@0
|
169 $node->save();
|
Chris@0
|
170
|
Chris@0
|
171 $this->nodes['public_no_language_private'] = $this->drupalCreateNode([
|
Chris@0
|
172 'field_private' => [['value' => 1]],
|
Chris@0
|
173 'private' => FALSE,
|
Chris@0
|
174 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
Chris@0
|
175 ]);
|
Chris@0
|
176 $this->nodes['public_no_language_public'] = $this->drupalCreateNode([
|
Chris@0
|
177 'field_private' => [['value' => 0]],
|
Chris@0
|
178 'private' => FALSE,
|
Chris@0
|
179 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
Chris@0
|
180 ]);
|
Chris@0
|
181 $this->nodes['private_no_language_private'] = $this->drupalCreateNode([
|
Chris@0
|
182 'field_private' => [['value' => 1]],
|
Chris@0
|
183 'private' => TRUE,
|
Chris@0
|
184 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
Chris@0
|
185 ]);
|
Chris@0
|
186 $this->nodes['private_no_language_public'] = $this->drupalCreateNode([
|
Chris@0
|
187 'field_private' => [['value' => 1]],
|
Chris@0
|
188 'private' => TRUE,
|
Chris@0
|
189 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
Chris@0
|
190 ]);
|
Chris@0
|
191 }
|
Chris@0
|
192
|
Chris@0
|
193 /**
|
Chris@0
|
194 * Tests node access and node access queries with multiple node languages.
|
Chris@0
|
195 */
|
Chris@0
|
196 public function testNodeAccessLanguageAwareCombination() {
|
Chris@0
|
197
|
Chris@0
|
198 $expected_node_access = ['view' => TRUE, 'update' => FALSE, 'delete' => FALSE];
|
Chris@0
|
199 $expected_node_access_no_access = ['view' => FALSE, 'update' => FALSE, 'delete' => FALSE];
|
Chris@0
|
200
|
Chris@0
|
201 // When the node and both translations are public, access should always be
|
Chris@0
|
202 // granted.
|
Chris@0
|
203 $this->assertNodeAccess($expected_node_access, $this->nodes['public_both_public'], $this->webUser);
|
Chris@0
|
204 $this->assertNodeAccess($expected_node_access, $this->nodes['public_both_public']->getTranslation('hu'), $this->webUser);
|
Chris@0
|
205 $this->assertNodeAccess($expected_node_access, $this->nodes['public_both_public']->getTranslation('ca'), $this->webUser);
|
Chris@0
|
206
|
Chris@0
|
207 // If the node is marked private but both existing translations are not,
|
Chris@0
|
208 // access should still be granted, because the grants are additive.
|
Chris@0
|
209 $this->assertNodeAccess($expected_node_access, $this->nodes['private_both_public'], $this->webUser);
|
Chris@0
|
210 $this->assertNodeAccess($expected_node_access, $this->nodes['private_both_public']->getTranslation('hu'), $this->webUser);
|
Chris@0
|
211 $this->assertNodeAccess($expected_node_access, $this->nodes['private_both_public']->getTranslation('ca'), $this->webUser);
|
Chris@0
|
212
|
Chris@0
|
213 // If the node is marked private, but a existing translation is public,
|
Chris@0
|
214 // access should only be granted for the public translation. With the
|
Chris@0
|
215 // Hungarian translation marked as private, but the Catalan translation
|
Chris@0
|
216 // public, the access is granted.
|
Chris@0
|
217 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['public_hu_private'], $this->webUser);
|
Chris@0
|
218 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['public_hu_private']->getTranslation('hu'), $this->webUser);
|
Chris@0
|
219 $this->assertNodeAccess($expected_node_access, $this->nodes['public_hu_private']->getTranslation('ca'), $this->webUser);
|
Chris@0
|
220
|
Chris@0
|
221 // With the Catalan translation marked as private, but the node public,
|
Chris@0
|
222 // access is granted for the existing Hungarian translation, but not for the
|
Chris@0
|
223 // Catalan.
|
Chris@0
|
224 $this->assertNodeAccess($expected_node_access, $this->nodes['public_ca_private'], $this->webUser);
|
Chris@0
|
225 $this->assertNodeAccess($expected_node_access, $this->nodes['public_ca_private']->getTranslation('hu'), $this->webUser);
|
Chris@0
|
226 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['public_ca_private']->getTranslation('ca'), $this->webUser);
|
Chris@0
|
227
|
Chris@0
|
228 // With both translations marked as private, but the node public, access
|
Chris@0
|
229 // should be denied in all cases.
|
Chris@0
|
230 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['public_both_private'], $this->webUser);
|
Chris@0
|
231 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['public_both_private']->getTranslation('hu'), $this->webUser);
|
Chris@0
|
232 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['public_both_private']->getTranslation('ca'), $this->webUser);
|
Chris@0
|
233
|
Chris@0
|
234 // If the node and both its existing translations are private, access should
|
Chris@0
|
235 // be denied in all cases.
|
Chris@0
|
236 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['private_both_private'], $this->webUser);
|
Chris@0
|
237 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['private_both_private']->getTranslation('hu'), $this->webUser);
|
Chris@0
|
238 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['private_both_private']->getTranslation('ca'), $this->webUser);
|
Chris@0
|
239
|
Chris@0
|
240 // No access for all languages as the language aware node access module
|
Chris@0
|
241 // denies access.
|
Chris@0
|
242 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['public_no_language_private'], $this->webUser);
|
Chris@0
|
243
|
Chris@0
|
244 // Access only for request with no language defined.
|
Chris@0
|
245 $this->assertNodeAccess($expected_node_access, $this->nodes['public_no_language_public'], $this->webUser);
|
Chris@0
|
246
|
Chris@0
|
247 // No access for all languages as both node access modules deny access.
|
Chris@0
|
248 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['private_no_language_private'], $this->webUser);
|
Chris@0
|
249
|
Chris@0
|
250 // No access for all languages as the non language aware node access module
|
Chris@0
|
251 // denies access.
|
Chris@0
|
252 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['private_no_language_public'], $this->webUser);
|
Chris@0
|
253
|
Chris@0
|
254 // Query the node table with the node access tag in several languages.
|
Chris@18
|
255 $connection = Database::getConnection();
|
Chris@0
|
256 // Query with no language specified. The fallback (hu or und) will be used.
|
Chris@18
|
257 $select = $connection->select('node', 'n')
|
Chris@0
|
258 ->fields('n', ['nid'])
|
Chris@0
|
259 ->addMetaData('account', $this->webUser)
|
Chris@0
|
260 ->addTag('node_access');
|
Chris@0
|
261 $nids = $select->execute()->fetchAllAssoc('nid');
|
Chris@0
|
262
|
Chris@0
|
263 // Four nodes should be returned with public Hungarian translations or the
|
Chris@0
|
264 // no language public node.
|
Chris@0
|
265 $this->assertEqual(count($nids), 4, 'db_select() returns 4 nodes when no langcode is specified.');
|
Chris@0
|
266 $this->assertTrue(array_key_exists($this->nodes['public_both_public']->id(), $nids), 'Returned node ID is full public node.');
|
Chris@0
|
267 $this->assertTrue(array_key_exists($this->nodes['public_ca_private']->id(), $nids), 'Returned node ID is Hungarian public only node.');
|
Chris@0
|
268 $this->assertTrue(array_key_exists($this->nodes['private_both_public']->id(), $nids), 'Returned node ID is both public non-language-aware private only node.');
|
Chris@0
|
269 $this->assertTrue(array_key_exists($this->nodes['public_no_language_public']->id(), $nids), 'Returned node ID is no language public node.');
|
Chris@0
|
270
|
Chris@0
|
271 // Query with Hungarian (hu) specified.
|
Chris@18
|
272 $select = $connection->select('node', 'n')
|
Chris@0
|
273 ->fields('n', ['nid'])
|
Chris@0
|
274 ->addMetaData('account', $this->webUser)
|
Chris@0
|
275 ->addMetaData('langcode', 'hu')
|
Chris@0
|
276 ->addTag('node_access');
|
Chris@0
|
277 $nids = $select->execute()->fetchAllAssoc('nid');
|
Chris@0
|
278
|
Chris@0
|
279 // Three nodes should be returned (with public Hungarian translations).
|
Chris@0
|
280 $this->assertEqual(count($nids), 3, 'db_select() returns 3 nodes.');
|
Chris@0
|
281 $this->assertTrue(array_key_exists($this->nodes['public_both_public']->id(), $nids), 'Returned node ID is both public node.');
|
Chris@0
|
282 $this->assertTrue(array_key_exists($this->nodes['public_ca_private']->id(), $nids), 'Returned node ID is Hungarian public only node.');
|
Chris@0
|
283 $this->assertTrue(array_key_exists($this->nodes['private_both_public']->id(), $nids), 'Returned node ID is both public non-language-aware private only node.');
|
Chris@0
|
284
|
Chris@0
|
285 // Query with Catalan (ca) specified.
|
Chris@18
|
286 $select = $connection->select('node', 'n')
|
Chris@0
|
287 ->fields('n', ['nid'])
|
Chris@0
|
288 ->addMetaData('account', $this->webUser)
|
Chris@0
|
289 ->addMetaData('langcode', 'ca')
|
Chris@0
|
290 ->addTag('node_access');
|
Chris@0
|
291 $nids = $select->execute()->fetchAllAssoc('nid');
|
Chris@0
|
292
|
Chris@0
|
293 // Three nodes should be returned (with public Catalan translations).
|
Chris@0
|
294 $this->assertEqual(count($nids), 3, 'db_select() returns 3 nodes.');
|
Chris@0
|
295 $this->assertTrue(array_key_exists($this->nodes['public_both_public']->id(), $nids), 'Returned node ID is both public node.');
|
Chris@0
|
296 $this->assertTrue(array_key_exists($this->nodes['public_hu_private']->id(), $nids), 'Returned node ID is Catalan public only node.');
|
Chris@0
|
297 $this->assertTrue(array_key_exists($this->nodes['private_both_public']->id(), $nids), 'Returned node ID is both public non-language-aware private only node.');
|
Chris@0
|
298
|
Chris@0
|
299 // Query with German (de) specified.
|
Chris@18
|
300 $select = $connection->select('node', 'n')
|
Chris@0
|
301 ->fields('n', ['nid'])
|
Chris@0
|
302 ->addMetaData('account', $this->webUser)
|
Chris@0
|
303 ->addMetaData('langcode', 'de')
|
Chris@0
|
304 ->addTag('node_access');
|
Chris@0
|
305 $nids = $select->execute()->fetchAllAssoc('nid');
|
Chris@0
|
306
|
Chris@0
|
307 // There are no nodes with German translations, so no results are returned.
|
Chris@0
|
308 $this->assertTrue(empty($nids), 'db_select() returns an empty result.');
|
Chris@0
|
309
|
Chris@0
|
310 // Query the nodes table as admin user (full access) with the node access
|
Chris@0
|
311 // tag and no specific langcode.
|
Chris@18
|
312 $select = $connection->select('node', 'n')
|
Chris@0
|
313 ->fields('n', ['nid'])
|
Chris@0
|
314 ->addMetaData('account', $this->adminUser)
|
Chris@0
|
315 ->addTag('node_access');
|
Chris@0
|
316 $nids = $select->execute()->fetchAllAssoc('nid');
|
Chris@0
|
317
|
Chris@0
|
318 // All nodes are returned.
|
Chris@0
|
319 $this->assertEqual(count($nids), 10, 'db_select() returns all nodes.');
|
Chris@0
|
320
|
Chris@0
|
321 // Query the nodes table as admin user (full access) with the node access
|
Chris@0
|
322 // tag and langcode de.
|
Chris@18
|
323 $select = $connection->select('node', 'n')
|
Chris@0
|
324 ->fields('n', ['nid'])
|
Chris@0
|
325 ->addMetaData('account', $this->adminUser)
|
Chris@0
|
326 ->addMetaData('langcode', 'de')
|
Chris@0
|
327 ->addTag('node_access');
|
Chris@0
|
328 $nids = $select->execute()->fetchAllAssoc('nid');
|
Chris@0
|
329
|
Chris@0
|
330 // Even though there is no German translation, all nodes are returned
|
Chris@0
|
331 // because node access filtering does not occur when the user is user 1.
|
Chris@0
|
332 $this->assertEqual(count($nids), 10, 'db_select() returns all nodes.');
|
Chris@0
|
333 }
|
Chris@0
|
334
|
Chris@0
|
335 }
|