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