3 namespace Drupal\content_moderation;
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;
19 * Defines a class for reacting to entity events.
23 class EntityOperations implements ContainerInjectionInterface {
26 * The Moderation Information service.
28 * @var \Drupal\content_moderation\ModerationInformationInterface
30 protected $moderationInfo;
33 * The Entity Type Manager service.
35 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
37 protected $entityTypeManager;
40 * The Form Builder service.
42 * @var \Drupal\Core\Form\FormBuilderInterface
44 protected $formBuilder;
47 * The entity bundle information service.
49 * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
51 protected $bundleInfo;
54 * The router builder service.
56 * @var \Drupal\Core\Routing\RouteBuilderInterface
58 protected $routerBuilder;
61 * Constructs a new EntityOperations object.
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
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.
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;
85 public static function create(ContainerInterface $container) {
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')
96 * Acts on an entity and set published status based on the moderation state.
98 * @param \Drupal\Core\Entity\EntityInterface $entity
99 * The entity being saved.
101 * @see hook_entity_presave()
103 public function entityPresave(EntityInterface $entity) {
104 if (!$this->moderationInfo->isModeratedEntity($entity)) {
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);
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);
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());
128 * @param \Drupal\Core\Entity\EntityInterface $entity
129 * The entity that was just saved.
131 * @see hook_entity_insert()
133 public function entityInsert(EntityInterface $entity) {
134 if ($this->moderationInfo->isModeratedEntity($entity)) {
135 $this->updateOrCreateFromEntity($entity);
140 * @param \Drupal\Core\Entity\EntityInterface $entity
141 * The entity that was just saved.
143 * @see hook_entity_update()
145 public function entityUpdate(EntityInterface $entity) {
146 if ($this->moderationInfo->isModeratedEntity($entity)) {
147 $this->updateOrCreateFromEntity($entity);
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
152 elseif ($entity instanceof Workflow && $entity->getTypePlugin()->getPluginId() == 'content_moderation') {
153 $this->routerBuilder->setRebuildNeeded();
158 * Creates or updates the moderation state of an entity.
160 * @param \Drupal\Core\Entity\EntityInterface $entity
161 * The entity to update or create a moderation state for.
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');
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(),
179 $content_moderation_state->workflow->target_id = $workflow->id();
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);
188 if ($content_moderation_state->language()->getId() !== $entity_langcode) {
189 $content_moderation_state = $content_moderation_state->getTranslation($entity_langcode);
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());
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();
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);
213 * @param \Drupal\Core\Entity\EntityInterface $entity
214 * The entity being deleted.
216 * @see hook_entity_delete()
218 public function entityDelete(EntityInterface $entity) {
219 $content_moderation_state = ContentModerationStateEntity::loadFromModeratedEntity($entity);
220 if ($content_moderation_state) {
221 $content_moderation_state->delete();
226 * @param \Drupal\Core\Entity\EntityInterface $entity
227 * The entity revision being deleted.
229 * @see hook_entity_revision_delete()
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());
244 * @param \Drupal\Core\Entity\EntityInterface $translation
245 * The entity translation being deleted.
247 * @see hook_entity_translation_delete()
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);
262 * Act on entities being assembled before rendering.
264 * @see hook_entity_view()
265 * @see EntityFieldManagerInterface::getExtraFields()
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)) {
272 if (isset($entity->in_preview) && $entity->in_preview) {
275 // If the component is not defined for this display, we have nothing to do.
276 if (!$display->getComponent('content_moderation_control')) {
279 // The moderation form should be displayed only when viewing the latest
280 // (translation-affecting) revision, unless it was created as published
282 if (!$entity->isLatestRevision() && !$entity->isLatestTranslationAffectedRevision()) {
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()) {
292 $build['content_moderation_control'] = $this->formBuilder->getForm(EntityModerationForm::class, $entity);