Security update for Core, with self-updated composer
[yaffs-website] / web / core / modules / content_moderation / src / EntityOperations.php
1 <?php
2
3 namespace Drupal\content_moderation;
4
5 use Drupal\content_moderation\Entity\ContentModerationState as ContentModerationStateEntity;
6 use Drupal\content_moderation\Entity\ContentModerationStateInterface;
7 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
8 use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
9 use Drupal\Core\Entity\EntityInterface;
10 use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
11 use Drupal\Core\Entity\EntityTypeManagerInterface;
12 use Drupal\Core\Form\FormBuilderInterface;
13 use Drupal\content_moderation\Form\EntityModerationForm;
14 use Symfony\Component\DependencyInjection\ContainerInterface;
15
16 /**
17  * Defines a class for reacting to entity events.
18  *
19  * @internal
20  */
21 class EntityOperations implements ContainerInjectionInterface {
22
23   /**
24    * The Moderation Information service.
25    *
26    * @var \Drupal\content_moderation\ModerationInformationInterface
27    */
28   protected $moderationInfo;
29
30   /**
31    * The Entity Type Manager service.
32    *
33    * @var \Drupal\Core\Entity\EntityTypeManagerInterface
34    */
35   protected $entityTypeManager;
36
37   /**
38    * The Form Builder service.
39    *
40    * @var \Drupal\Core\Form\FormBuilderInterface
41    */
42   protected $formBuilder;
43
44   /**
45    * The entity bundle information service.
46    *
47    * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
48    */
49   protected $bundleInfo;
50
51   /**
52    * Constructs a new EntityOperations object.
53    *
54    * @param \Drupal\content_moderation\ModerationInformationInterface $moderation_info
55    *   Moderation information service.
56    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
57    *   Entity type manager service.
58    * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
59    *   The form builder.
60    * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_info
61    *   The entity bundle information service.
62    */
63   public function __construct(ModerationInformationInterface $moderation_info, EntityTypeManagerInterface $entity_type_manager, FormBuilderInterface $form_builder, EntityTypeBundleInfoInterface $bundle_info) {
64     $this->moderationInfo = $moderation_info;
65     $this->entityTypeManager = $entity_type_manager;
66     $this->formBuilder = $form_builder;
67     $this->bundleInfo = $bundle_info;
68   }
69
70   /**
71    * {@inheritdoc}
72    */
73   public static function create(ContainerInterface $container) {
74     return new static(
75       $container->get('content_moderation.moderation_information'),
76       $container->get('entity_type.manager'),
77       $container->get('form_builder'),
78       $container->get('entity_type.bundle.info')
79     );
80   }
81
82   /**
83    * Acts on an entity and set published status based on the moderation state.
84    *
85    * @param \Drupal\Core\Entity\EntityInterface $entity
86    *   The entity being saved.
87    *
88    * @see hook_entity_presave()
89    */
90   public function entityPresave(EntityInterface $entity) {
91     if (!$this->moderationInfo->isModeratedEntity($entity)) {
92       return;
93     }
94
95     if ($entity->moderation_state->value) {
96       $workflow = $this->moderationInfo->getWorkflowForEntity($entity);
97       /** @var \Drupal\content_moderation\ContentModerationState $current_state */
98       $current_state = $workflow->getTypePlugin()
99         ->getState($entity->moderation_state->value);
100
101       // This entity is default if it is new, a new translation, the default
102       // revision, or the default revision is not published.
103       $update_default_revision = $entity->isNew()
104         || $entity->isNewTranslation()
105         || $current_state->isDefaultRevisionState()
106         || !$this->moderationInfo->isDefaultRevisionPublished($entity);
107
108       // Fire per-entity-type logic for handling the save process.
109       $this->entityTypeManager
110         ->getHandler($entity->getEntityTypeId(), 'moderation')
111         ->onPresave($entity, $update_default_revision, $current_state->isPublishedState());
112     }
113   }
114
115   /**
116    * @param \Drupal\Core\Entity\EntityInterface $entity
117    *   The entity that was just saved.
118    *
119    * @see hook_entity_insert()
120    */
121   public function entityInsert(EntityInterface $entity) {
122     if ($this->moderationInfo->isModeratedEntity($entity)) {
123       $this->updateOrCreateFromEntity($entity);
124     }
125   }
126
127   /**
128    * @param \Drupal\Core\Entity\EntityInterface $entity
129    *   The entity that was just saved.
130    *
131    * @see hook_entity_update()
132    */
133   public function entityUpdate(EntityInterface $entity) {
134     if ($this->moderationInfo->isModeratedEntity($entity)) {
135       $this->updateOrCreateFromEntity($entity);
136     }
137   }
138
139   /**
140    * Creates or updates the moderation state of an entity.
141    *
142    * @param \Drupal\Core\Entity\EntityInterface $entity
143    *   The entity to update or create a moderation state for.
144    */
145   protected function updateOrCreateFromEntity(EntityInterface $entity) {
146     /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
147     $entity_revision_id = $entity->getRevisionId();
148     $workflow = $this->moderationInfo->getWorkflowForEntity($entity);
149     $content_moderation_state = ContentModerationStateEntity::loadFromModeratedEntity($entity);
150
151     if (!($content_moderation_state instanceof ContentModerationStateInterface)) {
152       $storage = $this->entityTypeManager->getStorage('content_moderation_state');
153       $content_moderation_state = $storage->create([
154         'content_entity_type_id' => $entity->getEntityTypeId(),
155         'content_entity_id' => $entity->id(),
156         // Make sure that the moderation state entity has the same language code
157         // as the moderated entity.
158         'langcode' => $entity->language()->getId(),
159       ]);
160       $content_moderation_state->workflow->target_id = $workflow->id();
161     }
162     elseif ($content_moderation_state->content_entity_revision_id->value != $entity_revision_id) {
163       // If a new revision of the content has been created, add a new content
164       // moderation state revision.
165       $content_moderation_state->setNewRevision(TRUE);
166     }
167
168     // Sync translations.
169     if ($entity->getEntityType()->hasKey('langcode')) {
170       $entity_langcode = $entity->language()->getId();
171       if (!$content_moderation_state->hasTranslation($entity_langcode)) {
172         $content_moderation_state->addTranslation($entity_langcode);
173       }
174       if ($content_moderation_state->language()->getId() !== $entity_langcode) {
175         $content_moderation_state = $content_moderation_state->getTranslation($entity_langcode);
176       }
177     }
178
179     // Create the ContentModerationState entity for the inserted entity.
180     $moderation_state = $entity->moderation_state->value;
181     /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
182     if (!$moderation_state) {
183       $moderation_state = $workflow->getTypePlugin()->getInitialState($entity)->id();
184     }
185
186     // @todo what if $entity->moderation_state is null at this point?
187     $content_moderation_state->set('content_entity_revision_id', $entity_revision_id);
188     $content_moderation_state->set('moderation_state', $moderation_state);
189     ContentModerationStateEntity::updateOrCreateFromEntity($content_moderation_state);
190   }
191
192   /**
193    * @param \Drupal\Core\Entity\EntityInterface $entity
194    *   The entity being deleted.
195    *
196    * @see hook_entity_delete()
197    */
198   public function entityDelete(EntityInterface $entity) {
199     $content_moderation_state = ContentModerationStateEntity::loadFromModeratedEntity($entity);
200     if ($content_moderation_state) {
201       $content_moderation_state->delete();
202     }
203   }
204
205   /**
206    * @param \Drupal\Core\Entity\EntityInterface $entity
207    *   The entity revision being deleted.
208    *
209    * @see hook_entity_revision_delete()
210    */
211   public function entityRevisionDelete(EntityInterface $entity) {
212     /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
213     if (!$entity->isDefaultRevision()) {
214       $content_moderation_state = ContentModerationStateEntity::loadFromModeratedEntity($entity);
215       if ($content_moderation_state) {
216         $this->entityTypeManager
217           ->getStorage('content_moderation_state')
218           ->deleteRevision($content_moderation_state->getRevisionId());
219       }
220     }
221   }
222
223   /**
224    * @param \Drupal\Core\Entity\EntityInterface $translation
225    *   The entity translation being deleted.
226    *
227    * @see hook_entity_translation_delete()
228    */
229   public function entityTranslationDelete(EntityInterface $translation) {
230     /** @var \Drupal\Core\Entity\ContentEntityInterface $translation */
231     if (!$translation->isDefaultTranslation()) {
232       $langcode = $translation->language()->getId();
233       $content_moderation_state = ContentModerationStateEntity::loadFromModeratedEntity($translation);
234       if ($content_moderation_state && $content_moderation_state->hasTranslation($langcode)) {
235         $content_moderation_state->removeTranslation($langcode);
236         ContentModerationStateEntity::updateOrCreateFromEntity($content_moderation_state);
237       }
238     }
239   }
240
241   /**
242    * Act on entities being assembled before rendering.
243    *
244    * @see hook_entity_view()
245    * @see EntityFieldManagerInterface::getExtraFields()
246    */
247   public function entityView(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
248     if (!$this->moderationInfo->isModeratedEntity($entity)) {
249       return;
250     }
251     if (!$this->moderationInfo->isLatestRevision($entity)) {
252       return;
253     }
254     if ($this->moderationInfo->isLiveRevision($entity)) {
255       return;
256     }
257     // Don't display the moderation form when when:
258     // - The revision is not translation affected.
259     // - There are more than one translation languages.
260     // - The entity has pending revisions.
261     if (!$this->moderationInfo->isPendingRevisionAllowed($entity)) {
262       return;
263     }
264
265     $component = $display->getComponent('content_moderation_control');
266     if ($component) {
267       $build['content_moderation_control'] = $this->formBuilder->getForm(EntityModerationForm::class, $entity);
268     }
269   }
270
271 }