Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / web / core / modules / content_moderation / tests / src / Functional / ModerationFormTest.php
1 <?php
2
3 namespace Drupal\Tests\content_moderation\Functional;
4
5 use Drupal\Core\Entity\Entity\EntityFormDisplay;
6 use Drupal\Core\Url;
7
8 /**
9  * Tests the moderation form, specifically on nodes.
10  *
11  * @group content_moderation
12  */
13 class ModerationFormTest extends ModerationStateTestBase {
14
15   /**
16    * Modules to enable.
17    *
18    * @var array
19    */
20   public static $modules = [
21     'node',
22     'content_moderation',
23     'locale',
24     'content_translation',
25   ];
26
27   /**
28    * {@inheritdoc}
29    */
30   protected function setUp() {
31     parent::setUp();
32     $this->drupalLogin($this->adminUser);
33     $this->createContentTypeFromUi('Moderated content', 'moderated_content', TRUE);
34     $this->grantUserPermissionToCreateContentOfType($this->adminUser, 'moderated_content');
35   }
36
37   /**
38    * Tests the moderation form that shows on the latest version page.
39    *
40    * The latest version page only shows if there is a pending revision.
41    *
42    * @see \Drupal\content_moderation\EntityOperations
43    * @see \Drupal\Tests\content_moderation\Functional\ModerationStateBlockTest::testCustomBlockModeration
44    */
45   public function testModerationForm() {
46     // Create new moderated content in draft.
47     $this->drupalPostForm('node/add/moderated_content', [
48       'title[0][value]' => 'Some moderated content',
49       'body[0][value]' => 'First version of the content.',
50       'moderation_state[0][state]' => 'draft',
51     ], t('Save'));
52
53     $node = $this->drupalGetNodeByTitle('Some moderated content');
54     $canonical_path = sprintf('node/%d', $node->id());
55     $edit_path = sprintf('node/%d/edit', $node->id());
56     $latest_version_path = sprintf('node/%d/latest', $node->id());
57
58     $this->assertTrue($this->adminUser->hasPermission('edit any moderated_content content'));
59
60     // The canonical view should have a moderation form, because it is not the
61     // live revision.
62     $this->drupalGet($canonical_path);
63     $this->assertResponse(200);
64     $this->assertField('edit-new-state', 'The node view page has a moderation form.');
65
66     // The latest version page should not show, because there is no pending
67     // revision.
68     $this->drupalGet($latest_version_path);
69     $this->assertResponse(403);
70
71     // Update the draft.
72     $this->drupalPostForm($edit_path, [
73       'body[0][value]' => 'Second version of the content.',
74       'moderation_state[0][state]' => 'draft',
75     ], t('Save'));
76
77     // The canonical view should have a moderation form, because it is not the
78     // live revision.
79     $this->drupalGet($canonical_path);
80     $this->assertResponse(200);
81     $this->assertField('edit-new-state', 'The node view page has a moderation form.');
82
83     // Preview the draft.
84     $this->drupalPostForm($edit_path, [
85       'body[0][value]' => 'Second version of the content.',
86       'moderation_state[0][state]' => 'draft',
87     ], t('Preview'));
88
89     // The preview view should not have a moderation form.
90     $preview_url = Url::fromRoute('entity.node.preview', [
91       'node_preview' => $node->uuid(),
92       'view_mode_id' => 'full',
93     ]);
94     $this->assertResponse(200);
95     $this->assertUrl($preview_url);
96     $this->assertNoField('edit-new-state', 'The node preview page has no moderation form.');
97
98     // The latest version page should not show, because there is still no
99     // pending revision.
100     $this->drupalGet($latest_version_path);
101     $this->assertResponse(403);
102
103     // Publish the draft.
104     $this->drupalPostForm($edit_path, [
105       'body[0][value]' => 'Third version of the content.',
106       'moderation_state[0][state]' => 'published',
107     ], t('Save'));
108
109     // Check widget default value.
110     $this->drupalGet($edit_path);
111     $this->assertFieldByName('moderation_state[0][state]', 'published', 'The moderation default value is set correctly.');
112
113     // The published view should not have a moderation form, because it is the
114     // live revision.
115     $this->drupalGet($canonical_path);
116     $this->assertResponse(200);
117     $this->assertNoField('edit-new-state', 'The node view page has no moderation form.');
118
119     // The latest version page should not show, because there is still no
120     // pending revision.
121     $this->drupalGet($latest_version_path);
122     $this->assertResponse(403);
123
124     // Make a pending revision.
125     $this->drupalPostForm($edit_path, [
126       'body[0][value]' => 'Fourth version of the content.',
127       'moderation_state[0][state]' => 'draft',
128     ], t('Save'));
129
130     // The published view should not have a moderation form, because it is the
131     // live revision.
132     $this->drupalGet($canonical_path);
133     $this->assertResponse(200);
134     $this->assertNoField('edit-new-state', 'The node view page has no moderation form.');
135
136     // The latest version page should show the moderation form and have "Draft"
137     // status, because the pending revision is in "Draft".
138     $this->drupalGet($latest_version_path);
139     $this->assertResponse(200);
140     $this->assertField('edit-new-state', 'The latest-version page has a moderation form.');
141     $this->assertText('Draft', 'Correct status found on the latest-version page.');
142
143     // Submit the moderation form to change status to published.
144     $this->drupalPostForm($latest_version_path, [
145       'new_state' => 'published',
146     ], t('Apply'));
147
148     // The latest version page should not show, because there is no
149     // pending revision.
150     $this->drupalGet($latest_version_path);
151     $this->assertResponse(403);
152   }
153
154   /**
155    * Test moderation non-bundle entity type.
156    */
157   public function testNonBundleModerationForm() {
158     $this->drupalLogin($this->rootUser);
159     $this->workflow->getTypePlugin()->addEntityTypeAndBundle('entity_test_mulrevpub', 'entity_test_mulrevpub');
160     $this->workflow->save();
161
162     // Create new moderated content in draft.
163     $this->drupalPostForm('entity_test_mulrevpub/add', ['moderation_state[0][state]' => 'draft'], t('Save'));
164
165     // The latest version page should not show, because there is no pending
166     // revision.
167     $this->drupalGet('/entity_test_mulrevpub/manage/1/latest');
168     $this->assertResponse(403);
169
170     // Update the draft.
171     $this->drupalPostForm('entity_test_mulrevpub/manage/1/edit', ['moderation_state[0][state]' => 'draft'], t('Save'));
172
173     // The latest version page should not show, because there is still no
174     // pending revision.
175     $this->drupalGet('/entity_test_mulrevpub/manage/1/latest');
176     $this->assertResponse(403);
177
178     // Publish the draft.
179     $this->drupalPostForm('entity_test_mulrevpub/manage/1/edit', ['moderation_state[0][state]' => 'published'], t('Save'));
180
181     // The published view should not have a moderation form, because it is the
182     // default revision.
183     $this->drupalGet('entity_test_mulrevpub/manage/1');
184     $this->assertResponse(200);
185     $this->assertNoText('Status', 'The node view page has no moderation form.');
186
187     // The latest version page should not show, because there is still no
188     // pending revision.
189     $this->drupalGet('entity_test_mulrevpub/manage/1/latest');
190     $this->assertResponse(403);
191
192     // Make a pending revision.
193     $this->drupalPostForm('entity_test_mulrevpub/manage/1/edit', ['moderation_state[0][state]' => 'draft'], t('Save'));
194
195     // The published view should not have a moderation form, because it is the
196     // default revision.
197     $this->drupalGet('entity_test_mulrevpub/manage/1');
198     $this->assertResponse(200);
199     $this->assertNoText('Status', 'The node view page has no moderation form.');
200
201     // The latest version page should show the moderation form and have "Draft"
202     // status, because the pending revision is in "Draft".
203     $this->drupalGet('entity_test_mulrevpub/manage/1/latest');
204     $this->assertResponse(200);
205     $this->assertText('Moderation state', 'Form text found on the latest-version page.');
206     $this->assertText('Draft', 'Correct status found on the latest-version page.');
207
208     // Submit the moderation form to change status to published.
209     $this->drupalPostForm('entity_test_mulrevpub/manage/1/latest', [
210       'new_state' => 'published',
211     ], t('Apply'));
212
213     // The latest version page should not show, because there is no
214     // pending revision.
215     $this->drupalGet('entity_test_mulrevpub/manage/1/latest');
216     $this->assertResponse(403);
217   }
218
219   /**
220    * Tests the revision author is updated when the moderation form is used.
221    */
222   public function testModerationFormSetsRevisionAuthor() {
223     // Create new moderated content in published.
224     $node = $this->createNode(['type' => 'moderated_content', 'moderation_state' => 'published']);
225     // Make a pending revision.
226     $node->title = $this->randomMachineName();
227     $node->moderation_state->value = 'draft';
228     $node->setRevisionCreationTime(12345);
229     $node->save();
230
231     $another_user = $this->drupalCreateUser($this->permissions);
232     $this->grantUserPermissionToCreateContentOfType($another_user, 'moderated_content');
233     $this->drupalLogin($another_user);
234     $this->drupalPostForm(sprintf('node/%d/latest', $node->id()), [
235       'new_state' => 'published',
236     ], t('Apply'));
237
238     $this->drupalGet(sprintf('node/%d/revisions', $node->id()));
239     $this->assertText('by ' . $another_user->getAccountName());
240
241     // Verify the revision creation time has been updated.
242     $node = $node->load($node->id());
243     $this->assertGreaterThan(12345, $node->getRevisionCreationTime());
244   }
245
246   /**
247    * Tests translated and moderated nodes.
248    */
249   public function testContentTranslationNodeForm() {
250     $this->drupalLogin($this->rootUser);
251
252     // Add French language.
253     $edit = [
254       'predefined_langcode' => 'fr',
255     ];
256     $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
257
258     // Enable content translation on articles.
259     $this->drupalGet('admin/config/regional/content-language');
260     $edit = [
261       'entity_types[node]' => TRUE,
262       'settings[node][moderated_content][translatable]' => TRUE,
263       'settings[node][moderated_content][settings][language][language_alterable]' => TRUE,
264     ];
265     $this->drupalPostForm(NULL, $edit, t('Save configuration'));
266
267     // Adding languages requires a container rebuild in the test running
268     // environment so that multilingual services are used.
269     $this->rebuildContainer();
270
271     // Create new moderated content in draft (revision 1).
272     $this->drupalPostForm('node/add/moderated_content', [
273       'title[0][value]' => 'Some moderated content',
274       'body[0][value]' => 'First version of the content.',
275       'moderation_state[0][state]' => 'draft',
276     ], t('Save'));
277     $this->assertTrue($this->xpath('//ul[@class="entity-moderation-form"]'));
278
279     $node = $this->drupalGetNodeByTitle('Some moderated content');
280     $this->assertTrue($node->language(), 'en');
281     $edit_path = sprintf('node/%d/edit', $node->id());
282     $translate_path = sprintf('node/%d/translations/add/en/fr', $node->id());
283     $latest_version_path = sprintf('node/%d/latest', $node->id());
284     $french = \Drupal::languageManager()->getLanguage('fr');
285
286     $this->drupalGet($latest_version_path);
287     $this->assertSession()->statusCodeEquals('403');
288     $this->assertFalse($this->xpath('//ul[@class="entity-moderation-form"]'));
289
290     // Add french translation (revision 2).
291     $this->drupalGet($translate_path);
292     $this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
293     $this->assertSession()->optionExists('moderation_state[0][state]', 'published');
294     $this->assertSession()->optionNotExists('moderation_state[0][state]', 'archived');
295     $this->drupalPostForm(NULL, [
296       'body[0][value]' => 'Second version of the content.',
297       'moderation_state[0][state]' => 'published',
298     ], t('Save (this translation)'));
299
300     $this->drupalGet($latest_version_path, ['language' => $french]);
301     $this->assertSession()->statusCodeEquals('403');
302     $this->assertFalse($this->xpath('//ul[@class="entity-moderation-form"]'));
303
304     // Add french pending revision (revision 3).
305     $this->drupalGet($edit_path, ['language' => $french]);
306     $this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
307     $this->assertSession()->optionExists('moderation_state[0][state]', 'published');
308     $this->assertSession()->optionExists('moderation_state[0][state]', 'archived');
309     $this->drupalPostForm(NULL, [
310       'body[0][value]' => 'Third version of the content.',
311       'moderation_state[0][state]' => 'draft',
312     ], t('Save (this translation)'));
313
314     $this->drupalGet($latest_version_path, ['language' => $french]);
315     $this->assertTrue($this->xpath('//ul[@class="entity-moderation-form"]'));
316
317     $this->drupalGet($edit_path);
318     $this->clickLink('Delete');
319     $this->assertSession()->buttonExists('Delete');
320
321     $this->drupalGet($latest_version_path);
322     $this->assertFalse($this->xpath('//ul[@class="entity-moderation-form"]'));
323
324     // Publish the french pending revision (revision 4).
325     $this->drupalGet($edit_path, ['language' => $french]);
326     $this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
327     $this->assertSession()->optionExists('moderation_state[0][state]', 'published');
328     $this->assertSession()->optionNotExists('moderation_state[0][state]', 'archived');
329     $this->drupalPostForm(NULL, [
330       'body[0][value]' => 'Fifth version of the content.',
331       'moderation_state[0][state]' => 'published',
332     ], t('Save (this translation)'));
333
334     $this->drupalGet($latest_version_path, ['language' => $french]);
335     $this->assertFalse($this->xpath('//ul[@class="entity-moderation-form"]'));
336
337     // Publish the English pending revision (revision 5).
338     $this->drupalGet($edit_path);
339     $this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
340     $this->assertSession()->optionExists('moderation_state[0][state]', 'published');
341     $this->assertSession()->optionNotExists('moderation_state[0][state]', 'archived');
342     $this->drupalPostForm(NULL, [
343       'body[0][value]' => 'Sixth version of the content.',
344       'moderation_state[0][state]' => 'published',
345     ], t('Save (this translation)'));
346
347     $this->drupalGet($latest_version_path);
348     $this->assertFalse($this->xpath('//ul[@class="entity-moderation-form"]'));
349
350     // Make sure we are allowed to create a pending French revision.
351     $this->drupalGet($edit_path, ['language' => $french]);
352     $this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
353     $this->assertSession()->optionExists('moderation_state[0][state]', 'published');
354     $this->assertSession()->optionExists('moderation_state[0][state]', 'archived');
355
356     // Add an English pending revision (revision 6).
357     $this->drupalGet($edit_path);
358     $this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
359     $this->assertSession()->optionExists('moderation_state[0][state]', 'published');
360     $this->assertSession()->optionExists('moderation_state[0][state]', 'archived');
361     $this->drupalPostForm(NULL, [
362       'body[0][value]' => 'Seventh version of the content.',
363       'moderation_state[0][state]' => 'draft',
364     ], t('Save (this translation)'));
365
366     $this->drupalGet($latest_version_path);
367     $this->assertTrue($this->xpath('//ul[@class="entity-moderation-form"]'));
368     $this->drupalGet($latest_version_path, ['language' => $french]);
369     $this->assertFalse($this->xpath('//ul[@class="entity-moderation-form"]'));
370
371     // Publish the English pending revision (revision 7)
372     $this->drupalGet($edit_path);
373     $this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
374     $this->assertSession()->optionExists('moderation_state[0][state]', 'published');
375     $this->assertSession()->optionNotExists('moderation_state[0][state]', 'archived');
376     $this->drupalPostForm(NULL, [
377       'body[0][value]' => 'Eighth version of the content.',
378       'moderation_state[0][state]' => 'published',
379     ], t('Save (this translation)'));
380
381     $this->drupalGet($latest_version_path);
382     $this->assertFalse($this->xpath('//ul[@class="entity-moderation-form"]'));
383
384     // Make sure we are allowed to create a pending French revision.
385     $this->drupalGet($edit_path, ['language' => $french]);
386     $this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
387     $this->assertSession()->optionExists('moderation_state[0][state]', 'published');
388     $this->assertSession()->optionExists('moderation_state[0][state]', 'archived');
389
390     // Make sure we are allowed to create a pending English revision.
391     $this->drupalGet($edit_path);
392     $this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
393     $this->assertSession()->optionExists('moderation_state[0][state]', 'published');
394     $this->assertSession()->optionExists('moderation_state[0][state]', 'archived');
395
396     // Create new moderated content (revision 1).
397     $this->drupalPostForm('node/add/moderated_content', [
398       'title[0][value]' => 'Third moderated content',
399       'moderation_state[0][state]' => 'published',
400     ], t('Save'));
401
402     $node = $this->drupalGetNodeByTitle('Third moderated content');
403     $this->assertTrue($node->language(), 'en');
404     $edit_path = sprintf('node/%d/edit', $node->id());
405     $translate_path = sprintf('node/%d/translations/add/en/fr', $node->id());
406
407     // Translate it, without updating data (revision 2).
408     $this->drupalGet($translate_path);
409     $this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
410     $this->assertSession()->optionExists('moderation_state[0][state]', 'published');
411     $this->assertSession()->optionExists('moderation_state[0][state]', 'archived');
412     $this->drupalPostForm(NULL, [
413       'moderation_state[0][state]' => 'draft',
414     ], t('Save (this translation)'));
415
416     // Add another draft for the translation (revision 3).
417     $this->drupalGet($edit_path, ['language' => $french]);
418     $this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
419     $this->assertSession()->optionExists('moderation_state[0][state]', 'published');
420     $this->assertSession()->optionNotExists('moderation_state[0][state]', 'archived');
421     $this->drupalPostForm(NULL, [
422       'moderation_state[0][state]' => 'draft',
423     ], t('Save (this translation)'));
424
425     // Updating and publishing the french translation is still possible.
426     $this->drupalGet($edit_path, ['language' => $french]);
427     $this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
428     $this->assertSession()->optionExists('moderation_state[0][state]', 'published');
429     $this->assertSession()->optionNotExists('moderation_state[0][state]', 'archived');
430     $this->drupalPostForm(NULL, [
431       'moderation_state[0][state]' => 'published',
432     ], t('Save (this translation)'));
433
434     // Now the french translation is published, an english draft can be added.
435     $this->drupalGet($edit_path);
436     $this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
437     $this->assertSession()->optionExists('moderation_state[0][state]', 'published');
438     $this->assertSession()->optionExists('moderation_state[0][state]', 'archived');
439     $this->drupalPostForm(NULL, [
440       'moderation_state[0][state]' => 'draft',
441     ], t('Save (this translation)'));
442   }
443
444   /**
445    * Test the moderation_state field when an alternative widget is set.
446    */
447   public function testAlternativeModerationStateWidget() {
448     $entity_form_display = EntityFormDisplay::load('node.moderated_content.default');
449     $entity_form_display->setComponent('moderation_state', [
450       'type' => 'string_textfield',
451       'region' => 'content',
452     ]);
453     $entity_form_display->save();
454     $this->drupalPostForm('node/add/moderated_content', [
455       'title[0][value]' => 'Test content',
456       'moderation_state[0][value]' => 'published',
457     ], 'Save');
458     $this->assertSession()->pageTextContains('Moderated content Test content has been created.');
459   }
460
461   /**
462    * Tests that workflows and states can not be deleted if they are in use.
463    *
464    * @covers \Drupal\content_moderation\Plugin\WorkflowType\ContentModeration::workflowHasData
465    * @covers \Drupal\content_moderation\Plugin\WorkflowType\ContentModeration::workflowStateHasData
466    */
467   public function testWorkflowInUse() {
468     $user = $this->createUser([
469       'administer workflows',
470       'create moderated_content content',
471       'edit own moderated_content content',
472       'use editorial transition create_new_draft',
473       'use editorial transition publish',
474       'use editorial transition archive',
475     ]);
476     $this->drupalLogin($user);
477     $paths = [
478       'archived_state' => 'admin/config/workflow/workflows/manage/editorial/state/archived/delete',
479       'editorial_workflow' => 'admin/config/workflow/workflows/manage/editorial/delete',
480     ];
481     $messages = [
482       'archived_state' => 'This workflow state is in use. You cannot remove this workflow state until you have removed all content using it.',
483       'editorial_workflow' => 'This workflow is in use. You cannot remove this workflow until you have removed all content using it.',
484     ];
485     foreach ($paths as $path) {
486       $this->drupalGet($path);
487       $this->assertSession()->buttonExists('Delete');
488     }
489     // Create new moderated content in draft.
490     $this->drupalPostForm('node/add/moderated_content', [
491       'title[0][value]' => 'Some moderated content',
492       'body[0][value]' => 'First version of the content.',
493       'moderation_state[0][state]' => 'draft',
494     ], 'Save');
495
496     // The archived state is not used yet, so can still be deleted.
497     $this->drupalGet($paths['archived_state']);
498     $this->assertSession()->buttonExists('Delete');
499
500     // The workflow is being used, so can't be deleted.
501     $this->drupalGet($paths['editorial_workflow']);
502     $this->assertSession()->buttonNotExists('Delete');
503     $this->assertSession()->statusCodeEquals(200);
504     $this->assertSession()->pageTextContains($messages['editorial_workflow']);
505
506     $node = $this->drupalGetNodeByTitle('Some moderated content');
507     $this->drupalPostForm('node/' . $node->id() . '/edit', [
508       'moderation_state[0][state]' => 'published',
509     ], 'Save');
510     $this->drupalPostForm('node/' . $node->id() . '/edit', [
511       'moderation_state[0][state]' => 'archived',
512     ], 'Save');
513
514     // Now the archived state is being used so it can not be deleted either.
515     foreach ($paths as $type => $path) {
516       $this->drupalGet($path);
517       $this->assertSession()->buttonNotExists('Delete');
518       $this->assertSession()->statusCodeEquals(200);
519       $this->assertSession()->pageTextContains($messages[$type]);
520     }
521   }
522
523 }