From 2d5e8d119dfa651d16d426ddad9c4387da3bb6be Mon Sep 17 00:00:00 2001 From: Lee Rowlands <lee.rowlands@previousnext.com.au> Date: Wed, 2 Aug 2017 07:34:36 +1000 Subject: [PATCH] Issue #2753717 by timmillwood, amateescu, alexpott, mtodor, boaloysius, Jo Fitzgerald, phenaproxima, plach, gaurav.kapoor, tkoleary, yoroy, larowlan, karlos007, Berdir, Sam152: Add select field to choose moderation state on entity forms --- .../Functional/BookContentModerationTest.php | 53 ++++-- .../tests/src/Functional/BookTestTrait.php | 37 ++-- .../content_moderation/src/EntityTypeInfo.php | 20 +- .../src/Form/EntityModerationForm.php | 4 +- .../FieldWidget/ModerationStateWidget.php | 106 ++++------- .../Field/ModerationStateFieldItemList.php | 31 ++- .../src/Functional/ModerationFormTest.php | 179 ++++++++++-------- .../src/Functional/ModerationLocaleTest.php | 45 +++-- .../ModerationRevisionRevertTest.php | 14 +- .../Functional/ModerationStateBlockTest.php | 13 +- .../Functional/ModerationStateNodeTest.php | 61 +++++- .../ModerationStateNodeTypeTest.php | 8 +- .../Functional/ModerationStateTestBase.php | 3 + .../tests/src/Functional/NodeAccessTest.php | 10 +- .../MenuUiContentModerationTest.php | 32 ++-- .../Functional/PathContentModerationTest.php | 18 +- 16 files changed, 385 insertions(+), 249 deletions(-) diff --git a/core/modules/book/tests/src/Functional/BookContentModerationTest.php b/core/modules/book/tests/src/Functional/BookContentModerationTest.php index ddd9d60b6dfc..5d6a34063c0b 100644 --- a/core/modules/book/tests/src/Functional/BookContentModerationTest.php +++ b/core/modules/book/tests/src/Functional/BookContentModerationTest.php @@ -43,10 +43,10 @@ protected function setUp() { */ public function testBookWithPendingRevisions() { // Create two books. - $book_1_nodes = $this->createBook(t('Save and Publish')); + $book_1_nodes = $this->createBook(['moderation_state[0][state]' => 'published']); $book_1 = $this->book; - $this->createBook(t('Save and Publish')); + $this->createBook(['moderation_state[0][state]' => 'published']); $book_2 = $this->book; $this->drupalLogin($this->bookAuthor); @@ -58,21 +58,37 @@ public function testBookWithPendingRevisions() { // Create a new book page without actually attaching it to a book and create // a draft. - $edit = ['title[0][value]' => $this->randomString()]; - $this->drupalPostForm('node/add/book', $edit, t('Save and Publish')); + $edit = [ + 'title[0][value]' => $this->randomString(), + 'moderation_state[0][state]' => 'published', + ]; + $this->drupalPostForm('node/add/book', $edit, t('Save')); $node = $this->drupalGetNodeByTitle($edit['title[0][value]']); $this->assertTrue($node); - $this->drupalPostForm('node/' . $node->id() . '/edit', [], t('Save and Create New Draft')); + + $edit = [ + 'moderation_state[0][state]' => 'draft', + ]; + $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save')); $this->assertSession()->pageTextNotContains('You can only change the book outline for the published version of this content.'); // Create a book draft with no changes, then publish it. - $this->drupalPostForm('node/' . $book_1->id() . '/edit', [], t('Save and Create New Draft')); + $edit = [ + 'moderation_state[0][state]' => 'draft', + ]; + $this->drupalPostForm('node/' . $book_1->id() . '/edit', $edit, t('Save')); $this->assertSession()->pageTextNotContains('You can only change the book outline for the published version of this content.'); - $this->drupalPostForm('node/' . $book_1->id() . '/edit', [], t('Save and Publish')); + $edit = [ + 'moderation_state[0][state]' => 'published', + ]; + $this->drupalPostForm('node/' . $book_1->id() . '/edit', $edit, t('Save')); // Try to move Node 2 to a different parent. - $edit['book[pid]'] = $book_1_nodes[3]->id(); - $this->drupalPostForm('node/' . $book_1_nodes[1]->id() . '/edit', $edit, t('Save and Create New Draft')); + $edit = [ + 'book[pid]' => $book_1_nodes[3]->id(), + 'moderation_state[0][state]' => 'draft', + ]; + $this->drupalPostForm('node/' . $book_1_nodes[1]->id() . '/edit', $edit, t('Save')); $this->assertSession()->pageTextContains('You can only change the book outline for the published version of this content.'); @@ -82,8 +98,11 @@ public function testBookWithPendingRevisions() { $this->checkBookNode($book_1_nodes[0], [$book_1_nodes[1], $book_1_nodes[2]], $book_1, $book_1, $book_1_nodes[1], [$book_1]); // Try to move Node 2 to a different book. - $edit['book[bid]'] = $book_2->id(); - $this->drupalPostForm('node/' . $book_1_nodes[1]->id() . '/edit', $edit, t('Save and Create New Draft')); + $edit = [ + 'book[bid]' => $book_2->id(), + 'moderation_state[0][state]' => 'draft', + ]; + $this->drupalPostForm('node/' . $book_1_nodes[1]->id() . '/edit', $edit, t('Save')); $this->assertSession()->pageTextContains('You can only change the book outline for the published version of this content.'); @@ -93,8 +112,11 @@ public function testBookWithPendingRevisions() { $this->checkBookNode($book_1_nodes[0], [$book_1_nodes[1], $book_1_nodes[2]], $book_1, $book_1, $book_1_nodes[1], [$book_1]); // Try to change the weight of Node 2. - $edit['book[weight]'] = 2; - $this->drupalPostForm('node/' . $book_1_nodes[1]->id() . '/edit', $edit, t('Save and Create New Draft')); + $edit = [ + 'book[weight]' => 2, + 'moderation_state[0][state]' => 'draft', + ]; + $this->drupalPostForm('node/' . $book_1_nodes[1]->id() . '/edit', $edit, t('Save')); $this->assertSession()->pageTextContains('You can only change the book outline for the published version of this content.'); @@ -105,7 +127,10 @@ public function testBookWithPendingRevisions() { // Save a new draft revision for the node without any changes and check that // the error message is not displayed. - $this->drupalPostForm('node/' . $book_1_nodes[1]->id() . '/edit', [], t('Save and Create New Draft')); + $edit = [ + 'moderation_state[0][state]' => 'draft', + ]; + $this->drupalPostForm('node/' . $book_1_nodes[1]->id() . '/edit', $edit, t('Save')); $this->assertSession()->pageTextNotContains('You can only change the book outline for the published version of this content.'); } diff --git a/core/modules/book/tests/src/Functional/BookTestTrait.php b/core/modules/book/tests/src/Functional/BookTestTrait.php index 87bbb8b1bc3c..f6944063244d 100644 --- a/core/modules/book/tests/src/Functional/BookTestTrait.php +++ b/core/modules/book/tests/src/Functional/BookTestTrait.php @@ -27,17 +27,18 @@ trait BookTestTrait { /** * Creates a new book with a page hierarchy. * - * @param string $submit - * (optional) Value of the submit button whose click is to be emulated. - * Defaults to 'Save'. + * @param array $edit + * (optional) Field data in an associative array. Changes the current input + * fields (where possible) to the values indicated. Defaults to an empty + * array. * * @return \Drupal\node\NodeInterface[] */ - public function createBook($submit = NULL) { + public function createBook($edit = []) { // Create new book. $this->drupalLogin($this->bookAuthor); - $this->book = $this->createBookNode('new', NULL, $submit); + $this->book = $this->createBookNode('new', NULL, $edit); $book = $this->book; /* @@ -50,11 +51,11 @@ public function createBook($submit = NULL) { * |- Node 4 */ $nodes = []; - $nodes[] = $this->createBookNode($book->id(), NULL, $submit); // Node 0. - $nodes[] = $this->createBookNode($book->id(), $nodes[0]->book['nid'], $submit); // Node 1. - $nodes[] = $this->createBookNode($book->id(), $nodes[0]->book['nid'], $submit); // Node 2. - $nodes[] = $this->createBookNode($book->id(), NULL, $submit); // Node 3. - $nodes[] = $this->createBookNode($book->id(), NULL, $submit); // Node 4. + $nodes[] = $this->createBookNode($book->id(), NULL, $edit); // Node 0. + $nodes[] = $this->createBookNode($book->id(), $nodes[0]->book['nid'], $edit); // Node 1. + $nodes[] = $this->createBookNode($book->id(), $nodes[0]->book['nid'], $edit); // Node 2. + $nodes[] = $this->createBookNode($book->id(), NULL, $edit); // Node 3. + $nodes[] = $this->createBookNode($book->id(), NULL, $edit); // Node 4. $this->drupalLogout(); @@ -167,21 +168,19 @@ public function generateOutlinePattern($nodes) { * A book node ID or set to 'new' to create a new book. * @param int|null $parent * (optional) Parent book reference ID. Defaults to NULL. - * @param string $submit - * (optional) Value of the submit button whose click is to be emulated. - * Defaults to 'Save'. + * @param array $edit + * (optional) Field data in an associative array. Changes the current input + * fields (where possible) to the values indicated. Defaults to an empty + * array. * * @return \Drupal\node\NodeInterface * The created node. */ - public function createBookNode($book_nid, $parent = NULL, $submit = NULL) { + public function createBookNode($book_nid, $parent = NULL, $edit = []) { // $number does not use drupal_static as it should not be reset // since it uniquely identifies each call to createBookNode(). static $number = 0; // Used to ensure that when sorted nodes stay in same order. - $submit = $submit ?: t('Save'); - - $edit = []; $edit['title[0][value]'] = str_pad($number, 2, '0', STR_PAD_LEFT) . ' - SimpleTest test node ' . $this->randomMachineName(10); $edit['body[0][value]'] = 'SimpleTest test body ' . $this->randomMachineName(32) . ' ' . $this->randomMachineName(32); $edit['book[bid]'] = $book_nid; @@ -190,13 +189,13 @@ public function createBookNode($book_nid, $parent = NULL, $submit = NULL) { $this->drupalPostForm('node/add/book', $edit, t('Change book (update list of parents)')); $edit['book[pid]'] = $parent; - $this->drupalPostForm(NULL, $edit, $submit); + $this->drupalPostForm(NULL, $edit, t('Save')); // Make sure the parent was flagged as having children. $parent_node = \Drupal::entityManager()->getStorage('node')->loadUnchanged($parent); $this->assertFalse(empty($parent_node->book['has_children']), 'Parent node is marked as having children'); } else { - $this->drupalPostForm('node/add/book', $edit, $submit); + $this->drupalPostForm('node/add/book', $edit, t('Save')); } // Check to make sure the book node was created. diff --git a/core/modules/content_moderation/src/EntityTypeInfo.php b/core/modules/content_moderation/src/EntityTypeInfo.php index dfb93c5e9397..132e7156d7c7 100644 --- a/core/modules/content_moderation/src/EntityTypeInfo.php +++ b/core/modules/content_moderation/src/EntityTypeInfo.php @@ -261,11 +261,11 @@ public function entityBaseFieldInfo(EntityTypeInterface $entity_type) { ]) ->setDisplayOptions('form', [ 'type' => 'moderation_state_default', - 'weight' => 5, + 'weight' => 100, 'settings' => [], ]) ->addConstraint('ModerationState', []) - ->setDisplayConfigurable('form', FALSE) + ->setDisplayConfigurable('form', TRUE) ->setDisplayConfigurable('view', FALSE) ->setReadOnly(FALSE) ->setTranslatable(TRUE); @@ -322,6 +322,7 @@ public function formAlter(array &$form, FormStateInterface $form_state, $form_id $full_message = $this->t('Unable to save this @type_label. <a href="@latest_revision_edit_url">Publish</a> or <a href="@latest_revision_delete_url">delete</a> the latest revision to allow all workflow transitions.', $args); drupal_set_message($full_message, 'error'); + $form['moderation_state']['#access'] = FALSE; $form['actions']['#access'] = FALSE; $form['invalid_transitions'] = [ 'label' => [ @@ -339,13 +340,24 @@ public function formAlter(array &$form, FormStateInterface $form_state, $form_id ]; if ($form['footer']) { - $form['footer']['invalid_transitions'] = $form['invalid_transitions']; - unset($form['invalid_transitions']); + $form['invalid_transitions']['#group'] = 'footer'; } } // Submit handler to redirect to the latest version, if available. $form['actions']['submit']['#submit'][] = [EntityTypeInfo::class, 'bundleFormRedirect']; + + // Move the 'moderation_state' field widget to the footer region, if + // available. + if (isset($form['footer'])) { + $form['moderation_state']['#group'] = 'footer'; + } + + // Duplicate the label of the current moderation state to the meta + // region, if available. + if (isset($form['meta']['published'])) { + $form['meta']['published']['#markup'] = $form['moderation_state']['widget'][0]['current']['#markup']; + } } } } diff --git a/core/modules/content_moderation/src/Form/EntityModerationForm.php b/core/modules/content_moderation/src/Form/EntityModerationForm.php index 343ab9c12f76..85e18d130d4a 100644 --- a/core/modules/content_moderation/src/Form/EntityModerationForm.php +++ b/core/modules/content_moderation/src/Form/EntityModerationForm.php @@ -88,7 +88,7 @@ public function buildForm(array $form, FormStateInterface $form_state, ContentEn if ($current_state) { $form['current'] = [ '#type' => 'item', - '#title' => $this->t('Status'), + '#title' => $this->t('Moderation state'), '#markup' => $workflow->getTypePlugin()->getState($current_state)->label(), ]; } @@ -98,7 +98,7 @@ public function buildForm(array $form, FormStateInterface $form_state, ContentEn $form['new_state'] = [ '#type' => 'select', - '#title' => $this->t('Moderate'), + '#title' => $this->t('Change to'), '#options' => $target_states, ]; diff --git a/core/modules/content_moderation/src/Plugin/Field/FieldWidget/ModerationStateWidget.php b/core/modules/content_moderation/src/Plugin/Field/FieldWidget/ModerationStateWidget.php index be54bb76c641..9a35f905d860 100644 --- a/core/modules/content_moderation/src/Plugin/Field/FieldWidget/ModerationStateWidget.php +++ b/core/modules/content_moderation/src/Plugin/Field/FieldWidget/ModerationStateWidget.php @@ -2,7 +2,7 @@ namespace Drupal\content_moderation\Plugin\Field\FieldWidget; -use Drupal\Core\Entity\ContentEntityInterface; +use Drupal\content_moderation\Plugin\Field\ModerationStateFieldItemList; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldItemListInterface; @@ -75,7 +75,7 @@ class ModerationStateWidget extends OptionsSelectWidget implements ContainerFact * @param \Drupal\content_moderation\ModerationInformation $moderation_information * Moderation information service. * @param \Drupal\content_moderation\StateTransitionValidation $validator - * Moderation state transition validation service + * Moderation state transition validation service. */ public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, AccountInterface $current_user, EntityTypeManagerInterface $entity_type_manager, ModerationInformation $moderation_information, StateTransitionValidation $validator) { parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings); @@ -106,7 +106,7 @@ public static function create(ContainerInterface $container, array $configuratio * {@inheritdoc} */ public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { - /** @var ContentEntityInterface $entity */ + /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ $entity = $items->getEntity(); /* @var \Drupal\Core\Config\Entity\ConfigEntityInterface $bundle_entity */ @@ -121,95 +121,53 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen /** @var \Drupal\workflows\Transition[] $transitions */ $transitions = $this->validator->getValidTransitions($entity, $this->currentUser); - $target_states = []; + $transition_labels = []; + $default_value = NULL; foreach ($transitions as $transition) { - $target_states[$transition->to()->id()] = $transition->label(); + $transition_to_state = $transition->to(); + $transition_labels[$transition_to_state->id()] = $transition_to_state->label(); } - // @todo https://www.drupal.org/node/2779933 write a test for this. $element += [ - '#access' => FALSE, - '#type' => 'select', - '#options' => $target_states, - '#default_value' => $default->id(), - '#published' => $default->isPublishedState(), - '#key_column' => $this->column, + '#type' => 'container', + 'current' => [ + '#type' => 'item', + '#title' => $this->t('Current state'), + '#markup' => $default->label(), + '#access' => !$entity->isNew(), + '#wrapper_attributes' => [ + 'class' => ['container-inline'], + ], + ], + 'state' => [ + '#type' => 'select', + '#title' => $entity->isNew() ? $this->t('Save as') : $this->t('Change to'), + '#key_column' => $this->column, + '#options' => $transition_labels, + '#default_value' => $default_value, + '#access' => !empty($transition_labels), + '#wrapper_attributes' => [ + 'class' => ['container-inline'], + ], + ], ]; $element['#element_validate'][] = [get_class($this), 'validateElement']; - // Use the dropbutton. - $element['#process'][] = [get_called_class(), 'processActions']; return $element; } /** - * Entity builder updating the node moderation state with the submitted value. - * - * @param string $entity_type_id - * The entity type identifier. - * @param \Drupal\Core\Entity\ContentEntityInterface $entity - * The entity updated with the submitted values. - * @param array $form - * The complete form array. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The current state of the form. - */ - public static function updateStatus($entity_type_id, ContentEntityInterface $entity, array $form, FormStateInterface $form_state) { - $element = $form_state->getTriggeringElement(); - if (isset($element['#moderation_state'])) { - $entity->moderation_state->value = $element['#moderation_state']; - } - } - - /** - * Process callback to alter action buttons. + * {@inheritdoc} */ - public static function processActions($element, FormStateInterface $form_state, array &$form) { - - // We'll steal most of the button configuration from the default submit - // button. However, NodeForm also hides that button for admins (as it adds - // its own, too), so we have to restore it. - $default_button = $form['actions']['submit']; - $default_button['#access'] = TRUE; - - // Add a custom button for each transition we're allowing. The #dropbutton - // property tells FAPI to cluster them all together into a single widget. - $options = $element['#options']; - - $entity = $form_state->getFormObject()->getEntity(); - $translatable = !$entity->isNew() && $entity->isTranslatable(); - foreach ($options as $id => $label) { - $button = [ - '#dropbutton' => 'save', - '#moderation_state' => $id, - '#weight' => -10, - ]; - - $button['#value'] = $translatable - ? t('Save and @transition (this translation)', ['@transition' => $label]) - : t('Save and @transition', ['@transition' => $label]); - - $form['actions']['moderation_state_' . $id] = $button + $default_button; - } - - // Hide the default buttons, including the specialty ones added by - // NodeForm. - foreach (['publish', 'unpublish', 'submit'] as $key) { - $form['actions'][$key]['#access'] = FALSE; - unset($form['actions'][$key]['#dropbutton']); - } - - // Setup a callback to translate the button selection back into field - // widget, so that it will get saved properly. - $form['#entity_builders']['update_moderation_state'] = [get_called_class(), 'updateStatus']; - return $element; + public static function validateElement(array $element, FormStateInterface $form_state) { + $form_state->setValueForElement($element, [$element['state']['#key_column'] => $element['state']['#value']]); } /** * {@inheritdoc} */ public static function isApplicable(FieldDefinitionInterface $field_definition) { - return $field_definition->getName() === 'moderation_state'; + return is_a($field_definition->getClass(), ModerationStateFieldItemList::class, TRUE); } } diff --git a/core/modules/content_moderation/src/Plugin/Field/ModerationStateFieldItemList.php b/core/modules/content_moderation/src/Plugin/Field/ModerationStateFieldItemList.php index fcd6bc6932e3..99e5f558d31f 100644 --- a/core/modules/content_moderation/src/Plugin/Field/ModerationStateFieldItemList.php +++ b/core/modules/content_moderation/src/Plugin/Field/ModerationStateFieldItemList.php @@ -122,19 +122,40 @@ protected function computeModerationFieldItemList() { * {@inheritdoc} */ public function onChange($delta) { + $this->updateModeratedEntity($this->list[$delta]->value); + + parent::onChange($delta); + } + + /** + * {@inheritdoc} + */ + public function setValue($values, $notify = TRUE) { + parent::setValue($values, $notify); + + if (isset($this->list[0])) { + $this->updateModeratedEntity($this->list[0]->value); + } + } + + /** + * Updates the default revision flag and the publishing status of the entity. + * + * @param string $moderation_state_id + * The ID of the new moderation state. + */ + protected function updateModeratedEntity($moderation_state_id) { $entity = $this->getEntity(); /** @var \Drupal\content_moderation\ModerationInformationInterface $content_moderation_info */ $content_moderation_info = \Drupal::service('content_moderation.moderation_information'); $workflow = $content_moderation_info->getWorkflowForEntity($entity); - $current_state_id = $this->list[0]->value; - // Change the entity's default revision flag and the publishing status only // if the new workflow state is a valid one. - if ($workflow->getTypePlugin()->hasState($current_state_id)) { + if ($workflow->getTypePlugin()->hasState($moderation_state_id)) { /** @var \Drupal\content_moderation\ContentModerationState $current_state */ - $current_state = $workflow->getTypePlugin()->getState($current_state_id); + $current_state = $workflow->getTypePlugin()->getState($moderation_state_id); // This entity is default if it is new, a new translation, the default // revision state, or the default revision is not published. @@ -151,8 +172,6 @@ public function onChange($delta) { $published_state ? $entity->setPublished() : $entity->setUnpublished(); } } - - parent::onChange($delta); } } diff --git a/core/modules/content_moderation/tests/src/Functional/ModerationFormTest.php b/core/modules/content_moderation/tests/src/Functional/ModerationFormTest.php index 41cc14bc2f39..a284b5ab65d5 100644 --- a/core/modules/content_moderation/tests/src/Functional/ModerationFormTest.php +++ b/core/modules/content_moderation/tests/src/Functional/ModerationFormTest.php @@ -46,7 +46,8 @@ public function testModerationForm() { $this->drupalPostForm('node/add/moderated_content', [ 'title[0][value]' => 'Some moderated content', 'body[0][value]' => 'First version of the content.', - ], t('Save and Create New Draft')); + 'moderation_state[0][state]' => 'draft', + ], t('Save')); $node = $this->drupalGetNodeByTitle('Some moderated content'); $canonical_path = sprintf('node/%d', $node->id()); @@ -69,7 +70,8 @@ public function testModerationForm() { // Update the draft. $this->drupalPostForm($edit_path, [ 'body[0][value]' => 'Second version of the content.', - ], t('Save and Create New Draft')); + 'moderation_state[0][state]' => 'draft', + ], t('Save')); // The canonical view should have a moderation form, because it is not the // live revision. @@ -85,7 +87,8 @@ public function testModerationForm() { // Publish the draft. $this->drupalPostForm($edit_path, [ 'body[0][value]' => 'Third version of the content.', - ], t('Save and Publish')); + 'moderation_state[0][state]' => 'published', + ], t('Save')); // The published view should not have a moderation form, because it is the // live revision. @@ -101,7 +104,8 @@ public function testModerationForm() { // Make a pending revision. $this->drupalPostForm($edit_path, [ 'body[0][value]' => 'Fourth version of the content.', - ], t('Save and Create New Draft')); + 'moderation_state[0][state]' => 'draft', + ], t('Save')); // The published view should not have a moderation form, because it is the // live revision. @@ -137,7 +141,7 @@ public function testNonBundleModerationForm() { $workflow->save(); // Create new moderated content in draft. - $this->drupalPostForm('entity_test_mulrevpub/add', [], t('Save and Create New Draft')); + $this->drupalPostForm('entity_test_mulrevpub/add', ['moderation_state[0][state]' => 'draft'], t('Save')); // The latest version page should not show, because there is no pending // revision. @@ -145,7 +149,7 @@ public function testNonBundleModerationForm() { $this->assertResponse(403); // Update the draft. - $this->drupalPostForm('entity_test_mulrevpub/manage/1/edit', [], t('Save and Create New Draft')); + $this->drupalPostForm('entity_test_mulrevpub/manage/1/edit', ['moderation_state[0][state]' => 'draft'], t('Save')); // The latest version page should not show, because there is still no // pending revision. @@ -153,7 +157,7 @@ public function testNonBundleModerationForm() { $this->assertResponse(403); // Publish the draft. - $this->drupalPostForm('entity_test_mulrevpub/manage/1/edit', [], t('Save and Publish')); + $this->drupalPostForm('entity_test_mulrevpub/manage/1/edit', ['moderation_state[0][state]' => 'published'], t('Save')); // The published view should not have a moderation form, because it is the // default revision. @@ -167,7 +171,7 @@ public function testNonBundleModerationForm() { $this->assertResponse(403); // Make a pending revision. - $this->drupalPostForm('entity_test_mulrevpub/manage/1/edit', [], t('Save and Create New Draft')); + $this->drupalPostForm('entity_test_mulrevpub/manage/1/edit', ['moderation_state[0][state]' => 'draft'], t('Save')); // The published view should not have a moderation form, because it is the // default revision. @@ -179,7 +183,7 @@ public function testNonBundleModerationForm() { // status, because the pending revision is in "Draft". $this->drupalGet('entity_test_mulrevpub/manage/1/latest'); $this->assertResponse(200); - $this->assertText('Status', 'Form text found on the latest-version page.'); + $this->assertText('Moderation state', 'Form text found on the latest-version page.'); $this->assertText('Draft', 'Correct status found on the latest-version page.'); // Submit the moderation form to change status to published. @@ -244,7 +248,8 @@ public function testContentTranslationNodeForm() { $this->drupalPostForm('node/add/moderated_content', [ 'title[0][value]' => 'Some moderated content', 'body[0][value]' => 'First version of the content.', - ], t('Save and Create New Draft')); + 'moderation_state[0][state]' => 'draft', + ], t('Save')); $this->assertTrue($this->xpath('//ul[@class="entity-moderation-form"]')); $node = $this->drupalGetNodeByTitle('Some moderated content'); @@ -260,12 +265,13 @@ public function testContentTranslationNodeForm() { // Add french translation (revision 2). $this->drupalGet($translate_path); - $this->assertTrue($this->xpath('//input[@value="Save and Create New Draft (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Publish (this translation)"]')); - $this->assertFalse($this->xpath('//input[@value="Save and Archive (this translation)"]')); + $this->assertSession()->optionExists('moderation_state[0][state]', 'draft'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'published'); + $this->assertSession()->optionNotExists('moderation_state[0][state]', 'archived'); $this->drupalPostForm(NULL, [ 'body[0][value]' => 'Second version of the content.', - ], t('Save and Publish (this translation)')); + 'moderation_state[0][state]' => 'published', + ], t('Save (this translation)')); $this->drupalGet($latest_version_path, ['language' => $french]); $this->assertSession()->statusCodeEquals('403'); @@ -273,21 +279,20 @@ public function testContentTranslationNodeForm() { // Add french pending revision (revision 3). $this->drupalGet($edit_path, ['language' => $french]); - $this->assertTrue($this->xpath('//input[@value="Save and Create New Draft (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Publish (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Archive (this translation)"]')); + $this->assertSession()->optionExists('moderation_state[0][state]', 'draft'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'published'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'archived'); $this->drupalPostForm(NULL, [ 'body[0][value]' => 'Third version of the content.', - ], t('Save and Create New Draft (this translation)')); + 'moderation_state[0][state]' => 'draft', + ], t('Save (this translation)')); $this->drupalGet($latest_version_path, ['language' => $french]); $this->assertTrue($this->xpath('//ul[@class="entity-moderation-form"]')); // It should not be possible to add a new english revision. $this->drupalGet($edit_path); - $this->assertFalse($this->xpath('//input[@value="Save and Create New Draft (this translation)"]')); - $this->assertFalse($this->xpath('//input[@value="Save and Publish (this translation)"]')); - $this->assertFalse($this->xpath('//input[@value="Save and Archive (this translation)"]')); + $this->assertSession()->fieldNotExists('moderation_state[0][state]'); $this->assertSession()->pageTextContains('Unable to save this Moderated content.'); $this->clickLink('Publish'); @@ -302,51 +307,52 @@ public function testContentTranslationNodeForm() { // Publish the french pending revision (revision 4). $this->drupalGet($edit_path, ['language' => $french]); - $this->assertTrue($this->xpath('//input[@value="Save and Create New Draft (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Publish (this translation)"]')); - $this->assertFalse($this->xpath('//input[@value="Save and Archive (this translation)"]')); + $this->assertSession()->optionExists('moderation_state[0][state]', 'draft'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'published'); + $this->assertSession()->optionNotExists('moderation_state[0][state]', 'archived'); $this->drupalPostForm(NULL, [ 'body[0][value]' => 'Fifth version of the content.', - ], t('Save and Publish (this translation)')); + 'moderation_state[0][state]' => 'published', + ], t('Save (this translation)')); $this->drupalGet($latest_version_path, ['language' => $french]); $this->assertFalse($this->xpath('//ul[@class="entity-moderation-form"]')); // Now we can publish the english (revision 5). $this->drupalGet($edit_path); - $this->assertTrue($this->xpath('//input[@value="Save and Create New Draft (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Publish (this translation)"]')); - $this->assertFalse($this->xpath('//input[@value="Save and Archive (this translation)"]')); + $this->assertSession()->optionExists('moderation_state[0][state]', 'draft'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'published'); + $this->assertSession()->optionNotExists('moderation_state[0][state]', 'archived'); $this->drupalPostForm(NULL, [ 'body[0][value]' => 'Sixth version of the content.', - ], t('Save and Publish (this translation)')); + 'moderation_state[0][state]' => 'published', + ], t('Save (this translation)')); $this->drupalGet($latest_version_path); $this->assertFalse($this->xpath('//ul[@class="entity-moderation-form"]')); // Make sure we're allowed to create a pending french revision. $this->drupalGet($edit_path, ['language' => $french]); - $this->assertTrue($this->xpath('//input[@value="Save and Create New Draft (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Publish (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Archive (this translation)"]')); + $this->assertSession()->optionExists('moderation_state[0][state]', 'draft'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'published'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'archived'); // Add a english pending revision (revision 6). $this->drupalGet($edit_path); - $this->assertTrue($this->xpath('//input[@value="Save and Create New Draft (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Publish (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Archive (this translation)"]')); + $this->assertSession()->optionExists('moderation_state[0][state]', 'draft'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'published'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'archived'); $this->drupalPostForm(NULL, [ 'body[0][value]' => 'Seventh version of the content.', - ], t('Save and Create New Draft (this translation)')); + 'moderation_state[0][state]' => 'draft', + ], t('Save (this translation)')); $this->drupalGet($latest_version_path); $this->assertTrue($this->xpath('//ul[@class="entity-moderation-form"]')); // Make sure we're not allowed to create a pending french revision. $this->drupalGet($edit_path, ['language' => $french]); - $this->assertFalse($this->xpath('//input[@value="Save and Create New Draft (this translation)"]')); - $this->assertFalse($this->xpath('//input[@value="Save and Publish (this translation)"]')); - $this->assertFalse($this->xpath('//input[@value="Save and Archive (this translation)"]')); + $this->assertSession()->fieldNotExists('moderation_state[0][state]'); $this->assertSession()->pageTextContains('Unable to save this Moderated content.'); $this->drupalGet($latest_version_path, ['language' => $french]); @@ -354,33 +360,35 @@ public function testContentTranslationNodeForm() { // We should be able to publish the english pending revision (revision 7) $this->drupalGet($edit_path); - $this->assertTrue($this->xpath('//input[@value="Save and Create New Draft (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Publish (this translation)"]')); - $this->assertFalse($this->xpath('//input[@value="Save and Archive (this translation)"]')); + $this->assertSession()->optionExists('moderation_state[0][state]', 'draft'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'published'); + $this->assertSession()->optionNotExists('moderation_state[0][state]', 'archived'); $this->drupalPostForm(NULL, [ 'body[0][value]' => 'Eighth version of the content.', - ], t('Save and Publish (this translation)')); + 'moderation_state[0][state]' => 'published', + ], t('Save (this translation)')); $this->drupalGet($latest_version_path); $this->assertFalse($this->xpath('//ul[@class="entity-moderation-form"]')); // Make sure we're allowed to create a pending french revision. $this->drupalGet($edit_path, ['language' => $french]); - $this->assertTrue($this->xpath('//input[@value="Save and Create New Draft (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Publish (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Archive (this translation)"]')); + $this->assertSession()->optionExists('moderation_state[0][state]', 'draft'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'published'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'archived'); // Make sure we're allowed to create a pending english revision. $this->drupalGet($edit_path); - $this->assertTrue($this->xpath('//input[@value="Save and Create New Draft (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Publish (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Archive (this translation)"]')); + $this->assertSession()->optionExists('moderation_state[0][state]', 'draft'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'published'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'archived'); // Create new moderated content. (revision 1). $this->drupalPostForm('node/add/moderated_content', [ 'title[0][value]' => 'Second moderated content', 'body[0][value]' => 'First version of the content.', - ], t('Save and Publish')); + 'moderation_state[0][state]' => 'published', + ], t('Save')); $node = $this->drupalGetNodeByTitle('Second moderated content'); $this->assertTrue($node->language(), 'en'); @@ -389,24 +397,24 @@ public function testContentTranslationNodeForm() { // Add a pending revision (revision 2). $this->drupalGet($edit_path); - $this->assertTrue($this->xpath('//input[@value="Save and Create New Draft (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Publish (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Archive (this translation)"]')); + $this->assertSession()->optionExists('moderation_state[0][state]', 'draft'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'published'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'archived'); $this->drupalPostForm(NULL, [ 'body[0][value]' => 'Second version of the content.', - ], t('Save and Create New Draft (this translation)')); + 'moderation_state[0][state]' => 'draft', + ], t('Save')); // It shouldn't be possible to translate as we have a pending revision. $this->drupalGet($translate_path); - $this->assertFalse($this->xpath('//input[@value="Save and Create New Draft (this translation)"]')); - $this->assertFalse($this->xpath('//input[@value="Save and Publish (this translation)"]')); - $this->assertFalse($this->xpath('//input[@value="Save and Archive (this translation)"]')); + $this->assertSession()->fieldNotExists('moderation_state[0][state]'); $this->assertSession()->pageTextContains('Unable to save this Moderated content.'); // Create new moderated content (revision 1). $this->drupalPostForm('node/add/moderated_content', [ 'title[0][value]' => 'Third moderated content', - ], t('Save and Publish')); + 'moderation_state[0][state]' => 'published', + ], t('Save')); $node = $this->drupalGetNodeByTitle('Third moderated content'); $this->assertTrue($node->language(), 'en'); @@ -415,38 +423,44 @@ public function testContentTranslationNodeForm() { // Translate it, without updating data (revision 2). $this->drupalGet($translate_path); - $this->assertTrue($this->xpath('//input[@value="Save and Create New Draft (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Publish (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Archive (this translation)"]')); - $this->drupalPostForm(NULL, [], t('Save and Create New Draft (this translation)')); + $this->assertSession()->optionExists('moderation_state[0][state]', 'draft'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'published'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'archived'); + $this->drupalPostForm(NULL, [ + 'moderation_state[0][state]' => 'draft', + ], t('Save (this translation)')); // Add another draft for the translation (revision 3). $this->drupalGet($edit_path, ['language' => $french]); - $this->assertTrue($this->xpath('//input[@value="Save and Create New Draft (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Publish (this translation)"]')); - $this->assertFalse($this->xpath('//input[@value="Save and Archive (this translation)"]')); - $this->drupalPostForm(NULL, [], t('Save and Create New Draft (this translation)')); + $this->assertSession()->optionExists('moderation_state[0][state]', 'draft'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'published'); + $this->assertSession()->optionNotExists('moderation_state[0][state]', 'archived'); + $this->drupalPostForm(NULL, [ + 'moderation_state[0][state]' => 'draft', + ], t('Save (this translation)')); // Editing the original translation should not be possible. $this->drupalGet($edit_path); - $this->assertFalse($this->xpath('//input[@value="Save and Create New Draft (this translation)"]')); - $this->assertFalse($this->xpath('//input[@value="Save and Publish (this translation)"]')); - $this->assertFalse($this->xpath('//input[@value="Save and Archive (this translation)"]')); + $this->assertSession()->fieldNotExists('moderation_state[0][state]'); $this->assertSession()->pageTextContains('Unable to save this Moderated content.'); // Updating and publishing the french translation is still possible. $this->drupalGet($edit_path, ['language' => $french]); - $this->assertTrue($this->xpath('//input[@value="Save and Create New Draft (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Publish (this translation)"]')); - $this->assertFalse($this->xpath('//input[@value="Save and Archive (this translation)"]')); - $this->drupalPostForm(NULL, [], t('Save and Publish (this translation)')); + $this->assertSession()->optionExists('moderation_state[0][state]', 'draft'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'published'); + $this->assertSession()->optionNotExists('moderation_state[0][state]', 'archived'); + $this->drupalPostForm(NULL, [ + 'moderation_state[0][state]' => 'published', + ], t('Save (this translation)')); // Now the french translation is published, an english draft can be added. $this->drupalGet($edit_path); - $this->assertTrue($this->xpath('//input[@value="Save and Create New Draft (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Publish (this translation)"]')); - $this->assertTrue($this->xpath('//input[@value="Save and Archive (this translation)"]')); - $this->drupalPostForm(NULL, [], t('Save and Create New Draft (this translation)')); + $this->assertSession()->optionExists('moderation_state[0][state]', 'draft'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'published'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'archived'); + $this->drupalPostForm(NULL, [ + 'moderation_state[0][state]' => 'draft', + ], t('Save (this translation)')); } /** @@ -477,7 +491,8 @@ public function testWorkflowInUse() { $this->drupalPostForm('node/add/moderated_content', [ 'title[0][value]' => 'Some moderated content', 'body[0][value]' => 'First version of the content.', - ], 'Save and Create New Draft'); + 'moderation_state[0][state]' => 'draft', + ], 'Save'); // The archived state is not used yet, so can still be deleted. $this->drupalGet($paths['archived_state']); @@ -488,8 +503,12 @@ public function testWorkflowInUse() { $this->assertSession()->buttonNotExists('Delete'); $node = $this->drupalGetNodeByTitle('Some moderated content'); - $this->drupalPostForm('node/' . $node->id() . '/edit', [], 'Save and Publish'); - $this->drupalPostForm('node/' . $node->id() . '/edit', [], 'Save and Archive'); + $this->drupalPostForm('node/' . $node->id() . '/edit', [ + 'moderation_state[0][state]' => 'published', + ], 'Save'); + $this->drupalPostForm('node/' . $node->id() . '/edit', [ + 'moderation_state[0][state]' => 'archived', + ], 'Save'); // Now the archived state is being used so it can not be deleted either. foreach ($paths as $path) { diff --git a/core/modules/content_moderation/tests/src/Functional/ModerationLocaleTest.php b/core/modules/content_moderation/tests/src/Functional/ModerationLocaleTest.php index 8516e87871ab..3104450fcef4 100644 --- a/core/modules/content_moderation/tests/src/Functional/ModerationLocaleTest.php +++ b/core/modules/content_moderation/tests/src/Functional/ModerationLocaleTest.php @@ -53,8 +53,9 @@ public function testTranslateModeratedContent() { $edit = [ 'title[0][value]' => 'Published English node', 'langcode[0][value]' => 'en', + 'moderation_state[0][state]' => 'published', ]; - $this->drupalPostForm('node/add/article', $edit, t('Save and Publish')); + $this->drupalPostForm('node/add/article', $edit, t('Save')); $this->assertText(t('Article Published English node has been created.')); $english_node = $this->drupalGetNodeByTitle('Published English node'); @@ -63,8 +64,9 @@ public function testTranslateModeratedContent() { $this->clickLink(t('Add')); $edit = [ 'title[0][value]' => 'French node Draft', + 'moderation_state[0][state]' => 'draft', ]; - $this->drupalPostForm(NULL, $edit, t('Save and Create New Draft (this translation)')); + $this->drupalPostForm(NULL, $edit, t('Save (this translation)')); // Here the error has occurred "The website encountered an unexpected error. // Please try again later." // If the translation has got lost. @@ -74,8 +76,9 @@ public function testTranslateModeratedContent() { $edit = [ 'title[0][value]' => 'English node', 'langcode[0][value]' => 'en', + 'moderation_state[0][state]' => 'draft', ]; - $this->drupalPostForm('node/add/article', $edit, t('Save and Create New Draft')); + $this->drupalPostForm('node/add/article', $edit, t('Save')); $this->assertText(t('Article English node has been created.')); $english_node = $this->drupalGetNodeByTitle('English node'); @@ -84,14 +87,17 @@ public function testTranslateModeratedContent() { $this->clickLink(t('Add')); $edit = [ 'title[0][value]' => 'French node', + 'moderation_state[0][state]' => 'draft', ]; - $this->drupalPostForm(NULL, $edit, t('Save and Create New Draft (this translation)')); + $this->drupalPostForm(NULL, $edit, t('Save (this translation)')); $this->assertText(t('Article French node has been updated.')); $english_node = $this->drupalGetNodeByTitle('English node', TRUE); // Publish the English article and check that the translation stays // unpublished. - $this->drupalPostForm('node/' . $english_node->id() . '/edit', [], t('Save and Publish (this translation)')); + $this->drupalPostForm('node/' . $english_node->id() . '/edit', [ + 'moderation_state[0][state]' => 'published', + ], t('Save (this translation)')); $this->assertText(t('Article English node has been updated.')); $english_node = $this->drupalGetNodeByTitle('English node', TRUE); $french_node = $english_node->getTranslation('fr'); @@ -106,8 +112,9 @@ public function testTranslateModeratedContent() { // the translation first. $edit = [ 'title[0][value]' => 'Another node', + 'moderation_state[0][state]' => 'draft', ]; - $this->drupalPostForm('node/add/article', $edit, t('Save and Create New Draft')); + $this->drupalPostForm('node/add/article', $edit, t('Save')); $this->assertText(t('Article Another node has been created.')); $english_node = $this->drupalGetNodeByTitle('Another node'); @@ -116,14 +123,17 @@ public function testTranslateModeratedContent() { $this->clickLink(t('Add')); $edit = [ 'title[0][value]' => 'Translated node', + 'moderation_state[0][state]' => 'draft', ]; - $this->drupalPostForm(NULL, $edit, t('Save and Create New Draft (this translation)')); + $this->drupalPostForm(NULL, $edit, t('Save (this translation)')); $this->assertText(t('Article Translated node has been updated.')); $english_node = $this->drupalGetNodeByTitle('Another node', TRUE); // Publish the translation and check that the source language version stays // unpublished. - $this->drupalPostForm('fr/node/' . $english_node->id() . '/edit', [], t('Save and Publish (this translation)')); + $this->drupalPostForm('fr/node/' . $english_node->id() . '/edit', [ + 'moderation_state[0][state]' => 'published', + ], t('Save (this translation)')); $this->assertText(t('Article Translated node has been updated.')); $english_node = $this->drupalGetNodeByTitle('Another node', TRUE); $french_node = $english_node->getTranslation('fr'); @@ -135,8 +145,9 @@ public function testTranslateModeratedContent() { // Now check that we can create a new draft of the translation. $edit = [ 'title[0][value]' => 'New draft of translated node', + 'moderation_state[0][state]' => 'draft', ]; - $this->drupalPostForm('fr/node/' . $english_node->id() . '/edit', $edit, t('Save and Create New Draft (this translation)')); + $this->drupalPostForm('fr/node/' . $english_node->id() . '/edit', $edit, t('Save (this translation)')); $this->assertText(t('Article New draft of translated node has been updated.')); $english_node = $this->drupalGetNodeByTitle('Another node', TRUE); $french_node = $english_node->getTranslation('fr'); @@ -145,7 +156,9 @@ public function testTranslateModeratedContent() { $this->assertEqual($french_node->getTitle(), 'Translated node', 'The default revision of the published translation remains the same.'); // Publish the French article before testing the archive transition. - $this->drupalPostForm('fr/node/' . $english_node->id() . '/edit', [], t('Save and Publish (this translation)')); + $this->drupalPostForm('fr/node/' . $english_node->id() . '/edit', [ + 'moderation_state[0][state]' => 'published', + ], t('Save (this translation)')); $this->assertText(t('Article New draft of translated node has been updated.')); $english_node = $this->drupalGetNodeByTitle('Another node', TRUE); $french_node = $english_node->getTranslation('fr'); @@ -154,15 +167,21 @@ public function testTranslateModeratedContent() { $this->assertEqual($french_node->getTitle(), 'New draft of translated node', 'The draft has replaced the published revision.'); // Publish the English article before testing the archive transition. - $this->drupalPostForm('node/' . $english_node->id() . '/edit', [], t('Save and Publish (this translation)')); + $this->drupalPostForm('node/' . $english_node->id() . '/edit', [ + 'moderation_state[0][state]' => 'published', + ], t('Save (this translation)')); $this->assertText(t('Article Another node has been updated.')); $english_node = $this->drupalGetNodeByTitle('Another node', TRUE); $this->assertEqual($english_node->moderation_state->value, 'published'); // Archive the node and its translation. - $this->drupalPostForm('node/' . $english_node->id() . '/edit', [], t('Save and Archive (this translation)')); + $this->drupalPostForm('node/' . $english_node->id() . '/edit', [ + 'moderation_state[0][state]' => 'archived', + ], t('Save (this translation)')); $this->assertText(t('Article Another node has been updated.')); - $this->drupalPostForm('fr/node/' . $english_node->id() . '/edit', [], t('Save and Archive (this translation)')); + $this->drupalPostForm('fr/node/' . $english_node->id() . '/edit', [ + 'moderation_state[0][state]' => 'archived', + ], t('Save (this translation)')); $this->assertText(t('Article New draft of translated node has been updated.')); $english_node = $this->drupalGetNodeByTitle('Another node', TRUE); $french_node = $english_node->getTranslation('fr'); diff --git a/core/modules/content_moderation/tests/src/Functional/ModerationRevisionRevertTest.php b/core/modules/content_moderation/tests/src/Functional/ModerationRevisionRevertTest.php index 7dac15667fbd..c054d9d92ed5 100644 --- a/core/modules/content_moderation/tests/src/Functional/ModerationRevisionRevertTest.php +++ b/core/modules/content_moderation/tests/src/Functional/ModerationRevisionRevertTest.php @@ -55,10 +55,16 @@ public function setUp() { */ public function testEditingAfterRevertRevision() { // Create a draft. - $this->drupalPostForm('node/add/moderated_bundle', ['title[0][value]' => 'First draft node'], t('Save and Create New Draft')); + $this->drupalPostForm('node/add/moderated_bundle', [ + 'title[0][value]' => 'First draft node', + 'moderation_state[0][state]' => 'draft', + ], t('Save')); // Now make it published. - $this->drupalPostForm('node/1/edit', ['title[0][value]' => 'Published node'], t('Save and Publish')); + $this->drupalPostForm('node/1/edit', [ + 'title[0][value]' => 'Published node', + 'moderation_state[0][state]' => 'published', + ], t('Save')); // Check the editing form that show the published title. $this->drupalGet('node/1/edit'); @@ -76,7 +82,9 @@ public function testEditingAfterRevertRevision() { $this->assertSession() ->pageTextContains('First draft node'); // Try to save the node. - $this->click('.moderation-state-draft > input'); + $this->drupalPostForm('node/1/edit', [ + 'moderation_state[0][state]' => 'draft', + ], t('Save')); // Check if the submission passed the EntityChangedConstraintValidator. $this->assertSession() diff --git a/core/modules/content_moderation/tests/src/Functional/ModerationStateBlockTest.php b/core/modules/content_moderation/tests/src/Functional/ModerationStateBlockTest.php index ee87e1384cdd..bfa70fcc75ce 100644 --- a/core/modules/content_moderation/tests/src/Functional/ModerationStateBlockTest.php +++ b/core/modules/content_moderation/tests/src/Functional/ModerationStateBlockTest.php @@ -59,9 +59,10 @@ public function testCustomBlockModeration() { $body = 'Body of moderated block'; $edit = [ 'info[0][value]' => 'Moderated block', + 'moderation_state[0][state]' => 'draft', 'body[0][value]' => $body, ]; - $this->drupalPostForm('block/add', $edit, t('Save and Create New Draft')); + $this->drupalPostForm('block/add', $edit, t('Save')); $this->assertText(t('basic Moderated block has been created.')); // Place the block in the Sidebar First region. @@ -83,8 +84,9 @@ public function testCustomBlockModeration() { $updated_body = 'This is the new body value'; $edit = [ 'body[0][value]' => $updated_body, + 'moderation_state[0][state]' => 'draft', ]; - $this->drupalPostForm('block/' . $block->id(), $edit, t('Save and Create New Draft')); + $this->drupalPostForm('block/' . $block->id(), $edit, t('Save')); $this->assertText(t('basic Moderated block has been updated.')); // Navigate to the home page and check that the block shows the updated @@ -94,14 +96,17 @@ public function testCustomBlockModeration() { $this->assertText($updated_body); // Publish the block so we can create a pending revision. - $this->drupalPostForm('block/' . $block->id(), [], t('Save and Publish')); + $this->drupalPostForm('block/' . $block->id(), [ + 'moderation_state[0][state]' => 'published', + ], t('Save')); // Create a pending revision. $pending_revision_body = 'This is the pending revision body value'; $edit = [ 'body[0][value]' => $pending_revision_body, + 'moderation_state[0][state]' => 'draft', ]; - $this->drupalPostForm('block/' . $block->id(), $edit, t('Save and Create New Draft')); + $this->drupalPostForm('block/' . $block->id(), $edit, t('Save')); $this->assertText(t('basic Moderated block has been updated.')); // Navigate to home page and check that the pending revision doesn't show, diff --git a/core/modules/content_moderation/tests/src/Functional/ModerationStateNodeTest.php b/core/modules/content_moderation/tests/src/Functional/ModerationStateNodeTest.php index bf8e29364ee3..11deaa72c061 100644 --- a/core/modules/content_moderation/tests/src/Functional/ModerationStateNodeTest.php +++ b/core/modules/content_moderation/tests/src/Functional/ModerationStateNodeTest.php @@ -28,7 +28,8 @@ protected function setUp() { public function testCreatingContent() { $this->drupalPostForm('node/add/moderated_content', [ 'title[0][value]' => 'moderated content', - ], t('Save and Create New Draft')); + 'moderation_state[0][state]' => 'draft', + ], t('Save')); $node = $this->getNodeByTitle('moderated content'); if (!$node) { $this->fail('Test node was not saved correctly.'); @@ -37,7 +38,9 @@ public function testCreatingContent() { $path = 'node/' . $node->id() . '/edit'; // Set up published revision. - $this->drupalPostForm($path, [], t('Save and Publish')); + $this->drupalPostForm($path, [ + 'moderation_state[0][state]' => 'published', + ], t('Save')); \Drupal::entityTypeManager()->getStorage('node')->resetCache([$node->id()]); /* @var \Drupal\node\NodeInterface $node */ $node = \Drupal::entityTypeManager()->getStorage('node')->load($node->id()); @@ -79,7 +82,8 @@ public function testFormSaveDestination() { $this->drupalPostForm('node/add/moderated_content', [ 'title[0][value]' => 'Some moderated content', 'body[0][value]' => 'First version of the content.', - ], t('Save and Create New Draft')); + 'moderation_state[0][state]' => 'draft', + ], t('Save')); $node = $this->drupalGetNodeByTitle('Some moderated content'); $edit_path = sprintf('node/%d/edit', $node->id()); @@ -93,7 +97,8 @@ public function testFormSaveDestination() { // URL, but viewing the second revision. $this->drupalPostForm($edit_path, [ 'body[0][value]' => 'Second version of the content.', - ], t('Save and Create New Draft')); + 'moderation_state[0][state]' => 'draft', + ], t('Save')); $this->assertUrl(Url::fromRoute('entity.node.canonical', ['node' => $node->id()])); $this->assertText('Second version of the content.'); @@ -101,7 +106,8 @@ public function testFormSaveDestination() { // canonical URL. $this->drupalPostForm($edit_path, [ 'body[0][value]' => 'Third version of the content.', - ], t('Save and Publish')); + 'moderation_state[0][state]' => 'published', + ], t('Save')); $this->assertUrl(Url::fromRoute('entity.node.canonical', ['node' => $node->id()])); $this->assertText('Third version of the content.'); @@ -109,7 +115,8 @@ public function testFormSaveDestination() { // version" tab. $this->drupalPostForm($edit_path, [ 'body[0][value]' => 'Fourth version of the content.', - ], t('Save and Create New Draft')); + 'moderation_state[0][state]' => 'draft', + ], t('Save')); $this->assertUrl(Url::fromRoute('entity.node.latest_version', ['node' => $node->id()])); $this->assertText('Fourth version of the content.'); } @@ -137,4 +144,46 @@ public function testPagers() { $this->assertEqual(0, $query['page']); } + /** + * Tests the workflow when a user has no Content Moderation permissions. + */ + public function testNoContentModerationPermissions() { + $session_assert = $this->assertSession(); + + // Create a user with quite advanced node permissions but no content + // moderation permissions. + $limited_user = $this->createUser([ + 'administer nodes', + 'bypass node access', + ]); + $this->drupalLogin($limited_user); + + // Check the user can add content, but can't see the moderation state + // select. + $this->drupalGet('node/add/moderated_content'); + $session_assert->statusCodeEquals(200); + $session_assert->fieldNotExists('moderation_state[0][state]'); + $this->drupalPostForm(NULL, [ + 'title[0][value]' => 'moderated content', + ], 'Save'); + + // Manually move the content to archived because the user doesn't have + // permission to do this. + $node = $this->getNodeByTitle('moderated content'); + $node->moderation_state->value = 'archived'; + $node->save(); + + // Check the user can see the current state but not the select. + $this->drupalGet('node/' . $node->id() . '/edit'); + $session_assert->statusCodeEquals(200); + $session_assert->pageTextContains('Archived'); + $session_assert->fieldNotExists('moderation_state[0][state]'); + $this->drupalPostForm(NULL, [], 'Save'); + + // When saving they should still be on the edit form, and see the validation + // error message. + $session_assert->pageTextContains('Edit Moderated content moderated content'); + $session_assert->pageTextContains('Invalid state transition from Archived to Archived'); + } + } diff --git a/core/modules/content_moderation/tests/src/Functional/ModerationStateNodeTypeTest.php b/core/modules/content_moderation/tests/src/Functional/ModerationStateNodeTypeTest.php index 055512095f5d..afe188f3e117 100644 --- a/core/modules/content_moderation/tests/src/Functional/ModerationStateNodeTypeTest.php +++ b/core/modules/content_moderation/tests/src/Functional/ModerationStateNodeTypeTest.php @@ -72,14 +72,14 @@ public function testEnablingOnExistingContent() { $this->assertLinkByHref('node/' . $node->id() . '/edit'); $this->drupalGet('node/' . $node->id() . '/edit'); $this->assertResponse(200); - $this->assertRaw('Save and Create New Draft'); - $this->assertNoRaw('Save and Publish'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'draft'); + $this->assertSession()->optionNotExists('moderation_state[0][state]', 'published'); $this->drupalLogin($editor_with_publish); $this->drupalGet('node/' . $node->id() . '/edit'); $this->assertResponse(200); - $this->assertRaw('Save and Create New Draft'); - $this->assertRaw('Save and Publish'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'draft'); + $this->assertSession()->optionExists('moderation_state[0][state]', 'published'); } } diff --git a/core/modules/content_moderation/tests/src/Functional/ModerationStateTestBase.php b/core/modules/content_moderation/tests/src/Functional/ModerationStateTestBase.php index 8a3a2d0cea01..fe3c1d1c7fda 100644 --- a/core/modules/content_moderation/tests/src/Functional/ModerationStateTestBase.php +++ b/core/modules/content_moderation/tests/src/Functional/ModerationStateTestBase.php @@ -38,6 +38,9 @@ abstract class ModerationStateTestBase extends BrowserTestBase { 'access content overview', 'use editorial transition create_new_draft', 'use editorial transition publish', + 'use editorial transition archive', + 'use editorial transition archived_draft', + 'use editorial transition archived_published', ]; /** diff --git a/core/modules/content_moderation/tests/src/Functional/NodeAccessTest.php b/core/modules/content_moderation/tests/src/Functional/NodeAccessTest.php index ed502b16afcf..ea79c2d0558c 100644 --- a/core/modules/content_moderation/tests/src/Functional/NodeAccessTest.php +++ b/core/modules/content_moderation/tests/src/Functional/NodeAccessTest.php @@ -75,7 +75,8 @@ public function testPageAccess() { // Create a node to test with. $this->drupalPostForm(NULL, [ 'title[0][value]' => 'moderated content', - ], t('Save and Create New Draft')); + 'moderation_state[0][state]' => 'draft', + ], t('Save')); $node = $this->getNodeByTitle('moderated content'); if (!$node) { $this->fail('Test node was not saved correctly.'); @@ -103,7 +104,9 @@ public function testPageAccess() { // Publish the node. $this->drupalLogin($this->adminUser); - $this->drupalPostForm($edit_path, [], t('Save and Publish')); + $this->drupalPostForm($edit_path, [ + 'moderation_state[0][state]' => 'published', + ], t('Save')); // Ensure access works correctly for anonymous users. $this->drupalLogout(); @@ -120,7 +123,8 @@ public function testPageAccess() { $this->drupalLogin($this->adminUser); $this->drupalPostForm($edit_path, [ 'title[0][value]' => 'moderated content revised', - ], t('Save and Create New Draft')); + 'moderation_state[0][state]' => 'draft', + ], t('Save')); $this->drupalLogin($user); diff --git a/core/modules/menu_ui/tests/src/Functional/MenuUiContentModerationTest.php b/core/modules/menu_ui/tests/src/Functional/MenuUiContentModerationTest.php index 93b0fa7c7e34..be018f4ce113 100644 --- a/core/modules/menu_ui/tests/src/Functional/MenuUiContentModerationTest.php +++ b/core/modules/menu_ui/tests/src/Functional/MenuUiContentModerationTest.php @@ -59,19 +59,21 @@ public function testMenuUiWithPendingRevisions() { $node = $this->drupalCreateNode(); // Publish the node with no changes. - $this->drupalPostForm('node/' . $node->id() . '/edit', [], t('Save and Publish')); + $this->drupalPostForm('node/' . $node->id() . '/edit', [], t('Save')); $this->assertSession()->responseContains(t('Page %label has been updated.', ['%label' => $node->toLink($node->label())->toString()])); // Create a pending revision with no changes. - $this->drupalPostForm('node/' . $node->id() . '/edit', [], t('Save and Create New Draft')); + $edit = ['moderation_state[0][state]' => 'draft']; + $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save')); $this->assertSession()->responseContains(t('Page %label has been updated.', ['%label' => $node->toLink($node->label())->toString()])); // Add a menu link and save a new default (published) revision. $edit = [ 'menu[enabled]' => 1, 'menu[title]' => 'Test menu link', + 'moderation_state[0][state]' => 'published', ]; - $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and Publish')); + $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save')); $this->assertSession()->linkExists('Test menu link'); @@ -79,8 +81,9 @@ public function testMenuUiWithPendingRevisions() { // revision. $edit = [ 'menu[title]' => 'Test menu link draft', + 'moderation_state[0][state]' => 'draft', ]; - $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and Create New Draft')); + $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save')); // Check that the menu settings were not applied. $this->assertSession()->pageTextContains('You can only change the menu settings for the published version of this content.'); @@ -91,8 +94,9 @@ public function testMenuUiWithPendingRevisions() { // (draft) revision. $edit = [ 'menu[description]' => 'Test menu link description', + 'moderation_state[0][state]' => 'draft', ]; - $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and Create New Draft')); + $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save')); // Check that the menu settings were not applied. $this->assertSession()->pageTextContains('You can only change the menu settings for the published version of this content.'); @@ -101,8 +105,9 @@ public function testMenuUiWithPendingRevisions() { // revision. $edit = [ 'menu[weight]' => 1, + 'moderation_state[0][state]' => 'draft', ]; - $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and Create New Draft')); + $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save')); // Check that the menu settings were not applied. $this->assertSession()->pageTextContains('You can only change the menu settings for the published version of this content.'); @@ -111,8 +116,9 @@ public function testMenuUiWithPendingRevisions() { // revision. $edit = [ 'menu[menu_parent]' => 'main:test_page_test.front_page', + 'moderation_state[0][state]' => 'draft', ]; - $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and Create New Draft')); + $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save')); // Check that the menu settings were not applied. $this->assertSession()->pageTextContains('You can only change the menu settings for the published version of this content.'); @@ -120,8 +126,9 @@ public function testMenuUiWithPendingRevisions() { // Try to delete the menu link and save a new non-default (draft) revision. $edit = [ 'menu[enabled]' => 0, + 'moderation_state[0][state]' => 'draft', ]; - $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and Create New Draft')); + $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save')); // Check that the menu settings were not applied. $this->assertSession()->pageTextContains('You can only change the menu settings for the published version of this content.'); @@ -129,7 +136,8 @@ public function testMenuUiWithPendingRevisions() { // Try to save a new non-default (draft) revision without any changes and // check that the error message is not shown. - $this->drupalPostForm('node/' . $node->id() . '/edit', [], t('Save and Create New Draft')); + $edit = ['moderation_state[0][state]' => 'draft']; + $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save')); // Check that the menu settings were not applied. $this->assertSession()->pageTextNotContains('You can only change the menu settings for the published version of this content.'); @@ -139,15 +147,17 @@ public function testMenuUiWithPendingRevisions() { $node = $this->drupalCreateNode(); // Publish the node with no changes. - $this->drupalPostForm('node/' . $node->id() . '/edit', [], t('Save and Publish')); + $edit = ['moderation_state[0][state]' => 'published']; + $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save')); $this->assertSession()->responseContains(t('Page %label has been updated.', ['%label' => $node->toLink($node->label())->toString()])); // Add a menu link and save and create a new non-default (draft) revision. $edit = [ 'menu[enabled]' => 1, 'menu[title]' => 'Test menu link', + 'moderation_state[0][state]' => 'draft', ]; - $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and Create New Draft')); + $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save')); $this->assertSession()->pageTextContains('You can only change the menu settings for the published version of this content.'); } diff --git a/core/modules/path/tests/src/Functional/PathContentModerationTest.php b/core/modules/path/tests/src/Functional/PathContentModerationTest.php index c63c57332f7c..87f117b9aac6 100644 --- a/core/modules/path/tests/src/Functional/PathContentModerationTest.php +++ b/core/modules/path/tests/src/Functional/PathContentModerationTest.php @@ -49,7 +49,8 @@ public function testNodePathAlias() { $this->drupalPostForm(NULL, [ 'title[0][value]' => 'moderated content', 'path[0][alias]' => '/moderated-content', - ], t('Save and Publish')); + 'moderation_state[0][state]' => 'published', + ], t('Save')); $node = $this->getNodeByTitle('moderated content'); // Add a pending revision with the same alias. @@ -58,7 +59,8 @@ public function testNodePathAlias() { $this->drupalPostForm(NULL, [ 'title[0][value]' => 'pending revision', 'path[0][alias]' => '/moderated-content', - ], t('Save and Create New Draft')); + 'moderation_state[0][state]' => 'draft', + ], t('Save')); $this->assertSession()->pageTextNotContains('You can only change the URL alias for the published version of this content.'); // Create some moderated content with no path alias. @@ -67,7 +69,8 @@ public function testNodePathAlias() { $this->drupalPostForm(NULL, [ 'title[0][value]' => 'moderated content 2', 'path[0][alias]' => '', - ], t('Save and Publish')); + 'moderation_state[0][state]' => 'published', + ], t('Save')); $node = $this->getNodeByTitle('moderated content 2'); // Add a pending revision with a new alias. @@ -76,7 +79,8 @@ public function testNodePathAlias() { $this->drupalPostForm(NULL, [ 'title[0][value]' => 'pending revision', 'path[0][alias]' => '/pending-revision', - ], t('Save and Create New Draft')); + 'moderation_state[0][state]' => 'draft', + ], t('Save')); $this->assertSession()->pageTextContains('You can only change the URL alias for the published version of this content.'); // Create some moderated content with no path alias. @@ -85,7 +89,8 @@ public function testNodePathAlias() { $this->drupalPostForm(NULL, [ 'title[0][value]' => 'moderated content 3', 'path[0][alias]' => '', - ], t('Save and Publish')); + 'moderation_state[0][state]' => 'published', + ], t('Save')); $node = $this->getNodeByTitle('moderated content 3'); // Add a pending revision with no path alias. @@ -94,7 +99,8 @@ public function testNodePathAlias() { $this->drupalPostForm(NULL, [ 'title[0][value]' => 'pending revision', 'path[0][alias]' => '', - ], t('Save and Create New Draft')); + 'moderation_state[0][state]' => 'draft', + ], t('Save')); $this->assertSession()->pageTextNotContains('You can only change the URL alias for the published version of this content.'); } -- GitLab