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