Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / web / core / modules / content_moderation / tests / src / Functional / ModerationLocaleTest.php
1 <?php
2
3 namespace Drupal\Tests\content_moderation\Functional;
4
5 use Drupal\node\NodeInterface;
6
7 /**
8  * Test content_moderation functionality with localization and translation.
9  *
10  * @group content_moderation
11  */
12 class ModerationLocaleTest extends ModerationStateTestBase {
13
14   /**
15    * Modules to enable.
16    *
17    * @var array
18    */
19   public static $modules = [
20     'node',
21     'content_moderation',
22     'locale',
23     'content_translation',
24   ];
25
26   /**
27    * {@inheritdoc}
28    */
29   protected function setUp() {
30     parent::setUp();
31
32     $this->drupalLogin($this->rootUser);
33
34     // Enable moderation on Article node type.
35     $this->createContentTypeFromUi('Article', 'article', TRUE);
36
37     // Add French and Italian languages.
38     foreach (['fr', 'it'] as $langcode) {
39       $edit = [
40         'predefined_langcode' => $langcode,
41       ];
42       $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
43     }
44
45     // Enable content translation on articles.
46     $this->drupalGet('admin/config/regional/content-language');
47     $edit = [
48       'entity_types[node]' => TRUE,
49       'settings[node][article][translatable]' => TRUE,
50       'settings[node][article][settings][language][language_alterable]' => TRUE,
51     ];
52     $this->drupalPostForm(NULL, $edit, t('Save configuration'));
53
54     // Adding languages requires a container rebuild in the test running
55     // environment so that multilingual services are used.
56     $this->rebuildContainer();
57   }
58
59   /**
60    * Tests article translations can be moderated separately.
61    */
62   public function testTranslateModeratedContent() {
63     // Create a published article in English.
64     $edit = [
65       'title[0][value]' => 'Published English node',
66       'langcode[0][value]' => 'en',
67       'moderation_state[0][state]' => 'published',
68     ];
69     $this->drupalPostForm('node/add/article', $edit, t('Save'));
70     $this->assertText(t('Article Published English node has been created.'));
71     $english_node = $this->drupalGetNodeByTitle('Published English node');
72
73     // Add a French translation.
74     $this->drupalGet('node/' . $english_node->id() . '/translations');
75     $this->clickLink(t('Add'));
76     $edit = [
77       'title[0][value]' => 'French node Draft',
78       'moderation_state[0][state]' => 'draft',
79     ];
80     $this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
81     // Here the error has occurred "The website encountered an unexpected error.
82     // Please try again later."
83     // If the translation has got lost.
84     $this->assertText(t('Article French node Draft has been updated.'));
85
86     // Create an article in English.
87     $edit = [
88       'title[0][value]' => 'English node',
89       'langcode[0][value]' => 'en',
90       'moderation_state[0][state]' => 'draft',
91     ];
92     $this->drupalPostForm('node/add/article', $edit, t('Save'));
93     $this->assertText(t('Article English node has been created.'));
94     $english_node = $this->drupalGetNodeByTitle('English node');
95
96     // Add a French translation.
97     $this->drupalGet('node/' . $english_node->id() . '/translations');
98     $this->clickLink(t('Add'));
99     $edit = [
100       'title[0][value]' => 'French node',
101       'moderation_state[0][state]' => 'draft',
102     ];
103     $this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
104     $this->assertText(t('Article French node has been updated.'));
105     $english_node = $this->drupalGetNodeByTitle('English node', TRUE);
106
107     // Publish the English article and check that the translation stays
108     // unpublished.
109     $this->drupalPostForm('node/' . $english_node->id() . '/edit', [
110       'moderation_state[0][state]' => 'published',
111     ], t('Save (this translation)'));
112     $this->assertText(t('Article English node has been updated.'));
113     $english_node = $this->drupalGetNodeByTitle('English node', TRUE);
114     $french_node = $english_node->getTranslation('fr');
115     $this->assertEqual('French node', $french_node->label());
116
117     $this->assertEqual($english_node->moderation_state->value, 'published');
118     $this->assertTrue($english_node->isPublished());
119     $this->assertEqual($french_node->moderation_state->value, 'draft');
120     $this->assertFalse($french_node->isPublished());
121
122     // Create another article with its translation. This time we will publish
123     // the translation first.
124     $edit = [
125       'title[0][value]' => 'Another node',
126       'moderation_state[0][state]' => 'draft',
127     ];
128     $this->drupalPostForm('node/add/article', $edit, t('Save'));
129     $this->assertText(t('Article Another node has been created.'));
130     $english_node = $this->drupalGetNodeByTitle('Another node');
131
132     // Add a French translation.
133     $this->drupalGet('node/' . $english_node->id() . '/translations');
134     $this->clickLink(t('Add'));
135     $edit = [
136       'title[0][value]' => 'Translated node',
137       'moderation_state[0][state]' => 'draft',
138     ];
139     $this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
140     $this->assertText(t('Article Translated node has been updated.'));
141     $english_node = $this->drupalGetNodeByTitle('Another node', TRUE);
142
143     // Publish the translation and check that the source language version stays
144     // unpublished.
145     $this->drupalPostForm('fr/node/' . $english_node->id() . '/edit', [
146       'moderation_state[0][state]' => 'published',
147     ], t('Save (this translation)'));
148     $this->assertText(t('Article Translated node has been updated.'));
149     $english_node = $this->drupalGetNodeByTitle('Another node', TRUE);
150     $french_node = $english_node->getTranslation('fr');
151     $this->assertEqual($french_node->moderation_state->value, 'published');
152     $this->assertTrue($french_node->isPublished());
153     $this->assertEqual($english_node->moderation_state->value, 'draft');
154     $this->assertFalse($english_node->isPublished());
155
156     // Now check that we can create a new draft of the translation.
157     $edit = [
158       'title[0][value]' => 'New draft of translated node',
159       'moderation_state[0][state]' => 'draft',
160     ];
161     $this->drupalPostForm('fr/node/' . $english_node->id() . '/edit', $edit, t('Save (this translation)'));
162     $this->assertText(t('Article New draft of translated node has been updated.'));
163     $english_node = $this->drupalGetNodeByTitle('Another node', TRUE);
164     $french_node = $english_node->getTranslation('fr');
165     $this->assertEqual($french_node->moderation_state->value, 'published');
166     $this->assertTrue($french_node->isPublished());
167     $this->assertEqual($french_node->getTitle(), 'Translated node', 'The default revision of the published translation remains the same.');
168
169     // Publish the French article before testing the archive transition.
170     $this->drupalPostForm('fr/node/' . $english_node->id() . '/edit', [
171       'moderation_state[0][state]' => 'published',
172     ], t('Save (this translation)'));
173     $this->assertText(t('Article New draft of translated node has been updated.'));
174     $english_node = $this->drupalGetNodeByTitle('Another node', TRUE);
175     $french_node = $english_node->getTranslation('fr');
176     $this->assertEqual($french_node->moderation_state->value, 'published');
177     $this->assertTrue($french_node->isPublished());
178     $this->assertEqual($french_node->getTitle(), 'New draft of translated node', 'The draft has replaced the published revision.');
179
180     // Publish the English article before testing the archive transition.
181     $this->drupalPostForm('node/' . $english_node->id() . '/edit', [
182       'moderation_state[0][state]' => 'published',
183     ], t('Save (this translation)'));
184     $this->assertText(t('Article Another node has been updated.'));
185     $english_node = $this->drupalGetNodeByTitle('Another node', TRUE);
186     $this->assertEqual($english_node->moderation_state->value, 'published');
187
188     // Archive the node and its translation.
189     $this->drupalPostForm('node/' . $english_node->id() . '/edit', [
190       'moderation_state[0][state]' => 'archived',
191     ], t('Save (this translation)'));
192     $this->assertText(t('Article Another node has been updated.'));
193     $this->drupalPostForm('fr/node/' . $english_node->id() . '/edit', [
194       'moderation_state[0][state]' => 'archived',
195     ], t('Save (this translation)'));
196     $this->assertText(t('Article New draft of translated node has been updated.'));
197     $english_node = $this->drupalGetNodeByTitle('Another node', TRUE);
198     $french_node = $english_node->getTranslation('fr');
199     $this->assertEqual($english_node->moderation_state->value, 'archived');
200     $this->assertFalse($english_node->isPublished());
201     $this->assertEqual($french_node->moderation_state->value, 'archived');
202     $this->assertFalse($french_node->isPublished());
203   }
204
205   /**
206    * Tests that individual translations can be moderated independently.
207    */
208   public function testLanguageIndependentContentModeration() {
209     // Create a published article in English (revision 1).
210     $this->drupalGet('node/add/article');
211     $node = $this->submitNodeForm('Test 1.1 EN', 'published');
212     $this->assertNotLatestVersionPage($node);
213
214     $edit_path = $node->toUrl('edit-form');
215     $translate_path = $node->toUrl('drupal:content-translation-overview');
216
217     // Create a new English draft (revision 2).
218     $this->drupalGet($edit_path);
219     $this->submitNodeForm('Test 1.2 EN', 'draft', TRUE);
220     $this->assertLatestVersionPage($node);
221
222     // Add a French translation draft (revision 3).
223     $this->drupalGet($translate_path);
224     $this->clickLink(t('Add'));
225     $this->submitNodeForm('Test 1.3 FR', 'draft');
226     $fr_node = $this->loadTranslation($node, 'fr');
227     $this->assertLatestVersionPage($fr_node);
228     $this->assertModerationForm($node);
229
230     // Add an Italian translation draft (revision 4).
231     $this->drupalGet($translate_path);
232     $this->clickLink(t('Add'));
233     $this->submitNodeForm('Test 1.4 IT', 'draft');
234     $it_node = $this->loadTranslation($node, 'it');
235     $this->assertLatestVersionPage($it_node);
236     $this->assertModerationForm($node);
237     $this->assertModerationForm($fr_node);
238
239     // Publish the English draft (revision 5).
240     $this->drupalGet($edit_path);
241     $this->submitNodeForm('Test 1.5 EN', 'published', TRUE);
242     $this->assertNotLatestVersionPage($node);
243     $this->assertModerationForm($fr_node);
244     $this->assertModerationForm($it_node);
245
246     // Publish the Italian draft (revision 6).
247     $this->drupalGet($translate_path);
248     $this->clickLink(t('Edit'), 2);
249     $this->submitNodeForm('Test 1.6 IT', 'published');
250     $this->assertNotLatestVersionPage($it_node);
251     $this->assertNoModerationForm($node);
252     $this->assertModerationForm($fr_node);
253
254     // Publish the French draft (revision 7).
255     $this->drupalGet($translate_path);
256     $this->clickLink(t('Edit'), 1);
257     $this->submitNodeForm('Test 1.7 FR', 'published');
258     $this->assertNotLatestVersionPage($fr_node);
259     $this->assertNoModerationForm($node);
260     $this->assertNoModerationForm($it_node);
261
262     // Create an Italian draft (revision 8).
263     $this->drupalGet($translate_path);
264     $this->clickLink(t('Edit'), 2);
265     $this->submitNodeForm('Test 1.8 IT', 'draft');
266     $this->assertLatestVersionPage($it_node);
267     $this->assertNoModerationForm($node);
268     $this->assertNoModerationForm($fr_node);
269
270     // Create a French draft (revision 9).
271     $this->drupalGet($translate_path);
272     $this->clickLink(t('Edit'), 1);
273     $this->submitNodeForm('Test 1.9 FR', 'draft');
274     $this->assertLatestVersionPage($fr_node);
275     $this->assertNoModerationForm($node);
276     $this->assertModerationForm($it_node);
277
278     // Create an English draft (revision 10).
279     $this->drupalGet($edit_path);
280     $this->submitNodeForm('Test 1.10 EN', 'draft');
281     $this->assertLatestVersionPage($node);
282     $this->assertModerationForm($fr_node);
283     $this->assertModerationForm($it_node);
284
285     // Now start from a draft article in English (revision 1).
286     $this->drupalGet('node/add/article');
287     $node2 = $this->submitNodeForm('Test 2.1 EN', 'draft', TRUE);
288     $this->assertNotLatestVersionPage($node2, TRUE);
289
290     $edit_path = $node2->toUrl('edit-form');
291     $translate_path = $node2->toUrl('drupal:content-translation-overview');
292
293     // Add a French translation (revision 2).
294     $this->drupalGet($translate_path);
295     $this->clickLink(t('Add'));
296     $this->submitNodeForm('Test 2.2 FR', 'draft');
297     $fr_node2 = $this->loadTranslation($node2, 'fr');
298     $this->assertNotLatestVersionPage($fr_node2, TRUE);
299     $this->assertModerationForm($node2, FALSE);
300
301     // Add an Italian translation (revision 3).
302     $this->drupalGet($translate_path);
303     $this->clickLink(t('Add'));
304     $this->submitNodeForm('Test 2.3 IT', 'draft');
305     $it_node2 = $this->loadTranslation($node2, 'it');
306     $this->assertNotLatestVersionPage($it_node2, TRUE);
307     $this->assertModerationForm($node2, FALSE);
308     $this->assertModerationForm($fr_node2, FALSE);
309
310     // Publish the English draft (revision 4).
311     $this->drupalGet($edit_path);
312     $this->submitNodeForm('Test 2.4 EN', 'published', TRUE);
313     $this->assertNotLatestVersionPage($node2);
314     $this->assertModerationForm($fr_node2, FALSE);
315     $this->assertModerationForm($it_node2, FALSE);
316
317     // Publish the Italian draft (revision 5).
318     $this->drupalGet($translate_path);
319     $this->clickLink(t('Edit'), 2);
320     $this->submitNodeForm('Test 2.5 IT', 'published');
321     $this->assertNotLatestVersionPage($it_node2);
322     $this->assertNoModerationForm($node2);
323     $this->assertModerationForm($fr_node2, FALSE);
324
325     // Publish the French draft (revision 6).
326     $this->drupalGet($translate_path);
327     $this->clickLink(t('Edit'), 1);
328     $this->submitNodeForm('Test 2.6 FR', 'published');
329     $this->assertNotLatestVersionPage($fr_node2);
330     $this->assertNoModerationForm($node2);
331     $this->assertNoModerationForm($it_node2);
332
333     // Now that all revision translations are published, verify that the
334     // moderation form is never displayed on revision pages.
335     /** @var \Drupal\node\NodeStorageInterface $storage */
336     $storage = $this->container->get('entity_type.manager')->getStorage('node');
337     foreach (range(11, 16) as $revision_id) {
338       /** @var \Drupal\node\NodeInterface $revision */
339       $revision = $storage->loadRevision($revision_id);
340       foreach ($revision->getTranslationLanguages() as $langcode => $language) {
341         if ($revision->isRevisionTranslationAffected()) {
342           $this->drupalGet($revision->toUrl('revision'));
343           $this->assertFalse($this->hasModerationForm(), 'Moderation form is not displayed correctly for revision ' . $revision_id);
344           break;
345         }
346       }
347     }
348
349     // Create an Italian draft (revision 7).
350     $this->drupalGet($translate_path);
351     $this->clickLink(t('Edit'), 2);
352     $this->submitNodeForm('Test 2.7 IT', 'draft');
353     $this->assertLatestVersionPage($it_node2);
354     $this->assertNoModerationForm($node2);
355     $this->assertNoModerationForm($fr_node2);
356
357     // Create a French draft (revision 8).
358     $this->drupalGet($translate_path);
359     $this->clickLink(t('Edit'), 1);
360     $this->submitNodeForm('Test 2.8 FR', 'draft');
361     $this->assertLatestVersionPage($fr_node2);
362     $this->assertNoModerationForm($node2);
363     $this->assertModerationForm($it_node2);
364
365     // Create an English draft (revision 9).
366     $this->drupalGet($edit_path);
367     $this->submitNodeForm('Test 2.9 EN', 'draft', TRUE);
368     $this->assertLatestVersionPage($node2);
369     $this->assertModerationForm($fr_node2);
370     $this->assertModerationForm($it_node2);
371
372     // Now publish a draft in another language first and verify that the
373     // moderation form is not displayed on the English node view page.
374     $this->drupalGet('node/add/article');
375     $node3 = $this->submitNodeForm('Test 3.1 EN', 'published');
376     $this->assertNotLatestVersionPage($node3);
377
378     $edit_path = $node3->toUrl('edit-form');
379     $translate_path = $node3->toUrl('drupal:content-translation-overview');
380
381     // Create an English draft (revision 2).
382     $this->drupalGet($edit_path);
383     $this->submitNodeForm('Test 3.2 EN', 'draft', TRUE);
384     $this->assertLatestVersionPage($node3);
385
386     // Add a French translation (revision 3).
387     $this->drupalGet($translate_path);
388     $this->clickLink(t('Add'));
389     $this->submitNodeForm('Test 3.3 FR', 'draft');
390     $fr_node3 = $this->loadTranslation($node3, 'fr');
391     $this->assertLatestVersionPage($fr_node3);
392     $this->assertModerationForm($node3);
393
394     // Publish the French draft (revision 4).
395     $this->drupalGet($translate_path);
396     $this->clickLink(t('Edit'), 1);
397     $this->submitNodeForm('Test 3.4 FR', 'published');
398     $this->assertNotLatestVersionPage($fr_node3);
399     $this->assertModerationForm($node3);
400   }
401
402   /**
403    * Checks that new translation values are populated properly.
404    */
405   public function testNewTranslationSourceValues() {
406     // Create a published article in Italian (revision 1).
407     $this->drupalGet('node/add/article');
408     $node = $this->submitNodeForm('Test 1.1 IT', 'published', TRUE, 'it');
409     $this->assertNotLatestVersionPage($node);
410
411     // Create a new draft (revision 2).
412     $this->drupalGet($node->toUrl('edit-form'));
413     $this->submitNodeForm('Test 1.2 IT', 'draft', TRUE);
414     $this->assertLatestVersionPage($node);
415
416     // Create an English draft (revision 3) and verify that the Italian draft
417     // values are used as source values.
418     $url = $node->toUrl('drupal:content-translation-add');
419     $url->setRouteParameter('source', 'it');
420     $url->setRouteParameter('target', 'en');
421     $this->drupalGet($url);
422     $this->assertSession()->pageTextContains('Test 1.2 IT');
423     $this->submitNodeForm('Test 1.3 EN', 'draft');
424     $this->assertLatestVersionPage($node);
425
426     // Create a French draft (without saving) and verify that the Italian draft
427     // values are used as source values.
428     $url->setRouteParameter('target', 'fr');
429     $this->drupalGet($url);
430     $this->assertSession()->pageTextContains('Test 1.2 IT');
431
432     // Now switch source language and verify that the English draft values are
433     // used as source values.
434     $url->setRouteParameter('source', 'en');
435     $this->drupalGet($url);
436     $this->assertSession()->pageTextContains('Test 1.3 EN');
437   }
438
439   /**
440    * Submits the node form at the current URL with the specified values.
441    *
442    * @param string $title
443    *   The node title.
444    * @param string $moderation_state
445    *   The moderation state.
446    * @param bool $default_translation
447    *   (optional) Whether we are editing the default translation.
448    * @param string|null $langcode
449    *   (optional) The node language. Defaults to English.
450    *
451    * @return \Drupal\node\NodeInterface|null
452    *   A node object if a new one is being created, NULL otherwise.
453    */
454   protected function submitNodeForm($title, $moderation_state, $default_translation = FALSE, $langcode = 'en') {
455     $is_new = strpos($this->getSession()->getCurrentUrl(), '/node/add/') !== FALSE;
456     $edit = [
457       'title[0][value]' => $title,
458       'moderation_state[0][state]' => $moderation_state,
459     ];
460     if ($is_new) {
461       $default_translation = TRUE;
462       $edit['langcode[0][value]'] = $langcode;
463     }
464     $submit = $default_translation ? t('Save') : t('Save (this translation)');
465     $this->drupalPostForm(NULL, $edit, $submit);
466     $message = $is_new ? "Article $title has been created." : "Article $title has been updated.";
467     $this->assertSession()->pageTextContains($message);
468     return $is_new ? $this->drupalGetNodeByTitle($title) : NULL;
469   }
470
471   /**
472    * Loads the node translation for the specified language.
473    *
474    * @param \Drupal\node\NodeInterface $node
475    *   A node object.
476    * @param string $langcode
477    *   The translation language code.
478    *
479    * @return \Drupal\node\NodeInterface
480    *   The node translation object.
481    */
482   protected function loadTranslation(NodeInterface $node, $langcode) {
483     /** @var \Drupal\node\NodeStorageInterface $storage */
484     $storage = $this->container->get('entity_type.manager')->getStorage('node');
485     /** @var \Drupal\node\NodeInterface $node */
486     $node = $storage->loadRevision($storage->getLatestRevisionId($node->id()));
487     return $node->getTranslation($langcode);
488   }
489
490   /**
491    * Asserts that this is the "latest version" page for the specified node.
492    *
493    * @param \Drupal\node\NodeInterface $node
494    *   A node object.
495    */
496   public function assertLatestVersionPage(NodeInterface $node) {
497     $this->assertEquals($node->toUrl('latest-version')->setAbsolute()->toString(), $this->getSession()->getCurrentUrl());
498     $this->assertModerationForm($node);
499   }
500
501   /**
502    * Asserts that this is not the "latest version" page for the specified node.
503    *
504    * @param \Drupal\node\NodeInterface $node
505    *   A node object.
506    * @param bool $moderation_form
507    *   (optional) Whether the page should contain the moderation form. Defaults
508    *   to FALSE.
509    */
510   public function assertNotLatestVersionPage(NodeInterface $node, $moderation_form = FALSE) {
511     $this->assertNotEquals($node->toUrl('latest-version')->setAbsolute()->toString(), $this->getSession()->getCurrentUrl());
512     if ($moderation_form) {
513       $this->assertModerationForm($node, FALSE);
514     }
515     else {
516       $this->assertNoModerationForm($node);
517     }
518   }
519
520   /**
521    * Asserts that the moderation form is displayed for the specified node.
522    *
523    * @param \Drupal\node\NodeInterface $node
524    *   A node object.
525    * @param bool $latest_tab
526    *   (optional) Whether the node form is expected to be displayed on the
527    *   latest version page or on the node view page. Defaults to the former.
528    */
529   public function assertModerationForm(NodeInterface $node, $latest_tab = TRUE) {
530     $this->drupalGet($node->toUrl());
531     $this->assertEquals(!$latest_tab, $this->hasModerationForm());
532     $this->drupalGet($node->toUrl('latest-version'));
533     $this->assertEquals($latest_tab, $this->hasModerationForm());
534   }
535
536   /**
537    * Asserts that the moderation form is not displayed for the specified node.
538    *
539    * @param \Drupal\node\NodeInterface $node
540    *   A node object.
541    */
542   public function assertNoModerationForm(NodeInterface $node) {
543     $this->drupalGet($node->toUrl());
544     $this->assertFalse($this->hasModerationForm());
545     $this->drupalGet($node->toUrl('latest-version'));
546     $this->assertEquals(403, $this->getSession()->getStatusCode());
547   }
548
549   /**
550    * Checks whether the page contains the moderation form.
551    *
552    * @return bool
553    *   TRUE if the moderation form could be find in the page, FALSE otherwise.
554    */
555   public function hasModerationForm() {
556     return (bool) $this->xpath('//ul[@class="entity-moderation-form"]');
557   }
558
559 }