3 namespace Drupal\content_moderation\Plugin\Validation\Constraint;
5 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
6 use Drupal\Core\Entity\EntityInterface;
7 use Drupal\Core\Entity\EntityTypeManagerInterface;
8 use Drupal\content_moderation\ModerationInformationInterface;
9 use Symfony\Component\DependencyInjection\ContainerInterface;
10 use Symfony\Component\Validator\Constraint;
11 use Symfony\Component\Validator\ConstraintValidator;
14 * Checks if a moderation state transition is valid.
16 class ModerationStateConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface {
19 * The entity type manager.
21 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
23 private $entityTypeManager;
26 * The moderation info.
28 * @var \Drupal\content_moderation\ModerationInformationInterface
30 protected $moderationInformation;
33 * Creates a new ModerationStateConstraintValidator instance.
35 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
36 * The entity type manager.
37 * @param \Drupal\content_moderation\ModerationInformationInterface $moderation_information
38 * The moderation information.
40 public function __construct(EntityTypeManagerInterface $entity_type_manager, ModerationInformationInterface $moderation_information) {
41 $this->entityTypeManager = $entity_type_manager;
42 $this->moderationInformation = $moderation_information;
48 public static function create(ContainerInterface $container) {
50 $container->get('entity_type.manager'),
51 $container->get('content_moderation.moderation_information')
58 public function validate($value, Constraint $constraint) {
59 /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
60 $entity = $value->getEntity();
62 // Ignore entities that are not subject to moderation anyway.
63 if (!$this->moderationInformation->isModeratedEntity($entity)) {
67 $workflow = $this->moderationInformation->getWorkflowForEntity($entity);
69 if (!$workflow->getTypePlugin()->hasState($entity->moderation_state->value)) {
70 // If the state we are transitioning to doesn't exist, we can't validate
71 // the transitions for this entity further.
72 $this->context->addViolation($constraint->invalidStateMessage, [
73 '%state' => $entity->moderation_state->value,
74 '%workflow' => $workflow->label(),
79 // If a new state is being set and there is an existing state, validate
80 // there is a valid transition between them.
81 if (!$entity->isNew() && !$this->isFirstTimeModeration($entity)) {
82 $original_entity = $this->entityTypeManager->getStorage($entity->getEntityTypeId())->loadRevision($entity->getLoadedRevisionId());
83 if (!$entity->isDefaultTranslation() && $original_entity->hasTranslation($entity->language()->getId())) {
84 $original_entity = $original_entity->getTranslation($entity->language()->getId());
87 // If the state of the original entity doesn't exist on the workflow,
88 // we cannot do any further validation of transitions, because none will
89 // be setup for a state that doesn't exist. Instead allow any state to
91 if (!$workflow->getTypePlugin()->hasState($original_entity->moderation_state->value)) {
95 $new_state = $workflow->getTypePlugin()->getState($entity->moderation_state->value);
96 $original_state = $workflow->getTypePlugin()->getState($original_entity->moderation_state->value);
98 if (!$original_state->canTransitionTo($new_state->id())) {
99 $this->context->addViolation($constraint->message, [
100 '%from' => $original_state->label(),
101 '%to' => $new_state->label()
108 * Determines if this entity is being moderated for the first time.
110 * If the previous version of the entity has no moderation state, we assume
111 * that means it predates the presence of moderation states.
113 * @param \Drupal\Core\Entity\EntityInterface $entity
114 * The entity being moderated.
117 * TRUE if this is the entity's first time being moderated, FALSE otherwise.
119 protected function isFirstTimeModeration(EntityInterface $entity) {
120 $original_entity = $this->moderationInformation->getLatestRevision($entity->getEntityTypeId(), $entity->id());
122 if ($original_entity) {
123 $original_id = $original_entity->moderation_state;
126 return !($entity->moderation_state && $original_entity && $original_id);