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;
10 * A computed field that provides a content entity's moderation state.
12 * It links content entities to a moderation state configuration entity via a
13 * moderation state content entity.
15 class ModerationStateFieldItemList extends FieldItemList {
18 * Gets the moderation state ID linked to a content entity revision.
21 * The moderation state ID linked to a content entity revision.
23 protected function getModerationStateId() {
24 $entity = $this->getEntity();
26 /** @var \Drupal\content_moderation\ModerationInformationInterface $moderation_info */
27 $moderation_info = \Drupal::service('content_moderation.moderation_information');
28 if (!$moderation_info->shouldModerateEntitiesOfBundle($entity->getEntityType(), $entity->bundle())) {
32 // Existing entities will have a corresponding content_moderation_state
33 // entity associated with them.
34 if (!$entity->isNew() && $content_moderation_state = $this->loadContentModerationStateRevision($entity)) {
35 return $content_moderation_state->moderation_state->value;
38 // It is possible that the bundle does not exist at this point. For example,
39 // the node type form creates a fake Node entity to get default values.
40 // @see \Drupal\node\NodeTypeForm::form()
41 $workflow = $moderation_info->getWorkFlowForEntity($entity);
42 return $workflow ? $workflow->getTypePlugin()->getInitialState($entity)->id() : NULL;
46 * Load the content moderation state revision associated with an entity.
48 * @param \Drupal\Core\Entity\ContentEntityInterface $entity
49 * The entity the content moderation state entity will be loaded from.
51 * @return \Drupal\content_moderation\Entity\ContentModerationStateInterface|null
52 * The content_moderation_state revision or FALSE if none exists.
54 protected function loadContentModerationStateRevision(ContentEntityInterface $entity) {
55 $moderation_info = \Drupal::service('content_moderation.moderation_information');
56 $content_moderation_storage = \Drupal::entityTypeManager()->getStorage('content_moderation_state');
58 $revisions = \Drupal::service('entity.query')->get('content_moderation_state')
59 ->condition('content_entity_type_id', $entity->getEntityTypeId())
60 ->condition('content_entity_id', $entity->id())
61 // Ensure the correct revision is loaded in scenarios where a revision is
63 ->condition('content_entity_revision_id', $entity->isNewRevision() ? $entity->getLoadedRevisionId() : $entity->getRevisionId())
64 ->condition('workflow', $moderation_info->getWorkflowForEntity($entity)->id())
66 ->sort('revision_id', 'DESC')
68 if (empty($revisions)) {
72 /** @var \Drupal\content_moderation\Entity\ContentModerationStateInterface $content_moderation_state */
73 $content_moderation_state = $content_moderation_storage->loadRevision(key($revisions));
74 if ($entity->getEntityType()->hasKey('langcode')) {
75 $langcode = $entity->language()->getId();
76 if (!$content_moderation_state->hasTranslation($langcode)) {
77 $content_moderation_state->addTranslation($langcode);
79 if ($content_moderation_state->language()->getId() !== $langcode) {
80 $content_moderation_state = $content_moderation_state->getTranslation($langcode);
83 return $content_moderation_state;
89 public function get($index) {
91 throw new \InvalidArgumentException('An entity can not have multiple moderation states at the same time.');
93 $this->computeModerationFieldItemList();
94 return isset($this->list[$index]) ? $this->list[$index] : NULL;
100 public function getIterator() {
101 $this->computeModerationFieldItemList();
102 return parent::getIterator();
106 * Recalculate the moderation field item list.
108 protected function computeModerationFieldItemList() {
109 // Compute the value of the moderation state.
111 if (!isset($this->list[$index]) || $this->list[$index]->isEmpty()) {
113 $moderation_state = $this->getModerationStateId();
114 // Do not store NULL values in the static cache.
115 if ($moderation_state) {
116 $this->list[$index] = $this->createItem($index, $moderation_state);
124 public function onChange($delta) {
125 $this->updateModeratedEntity($this->list[$delta]->value);
127 parent::onChange($delta);
133 public function setValue($values, $notify = TRUE) {
134 parent::setValue($values, $notify);
136 if (isset($this->list[0])) {
137 $this->updateModeratedEntity($this->list[0]->value);
142 * Updates the default revision flag and the publishing status of the entity.
144 * @param string $moderation_state_id
145 * The ID of the new moderation state.
147 protected function updateModeratedEntity($moderation_state_id) {
148 $entity = $this->getEntity();
150 /** @var \Drupal\content_moderation\ModerationInformationInterface $content_moderation_info */
151 $content_moderation_info = \Drupal::service('content_moderation.moderation_information');
152 $workflow = $content_moderation_info->getWorkflowForEntity($entity);
154 // Change the entity's default revision flag and the publishing status only
155 // if the new workflow state is a valid one.
156 if ($workflow && $workflow->getTypePlugin()->hasState($moderation_state_id)) {
157 /** @var \Drupal\content_moderation\ContentModerationState $current_state */
158 $current_state = $workflow->getTypePlugin()->getState($moderation_state_id);
160 // This entity is default if it is new, a new translation, the default
161 // revision state, or the default revision is not published.
162 $update_default_revision = $entity->isNew()
163 || $entity->isNewTranslation()
164 || $current_state->isDefaultRevisionState()
165 || !$content_moderation_info->isDefaultRevisionPublished($entity);
167 $entity->isDefaultRevision($update_default_revision);
169 // Update publishing status if it can be updated and if it needs updating.
170 $published_state = $current_state->isPublishedState();
171 if (($entity instanceof EntityPublishedInterface) && $entity->isPublished() !== $published_state) {
172 $published_state ? $entity->setPublished() : $entity->setUnpublished();