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 {
19 ensureComputedValue as traitEnsureComputedValue;
26 protected function computeValue() {
27 $moderation_state = $this->getModerationStateId();
28 // Do not store NULL values, in the case where an entity does not have a
29 // moderation workflow associated with it, we do not create list items for
30 // the computed field.
31 if ($moderation_state) {
32 // An entity can only have a single moderation state.
33 $this->list[0] = $this->createItem(0, $moderation_state);
40 protected function ensureComputedValue() {
41 // If the moderation state field is set to an empty value, always recompute
42 // the state. Empty is not a valid moderation state value, when none is
43 // present the default state is used.
44 if (!isset($this->list[0]) || $this->list[0]->isEmpty()) {
45 $this->valueComputed = FALSE;
47 $this->traitEnsureComputedValue();
51 * Gets the moderation state ID linked to a content entity revision.
54 * The moderation state ID linked to a content entity revision.
56 protected function getModerationStateId() {
57 $entity = $this->getEntity();
59 /** @var \Drupal\content_moderation\ModerationInformationInterface $moderation_info */
60 $moderation_info = \Drupal::service('content_moderation.moderation_information');
61 if (!$moderation_info->shouldModerateEntitiesOfBundle($entity->getEntityType(), $entity->bundle())) {
65 // Existing entities will have a corresponding content_moderation_state
66 // entity associated with them.
67 if (!$entity->isNew() && $content_moderation_state = $this->loadContentModerationStateRevision($entity)) {
68 return $content_moderation_state->moderation_state->value;
71 // It is possible that the bundle does not exist at this point. For example,
72 // the node type form creates a fake Node entity to get default values.
73 // @see \Drupal\node\NodeTypeForm::form()
74 $workflow = $moderation_info->getWorkFlowForEntity($entity);
75 return $workflow ? $workflow->getTypePlugin()->getInitialState($entity)->id() : NULL;
79 * Load the content moderation state revision associated with an entity.
81 * @param \Drupal\Core\Entity\ContentEntityInterface $entity
82 * The entity the content moderation state entity will be loaded from.
84 * @return \Drupal\content_moderation\Entity\ContentModerationStateInterface|null
85 * The content_moderation_state revision or FALSE if none exists.
87 protected function loadContentModerationStateRevision(ContentEntityInterface $entity) {
88 $moderation_info = \Drupal::service('content_moderation.moderation_information');
89 $content_moderation_storage = \Drupal::entityTypeManager()->getStorage('content_moderation_state');
91 $revisions = \Drupal::service('entity.query')->get('content_moderation_state')
92 ->condition('content_entity_type_id', $entity->getEntityTypeId())
93 ->condition('content_entity_id', $entity->id())
94 // Ensure the correct revision is loaded in scenarios where a revision is
96 ->condition('content_entity_revision_id', $entity->isNewRevision() ? $entity->getLoadedRevisionId() : $entity->getRevisionId())
97 ->condition('workflow', $moderation_info->getWorkflowForEntity($entity)->id())
99 ->sort('revision_id', 'DESC')
101 if (empty($revisions)) {
105 /** @var \Drupal\content_moderation\Entity\ContentModerationStateInterface $content_moderation_state */
106 $content_moderation_state = $content_moderation_storage->loadRevision(key($revisions));
107 if ($entity->getEntityType()->hasKey('langcode')) {
108 $langcode = $entity->language()->getId();
109 if (!$content_moderation_state->hasTranslation($langcode)) {
110 $content_moderation_state->addTranslation($langcode);
112 if ($content_moderation_state->language()->getId() !== $langcode) {
113 $content_moderation_state = $content_moderation_state->getTranslation($langcode);
116 return $content_moderation_state;
122 public function get($index) {
124 throw new \InvalidArgumentException('An entity can not have multiple moderation states at the same time.');
126 return $this->traitGet($index);
132 public function onChange($delta) {
133 $this->updateModeratedEntity($this->list[$delta]->value);
135 parent::onChange($delta);
141 public function setValue($values, $notify = TRUE) {
142 parent::setValue($values, $notify);
144 // If the parent created a field item and if the parent should be notified
145 // about the change (e.g. this is not initialized with the current value),
146 // update the moderated entity.
147 if (isset($this->list[0]) && $notify) {
148 $this->valueComputed = TRUE;
149 $this->updateModeratedEntity($this->list[0]->value);
154 * Updates the default revision flag and the publishing status of the entity.
156 * @param string $moderation_state_id
157 * The ID of the new moderation state.
159 protected function updateModeratedEntity($moderation_state_id) {
160 $entity = $this->getEntity();
162 /** @var \Drupal\content_moderation\ModerationInformationInterface $content_moderation_info */
163 $content_moderation_info = \Drupal::service('content_moderation.moderation_information');
164 $workflow = $content_moderation_info->getWorkflowForEntity($entity);
166 // Change the entity's default revision flag and the publishing status only
167 // if the new workflow state is a valid one.
168 if ($workflow && $workflow->getTypePlugin()->hasState($moderation_state_id)) {
169 /** @var \Drupal\content_moderation\ContentModerationState $current_state */
170 $current_state = $workflow->getTypePlugin()->getState($moderation_state_id);
172 // This entity is default if it is new, the default revision state, or the
173 // default revision is not published.
174 $update_default_revision = $entity->isNew()
175 || $current_state->isDefaultRevisionState()
176 || !$content_moderation_info->isDefaultRevisionPublished($entity);
178 $entity->isDefaultRevision($update_default_revision);
180 // Update publishing status if it can be updated and if it needs updating.
181 $published_state = $current_state->isPublishedState();
182 if (($entity instanceof EntityPublishedInterface) && $entity->isPublished() !== $published_state) {
183 $published_state ? $entity->setPublished() : $entity->setUnpublished();