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