3 namespace Drupal\content_moderation\Plugin\Field;
5 use Drupal\Core\Entity\ContentEntityInterface;
6 use Drupal\Core\Entity\EntityPublishedInterface;
7 use Drupal\Core\Field\FieldItemList;
8 use Drupal\Core\TypedData\ComputedItemListTrait;
11 * A computed field that provides a content entity's moderation state.
13 * It links content entities to a moderation state configuration entity via a
14 * moderation state content entity.
16 class ModerationStateFieldItemList extends FieldItemList {
18 use ComputedItemListTrait {
25 protected function computeValue() {
26 $moderation_state = $this->getModerationStateId();
27 // Do not store NULL values, in the case where an entity does not have a
28 // moderation workflow associated with it, we do not create list items for
29 // the computed field.
30 if ($moderation_state) {
31 // An entity can only have a single moderation state.
32 $this->list[0] = $this->createItem(0, $moderation_state);
37 * Gets the moderation state ID linked to a content entity revision.
40 * The moderation state ID linked to a content entity revision.
42 protected function getModerationStateId() {
43 $entity = $this->getEntity();
45 /** @var \Drupal\content_moderation\ModerationInformationInterface $moderation_info */
46 $moderation_info = \Drupal::service('content_moderation.moderation_information');
47 if (!$moderation_info->shouldModerateEntitiesOfBundle($entity->getEntityType(), $entity->bundle())) {
51 // Existing entities will have a corresponding content_moderation_state
52 // entity associated with them.
53 if (!$entity->isNew() && $content_moderation_state = $this->loadContentModerationStateRevision($entity)) {
54 return $content_moderation_state->moderation_state->value;
57 // It is possible that the bundle does not exist at this point. For example,
58 // the node type form creates a fake Node entity to get default values.
59 // @see \Drupal\node\NodeTypeForm::form()
60 $workflow = $moderation_info->getWorkFlowForEntity($entity);
61 return $workflow ? $workflow->getTypePlugin()->getInitialState($entity)->id() : NULL;
65 * Load the content moderation state revision associated with an entity.
67 * @param \Drupal\Core\Entity\ContentEntityInterface $entity
68 * The entity the content moderation state entity will be loaded from.
70 * @return \Drupal\content_moderation\Entity\ContentModerationStateInterface|null
71 * The content_moderation_state revision or FALSE if none exists.
73 protected function loadContentModerationStateRevision(ContentEntityInterface $entity) {
74 $moderation_info = \Drupal::service('content_moderation.moderation_information');
75 $content_moderation_storage = \Drupal::entityTypeManager()->getStorage('content_moderation_state');
77 $revisions = $content_moderation_storage->getQuery()
78 ->condition('content_entity_type_id', $entity->getEntityTypeId())
79 ->condition('content_entity_id', $entity->id())
80 // Ensure the correct revision is loaded in scenarios where a revision is
82 ->condition('content_entity_revision_id', $entity->isNewRevision() ? $entity->getLoadedRevisionId() : $entity->getRevisionId())
83 ->condition('workflow', $moderation_info->getWorkflowForEntity($entity)->id())
85 ->sort('revision_id', 'DESC')
87 if (empty($revisions)) {
91 /** @var \Drupal\content_moderation\Entity\ContentModerationStateInterface $content_moderation_state */
92 $content_moderation_state = $content_moderation_storage->loadRevision(key($revisions));
93 if ($entity->getEntityType()->hasKey('langcode')) {
94 $langcode = $entity->language()->getId();
95 if (!$content_moderation_state->hasTranslation($langcode)) {
96 $content_moderation_state->addTranslation($langcode);
98 if ($content_moderation_state->language()->getId() !== $langcode) {
99 $content_moderation_state = $content_moderation_state->getTranslation($langcode);
102 return $content_moderation_state;
108 public function get($index) {
110 throw new \InvalidArgumentException('An entity can not have multiple moderation states at the same time.');
112 return $this->traitGet($index);
118 public function onChange($delta) {
119 $this->updateModeratedEntity($this->list[$delta]->value);
121 parent::onChange($delta);
127 public function setValue($values, $notify = TRUE) {
128 parent::setValue($values, $notify);
129 $this->valueComputed = TRUE;
131 // If the parent created a field item and if the parent should be notified
132 // about the change (e.g. this is not initialized with the current value),
133 // update the moderated entity.
134 if (isset($this->list[0]) && $notify) {
135 $this->updateModeratedEntity($this->list[0]->value);
140 * Updates the default revision flag and the publishing status of the entity.
142 * @param string $moderation_state_id
143 * The ID of the new moderation state.
145 protected function updateModeratedEntity($moderation_state_id) {
146 $entity = $this->getEntity();
148 /** @var \Drupal\content_moderation\ModerationInformationInterface $content_moderation_info */
149 $content_moderation_info = \Drupal::service('content_moderation.moderation_information');
150 $workflow = $content_moderation_info->getWorkflowForEntity($entity);
152 // Change the entity's default revision flag and the publishing status only
153 // if the new workflow state is a valid one.
154 if ($workflow && $workflow->getTypePlugin()->hasState($moderation_state_id)) {
155 /** @var \Drupal\content_moderation\ContentModerationState $current_state */
156 $current_state = $workflow->getTypePlugin()->getState($moderation_state_id);
158 // This entity is default if it is new, the default revision state, or the
159 // default revision is not published.
160 $update_default_revision = $entity->isNew()
161 || $current_state->isDefaultRevisionState()
162 || !$content_moderation_info->isDefaultRevisionPublished($entity);
164 $entity->isDefaultRevision($update_default_revision);
166 // Update publishing status if it can be updated and if it needs updating.
167 $published_state = $current_state->isPublishedState();
168 if (($entity instanceof EntityPublishedInterface) && $entity->isPublished() !== $published_state) {
169 $published_state ? $entity->setPublished() : $entity->setUnpublished();