Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / web / core / modules / content_moderation / src / Plugin / Validation / Constraint / ModerationStateConstraintValidator.php
index 4b879737dae519ed5fb2bb89cea0da772d2824bf..7894cadf752c21eded941e73389fbd00efdfe69b 100644 (file)
@@ -2,10 +2,14 @@
 
 namespace Drupal\content_moderation\Plugin\Validation\Constraint;
 
+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;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\NotNullConstraint;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\Validator\Constraint;
 use Symfony\Component\Validator\ConstraintValidator;
@@ -29,6 +33,20 @@ class ModerationStateConstraintValidator extends ConstraintValidator implements
    */
   protected $moderationInformation;
 
+  /**
+   * The current user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
+  /**
+   * The state transition validation service.
+   *
+   * @var \Drupal\content_moderation\StateTransitionValidationInterface
+   */
+  protected $stateTransitionValidation;
+
   /**
    * Creates a new ModerationStateConstraintValidator instance.
    *
@@ -36,10 +54,16 @@ class ModerationStateConstraintValidator extends ConstraintValidator implements
    *   The entity type manager.
    * @param \Drupal\content_moderation\ModerationInformationInterface $moderation_information
    *   The moderation information.
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   The current user.
+   * @param \Drupal\content_moderation\StateTransitionValidationInterface $state_transition_validation
+   *   The state transition validation service.
    */
-  public function __construct(EntityTypeManagerInterface $entity_type_manager, ModerationInformationInterface $moderation_information) {
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, ModerationInformationInterface $moderation_information, AccountInterface $current_user, StateTransitionValidationInterface $state_transition_validation) {
     $this->entityTypeManager = $entity_type_manager;
     $this->moderationInformation = $moderation_information;
+    $this->currentUser = $current_user;
+    $this->stateTransitionValidation = $state_transition_validation;
   }
 
   /**
@@ -48,7 +72,9 @@ class ModerationStateConstraintValidator extends ConstraintValidator implements
   public static function create(ContainerInterface $container) {
     return new static(
       $container->get('entity_type.manager'),
-      $container->get('content_moderation.moderation_information')
+      $container->get('content_moderation.moderation_information'),
+      $container->get('current_user'),
+      $container->get('content_moderation.state_transition_validation')
     );
   }
 
@@ -64,6 +90,13 @@ class ModerationStateConstraintValidator extends ConstraintValidator implements
       return;
     }
 
+    // If the entity is moderated and the item list is empty, ensure users see
+    // the same required message as typical NotNull constraints.
+    if ($value->isEmpty()) {
+      $this->context->addViolation((new NotNullConstraint())->message);
+      return;
+    }
+
     $workflow = $this->moderationInformation->getWorkflowForEntity($entity);
 
     if (!$workflow->getTypePlugin()->hasState($entity->moderation_state->value)) {
@@ -76,32 +109,59 @@ class ModerationStateConstraintValidator extends ConstraintValidator implements
       return;
     }
 
+    $new_state = $workflow->getTypePlugin()->getState($entity->moderation_state->value);
+    $original_state = $this->getOriginalOrInitialState($entity);
+
     // If a new state is being set and there is an existing state, validate
     // there is a valid transition between them.
+    if (!$original_state->canTransitionTo($new_state->id())) {
+      $this->context->addViolation($constraint->message, [
+        '%from' => $original_state->label(),
+        '%to' => $new_state->label(),
+      ]);
+    }
+    else {
+      // If we're sure the transition exists, make sure the user has permission
+      // to use it.
+      if (!$this->stateTransitionValidation->isTransitionValid($workflow, $original_state, $new_state, $this->currentUser)) {
+        $this->context->addViolation($constraint->invalidTransitionAccess, [
+          '%original_state' => $original_state->label(),
+          '%new_state' => $new_state->label(),
+        ]);
+      }
+    }
+  }
+
+  /**
+   * 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 the state of the original entity doesn't exist on the workflow,
-      // we cannot do any further validation of transitions, because none will
-      // be setup for a state that doesn't exist. Instead allow any state to
-      // take its place.
-      if (!$workflow->getTypePlugin()->hasState($original_entity->moderation_state->value)) {
-        return;
-      }
-
-      $new_state = $workflow->getTypePlugin()->getState($entity->moderation_state->value);
-      $original_state = $workflow->getTypePlugin()->getState($original_entity->moderation_state->value);
-
-      if (!$original_state->canTransitionTo($new_state->id())) {
-        $this->context->addViolation($constraint->message, [
-          '%from' => $original_state->label(),
-          '%to' => $new_state->label()
-        ]);
+      if ($workflow_type->hasState($original_entity->moderation_state->value)) {
+        $state = $workflow_type->getState($original_entity->moderation_state->value);
       }
     }
+    return $state ?: $workflow_type->getInitialState($entity);
   }
 
   /**