Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / web / core / modules / content_moderation / src / Plugin / Field / ModerationStateFieldItemList.php
1 <?php
2
3 namespace Drupal\content_moderation\Plugin\Field;
4
5 use Drupal\Core\Entity\ContentEntityInterface;
6 use Drupal\Core\Entity\EntityPublishedInterface;
7 use Drupal\Core\Field\FieldItemList;
8 use Drupal\Core\TypedData\ComputedItemListTrait;
9
10 /**
11  * A computed field that provides a content entity's moderation state.
12  *
13  * It links content entities to a moderation state configuration entity via a
14  * moderation state content entity.
15  */
16 class ModerationStateFieldItemList extends FieldItemList {
17
18   use ComputedItemListTrait {
19     ensureComputedValue as traitEnsureComputedValue;
20     get as traitGet;
21   }
22
23   /**
24    * {@inheritdoc}
25    */
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);
34     }
35   }
36
37   /**
38    * {@inheritdoc}
39    */
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;
46     }
47     $this->traitEnsureComputedValue();
48   }
49
50   /**
51    * Gets the moderation state ID linked to a content entity revision.
52    *
53    * @return string|null
54    *   The moderation state ID linked to a content entity revision.
55    */
56   protected function getModerationStateId() {
57     $entity = $this->getEntity();
58
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())) {
62       return NULL;
63     }
64
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;
69     }
70
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;
76   }
77
78   /**
79    * Load the content moderation state revision associated with an entity.
80    *
81    * @param \Drupal\Core\Entity\ContentEntityInterface $entity
82    *   The entity the content moderation state entity will be loaded from.
83    *
84    * @return \Drupal\content_moderation\Entity\ContentModerationStateInterface|null
85    *   The content_moderation_state revision or FALSE if none exists.
86    */
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');
90
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
95       // being reverted.
96       ->condition('content_entity_revision_id', $entity->isNewRevision() ? $entity->getLoadedRevisionId() : $entity->getRevisionId())
97       ->condition('workflow', $moderation_info->getWorkflowForEntity($entity)->id())
98       ->allRevisions()
99       ->sort('revision_id', 'DESC')
100       ->execute();
101     if (empty($revisions)) {
102       return NULL;
103     }
104
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);
111       }
112       if ($content_moderation_state->language()->getId() !== $langcode) {
113         $content_moderation_state = $content_moderation_state->getTranslation($langcode);
114       }
115     }
116     return $content_moderation_state;
117   }
118
119   /**
120    * {@inheritdoc}
121    */
122   public function get($index) {
123     if ($index !== 0) {
124       throw new \InvalidArgumentException('An entity can not have multiple moderation states at the same time.');
125     }
126     return $this->traitGet($index);
127   }
128
129   /**
130    * {@inheritdoc}
131    */
132   public function onChange($delta) {
133     $this->updateModeratedEntity($this->list[$delta]->value);
134
135     parent::onChange($delta);
136   }
137
138   /**
139    * {@inheritdoc}
140    */
141   public function setValue($values, $notify = TRUE) {
142     parent::setValue($values, $notify);
143
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);
150     }
151   }
152
153   /**
154    * Updates the default revision flag and the publishing status of the entity.
155    *
156    * @param string $moderation_state_id
157    *   The ID of the new moderation state.
158    */
159   protected function updateModeratedEntity($moderation_state_id) {
160     $entity = $this->getEntity();
161
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);
165
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);
171
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);
177
178       $entity->isDefaultRevision($update_default_revision);
179
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();
184       }
185     }
186   }
187
188 }