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