diff --git a/core/modules/content_moderation/content_moderation.libraries.yml b/core/modules/content_moderation/content_moderation.libraries.yml index 6caaaf3..377667b 100644 --- a/core/modules/content_moderation/content_moderation.libraries.yml +++ b/core/modules/content_moderation/content_moderation.libraries.yml @@ -3,3 +3,8 @@ entity-moderation-form: css: layout: css/entity-moderation-form.css: {} +moderation-state-widget: + version: VERSION + css: + layout: + css/moderation-state-widget.css: {} diff --git a/core/modules/content_moderation/css/moderation-state-widget.css b/core/modules/content_moderation/css/moderation-state-widget.css new file mode 100644 index 0000000..edb21de --- /dev/null +++ b/core/modules/content_moderation/css/moderation-state-widget.css @@ -0,0 +1,6 @@ +.field--widget-moderation-state-default label { + display: inline; +} +.field--widget-moderation-state-default label:after { + content: ":"; +} 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 2290dfe..3a2bd45 100644 --- a/core/modules/content_moderation/src/Plugin/Field/FieldWidget/ModerationStateWidget.php +++ b/core/modules/content_moderation/src/Plugin/Field/FieldWidget/ModerationStateWidget.php @@ -2,9 +2,7 @@ namespace Drupal\content_moderation\Plugin\Field\FieldWidget; -use Drupal\Component\Utility\Html; use Drupal\content_moderation\Plugin\Field\ModerationStateFieldItemList; -use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldItemListInterface; @@ -108,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 */ @@ -119,43 +117,48 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen } $workflow = $this->moderationInformation->getWorkflowForEntity($entity); - $default = $items->get($delta)->value ? $workflow->getState($items->get($delta)->value) : $workflow->getInitialState(); - if (!$default) { + $current_state = $items->get($delta)->value ? $workflow->getState($items->get($delta)->value) : $workflow->getInitialState(); + if (!$current_state) { throw new \UnexpectedValueException(sprintf('The %s bundle has an invalid moderation state configuration, moderation states are enabled but no default is set.', $bundle_entity->label())); } /** @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->to()->label(); - $transition_labels[$transition->to()->id()] = $transition->label(); + $transition_to_state = $transition->to(); + if ($transition_to_state->id() === $current_state->id() && !$entity->isNew()) { + $default_value = $transition_to_state->id(); + // Add the default transition to the top of the list. + $transition_labels = [ + $transition_to_state->id() => $this->t('Keep @state', ['@state' => $transition_to_state->label()]) + ] + $transition_labels; + } + else { + $transition_labels[$transition_to_state->id()] = $transition->label(); + } } - $description_wrapper_id = Html::getUniqueId($this->fieldDefinition->getName() . '-state'); $element += [ '#type' => 'container', 'current' => [ - '#markup' => $this->t('Current moderation state: @current_state', ['@current_state' => $default->label()]), + '#type' => 'item', + '#title' => $this->t('Current state'), + '#markup' => $current_state->label(), + '#access' => !$entity->isNew(), ], 'state' => [ '#type' => 'select', - '#title' => $this->t('New state'), - '#options' => $target_states, - '#default_value' => isset($target_states[$default->id()]) ? $default->id() : NULL, - '#empty_option' => $this->t('- Select new state -'), + '#title' => $this->t('On save'), '#key_column' => $this->column, - '#transitions' => $transition_labels, - '#description' => isset($transition_labels[$default->id()]) ? $this->t('
@transition when saved.
', ['@id' => $description_wrapper_id, '@transition' => $transition_labels[$default->id()]]) : '
', - '#ajax' => [ - 'callback' => [get_class($this), 'change'], - 'event' => 'change', - 'wrapper' => $description_wrapper_id, - 'method' => 'html', - ] - ] + '#options' => $transition_labels, + '#default_value' => $default_value, + ], + '#attached' => [ + 'library' => ['content_moderation/moderation-state-widget'] + ], ]; $element['#element_validate'][] = [get_class($this), 'validateElement']; @@ -163,31 +166,9 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen } /** - * Ajax callback for the moderation state field description. - * - * @param array $element - * The form element. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The form state. - * - * @return array - * The render array to use for the field description. - */ - public static function change(array $element, FormStateInterface $form_state) { - $field_name = $form_state->getTriggeringElement()['#parents'][0]; - $state = $form_state->getValue($field_name)[0]['value']; - $transition_labels = $element[$field_name]['widget'][0]['state']['#transitions']; - return ['#plain_text' => isset($transition_labels[$state]) ? \Drupal::translation()->translate('@transition when saved.', ['@transition' => $transition_labels[$state]]) : '']; - } - - /** * {@inheritdoc} */ public static function validateElement(array $element, FormStateInterface $form_state) { - if (empty($element['state']['#value'])) { - $form_state->setError($element, \Drupal::translation()->translate('Please select a moderation state.')); - return; - } $form_state->setValueForElement($element, [$element['state']['#key_column'] => $element['state']['#value']]); } diff --git a/core/modules/content_moderation/src/Tests/ModerationFormTest.php b/core/modules/content_moderation/src/Tests/ModerationFormTest.php index 9522c78..1b7e133 100644 --- a/core/modules/content_moderation/src/Tests/ModerationFormTest.php +++ b/core/modules/content_moderation/src/Tests/ModerationFormTest.php @@ -2,6 +2,8 @@ namespace Drupal\content_moderation\Tests; +use Drupal\workflows\Entity\Workflow; + /** * Tests the moderation form, specifically on nodes. * @@ -31,7 +33,11 @@ protected function setUp() { */ public function testModerationForm() { // Create new moderated content in draft. - $this->drupalPostForm('node/add/moderated_content', [ + $this->drupalGet('node/add/moderated_content'); + // Since the node is being created there is no current state. + $this->assertNoText('Current state:'); + + $this->drupalPostForm(NULL, [ 'title[0][value]' => 'Some moderated content', 'body[0][value]' => 'First version of the content.', 'moderation_state[0][state]' => 'draft', @@ -49,18 +55,21 @@ public function testModerationForm() { $this->drupalGet($latest_version_path); $this->assertResponse(403); - // Try saving with no moderation state. + // Test what occurs when the current state is not available to transition + // to. + $workflow = Workflow::load('editorial'); + $workflow->setTransitionFromStates('create_new_draft', ['published'])->save(); $this->drupalGet($edit_path); + $this->assertText('Current state: Draft'); + $this->assertFieldsByValue($this->xpath('//select[@id="edit-moderation-state-0-state"]'), 'published'); + $workflow->setTransitionFromStates('create_new_draft', ['draft', 'published'])->save(); + $this->drupalGet($edit_path); + $this->assertText('Current state: Draft'); $this->assertFieldsByValue($this->xpath('//select[@id="edit-moderation-state-0-state"]'), 'draft'); - $this->drupalPostForm($edit_path, [ - 'body[0][value]' => 'Second version of the content.', - 'moderation_state[0][state]' => '', - ], t('Save')); - $this->assertText('Please select a moderation state.'); + $this->assertText("Keep Draft"); // Update the draft. $this->drupalGet($edit_path); - $this->assertFieldsByValue($this->xpath('//select[@id="edit-moderation-state-0-state"]'), 'draft'); $this->drupalPostForm($edit_path, [ 'body[0][value]' => 'Second version of the content.', 'moderation_state[0][state]' => 'draft', @@ -74,6 +83,7 @@ public function testModerationForm() { // Publish the draft. $this->drupalGet($edit_path); $this->assertFieldsByValue($this->xpath('//select[@id="edit-moderation-state-0-state"]'), 'draft'); + $this->assertText("Keep Draft"); $this->drupalPostForm($edit_path, [ 'body[0][value]' => 'Third version of the content.', 'moderation_state[0][state]' => 'published', @@ -82,6 +92,7 @@ public function testModerationForm() { // Archive the published entity. $this->drupalGet($edit_path); $this->assertFieldsByValue($this->xpath('//select[@id="edit-moderation-state-0-state"]'), 'published'); + $this->assertText("Keep Published"); $this->drupalPostForm($edit_path, [ 'body[0][value]' => 'Fourth version of the content.', 'moderation_state[0][state]' => 'archived', @@ -89,7 +100,7 @@ public function testModerationForm() { // Then back to Published so we can add a forward revision. $this->drupalGet($edit_path); - $this->assertFieldsByValue($this->xpath('//select[@id="edit-moderation-state-0-state"]'), ''); + $this->assertFieldsByValue($this->xpath('//select[@id="edit-moderation-state-0-state"]'), 'draft'); $this->drupalPostForm($edit_path, [ 'body[0][value]' => 'Fifth version of the content.', 'moderation_state[0][state]' => 'published',