Unverified Commit 4a1e8207 authored by Alex Pott's avatar Alex Pott
Browse files

Issue #2914873 by nick.james, chr.fritsch, Sam152, alexpott, timmillwood,...

Issue #2914873 by nick.james, chr.fritsch, Sam152, alexpott, timmillwood, jhedstrom: The "from state" used for calculating available transitions is changed when using the content moderation state widget and preview mode
parent 7cea08f1
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
@@ -240,4 +240,45 @@ public function getUnsupportedFeatures(EntityTypeInterface $entity_type) {
    return $features;
  }

  /**
   * {@inheritdoc}
   */
  public function getOriginalState(ContentEntityInterface $entity) {
    $state = NULL;
    $workflow_type = $this->getWorkflowForEntity($entity)->getTypePlugin();
    if (!$entity->isNew() && !$this->isFirstTimeModeration($entity)) {
      /** @var \Drupal\Core\Entity\ContentEntityInterface $original_entity */
      $original_entity = $this->entityTypeManager->getStorage($entity->getEntityTypeId())->loadRevision($entity->getLoadedRevisionId());
      if (!$entity->isDefaultTranslation() && $original_entity->hasTranslation($entity->language()->getId())) {
        $original_entity = $original_entity->getTranslation($entity->language()->getId());
      }
      if ($workflow_type->hasState($original_entity->moderation_state->value)) {
        $state = $workflow_type->getState($original_entity->moderation_state->value);
      }
    }
    return $state ?: $workflow_type->getInitialState($entity);
  }

  /**
   * Determines if this entity is being moderated for the first time.
   *
   * If the previous version of the entity has no moderation state, we assume
   * that means it predates the presence of moderation states.
   *
   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
   *   The entity being moderated.
   *
   * @return bool
   *   TRUE if this is the entity's first time being moderated, FALSE otherwise.
   */
  protected function isFirstTimeModeration(ContentEntityInterface $entity) {
    $original_entity = $this->getLatestRevision($entity->getEntityTypeId(), $entity->id());

    if ($original_entity) {
      $original_id = $original_entity->moderation_state;
    }

    return !($entity->moderation_state && $original_entity && $original_id);
  }

}
+22 −0
Original line number Diff line number Diff line
@@ -198,4 +198,26 @@ public function getWorkflowForEntityTypeAndBundle($entity_type_id, $bundle_id);
   */
  public function getUnsupportedFeatures(EntityTypeInterface $entity_type);

  /**
   * Gets the original or initial state of the given entity.
   *
   * When a state is being validated, the original state is used to validate
   * that a valid transition exists for target state and the user has access
   * to the transition between those two states. If the entity has been
   * moderated before, we can load the original unmodified revision and
   * translation for this state.
   *
   * If the entity is new we need to load the initial state from the workflow.
   * Even if a value was assigned to the moderation_state field, the initial
   * state is used to compute an appropriate transition for the purposes of
   * validation.
   *
   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
   *   The content entity to get the workflow for.
   *
   * @return \Drupal\content_moderation\ContentModerationState
   *   The original or default moderation state.
   */
  public function getOriginalState(ContentEntityInterface $entity);

}
+16 −5
Original line number Diff line number Diff line
@@ -118,13 +118,24 @@ public function form(FieldItemListInterface $items, array &$form, FormStateInter
   */
  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
    $entity = $items->getEntity();

    $workflow = $this->moderationInformation->getWorkflowForEntity($entity);
    $default = $items->get($delta)->value ? $workflow->getTypePlugin()->getState($items->get($delta)->value) : $workflow->getTypePlugin()->getInitialState($entity);
    $entity = $original_entity = $items->getEntity();

    $default = $this->moderationInformation->getOriginalState($entity);

    // If the entity is not new, grab the most recent revision and
    // load it. The moderation state of the saved revision will be used
    // to display the current state as well determine the the appropriate
    // transitions.
    if (!$entity->isNew()) {
      /** @var \Drupal\Core\Entity\ContentEntityInterface $original_entity */
      $original_entity = $this->entityTypeManager->getStorage($entity->getEntityTypeId())->loadRevision($entity->getLoadedRevisionId());
      if (!$entity->isDefaultTranslation() && $original_entity->hasTranslation($entity->language()->getId())) {
        $original_entity = $original_entity->getTranslation($entity->language()->getId());
      }
    }

    /** @var \Drupal\workflows\Transition[] $transitions */
    $transitions = $this->validator->getValidTransitions($entity, $this->currentUser);
    $transitions = $this->validator->getValidTransitions($original_entity, $this->currentUser);

    $transition_labels = [];
    $default_value = $items->value;
+1 −57
Original line number Diff line number Diff line
@@ -4,8 +4,6 @@

use Drupal\content_moderation\StateTransitionValidationInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\content_moderation\ModerationInformationInterface;
use Drupal\Core\Session\AccountInterface;
@@ -110,7 +108,7 @@ public function validate($value, Constraint $constraint) {
    }

    $new_state = $workflow->getTypePlugin()->getState($entity->moderation_state->value);
    $original_state = $this->getOriginalOrInitialState($entity);
    $original_state = $this->moderationInformation->getOriginalState($entity);

    // If a new state is being set and there is an existing state, validate
    // there is a valid transition between them.
@@ -132,58 +130,4 @@ public function validate($value, Constraint $constraint) {
    }
  }

  /**
   * Gets the original or initial state of the given entity.
   *
   * When a state is being validated, the original state is used to validate
   * that a valid transition exists for target state and the user has access
   * to the transition between those two states. If the entity has been
   * moderated before, we can load the original unmodified revision and
   * translation for this state.
   *
   * If the entity is new we need to load the initial state from the workflow.
   * Even if a value was assigned to the moderation_state field, the initial
   * state is used to compute an appropriate transition for the purposes of
   * validation.
   *
   * @return \Drupal\workflows\StateInterface
   *   The original or default moderation state.
   */
  protected function getOriginalOrInitialState(ContentEntityInterface $entity) {
    $state = NULL;
    $workflow_type = $this->moderationInformation->getWorkflowForEntity($entity)->getTypePlugin();
    if (!$entity->isNew() && !$this->isFirstTimeModeration($entity)) {
      $original_entity = $this->entityTypeManager->getStorage($entity->getEntityTypeId())->loadRevision($entity->getLoadedRevisionId());
      if (!$entity->isDefaultTranslation() && $original_entity->hasTranslation($entity->language()->getId())) {
        $original_entity = $original_entity->getTranslation($entity->language()->getId());
      }
      if ($workflow_type->hasState($original_entity->moderation_state->value)) {
        $state = $workflow_type->getState($original_entity->moderation_state->value);
      }
    }
    return $state ?: $workflow_type->getInitialState($entity);
  }

  /**
   * Determines if this entity is being moderated for the first time.
   *
   * If the previous version of the entity has no moderation state, we assume
   * that means it predates the presence of moderation states.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity being moderated.
   *
   * @return bool
   *   TRUE if this is the entity's first time being moderated, FALSE otherwise.
   */
  protected function isFirstTimeModeration(EntityInterface $entity) {
    $original_entity = $this->moderationInformation->getLatestRevision($entity->getEntityTypeId(), $entity->id());

    if ($original_entity) {
      $original_id = $original_entity->moderation_state;
    }

    return !($entity->moderation_state && $original_entity && $original_id);
  }

}
+19 −0
Original line number Diff line number Diff line
@@ -110,6 +110,15 @@ public function testModerationForm() {
    $this->drupalGet($edit_path);
    $this->assertFieldByName('moderation_state[0][state]', 'published', 'The moderation default value is set correctly.');

    // Preview the content while selecting the "draft" state and when the user
    // returns to the edit form, ensure all of the available transitions are
    // still those available from the "published" source state.
    $this->submitForm(['moderation_state[0][state]' => 'draft'], 'Preview');
    $this->clickLink('Back to content editing');
    $this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
    $this->assertSession()->optionExists('moderation_state[0][state]', 'published');
    $this->assertSession()->optionExists('moderation_state[0][state]', 'archived');

    // The published view should not have a moderation form, because it is the
    // live revision.
    $this->drupalGet($canonical_path);
@@ -306,6 +315,16 @@ public function testContentTranslationNodeForm() {
    $this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
    $this->assertSession()->optionExists('moderation_state[0][state]', 'published');
    $this->assertSession()->optionExists('moderation_state[0][state]', 'archived');

    // Preview the content while selecting the "draft" state and when the user
    // returns to the edit form, ensure all of the available transitions are
    // still those available from the "published" source state.
    $this->submitForm(['moderation_state[0][state]' => 'draft'], 'Preview');
    $this->clickLink('Back to content editing');
    $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.',
      'moderation_state[0][state]' => 'draft',
Loading