Chris@0: moderationInfo = $moderation_info; Chris@0: $this->entityTypeManager = $entity_type_manager; Chris@0: $this->formBuilder = $form_builder; Chris@0: $this->bundleInfo = $bundle_info; Chris@14: $this->routerBuilder = $router_builder; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public static function create(ContainerInterface $container) { Chris@0: return new static( Chris@0: $container->get('content_moderation.moderation_information'), Chris@0: $container->get('entity_type.manager'), Chris@0: $container->get('form_builder'), Chris@14: $container->get('entity_type.bundle.info'), Chris@14: $container->get('router.builder') Chris@0: ); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Acts on an entity and set published status based on the moderation state. Chris@0: * Chris@0: * @param \Drupal\Core\Entity\EntityInterface $entity Chris@0: * The entity being saved. Chris@0: * Chris@0: * @see hook_entity_presave() Chris@0: */ Chris@0: public function entityPresave(EntityInterface $entity) { Chris@0: if (!$this->moderationInfo->isModeratedEntity($entity)) { Chris@0: return; Chris@0: } Chris@0: Chris@0: if ($entity->moderation_state->value) { Chris@0: $workflow = $this->moderationInfo->getWorkflowForEntity($entity); Chris@0: /** @var \Drupal\content_moderation\ContentModerationState $current_state */ Chris@0: $current_state = $workflow->getTypePlugin() Chris@0: ->getState($entity->moderation_state->value); Chris@0: Chris@14: // This entity is default if it is new, the default revision, or the Chris@14: // default revision is not published. Chris@0: $update_default_revision = $entity->isNew() Chris@0: || $current_state->isDefaultRevisionState() Chris@0: || !$this->moderationInfo->isDefaultRevisionPublished($entity); Chris@0: Chris@0: // Fire per-entity-type logic for handling the save process. Chris@0: $this->entityTypeManager Chris@0: ->getHandler($entity->getEntityTypeId(), 'moderation') Chris@0: ->onPresave($entity, $update_default_revision, $current_state->isPublishedState()); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * @param \Drupal\Core\Entity\EntityInterface $entity Chris@0: * The entity that was just saved. Chris@0: * Chris@0: * @see hook_entity_insert() Chris@0: */ Chris@0: public function entityInsert(EntityInterface $entity) { Chris@0: if ($this->moderationInfo->isModeratedEntity($entity)) { Chris@0: $this->updateOrCreateFromEntity($entity); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * @param \Drupal\Core\Entity\EntityInterface $entity Chris@0: * The entity that was just saved. Chris@0: * Chris@0: * @see hook_entity_update() Chris@0: */ Chris@0: public function entityUpdate(EntityInterface $entity) { Chris@0: if ($this->moderationInfo->isModeratedEntity($entity)) { Chris@0: $this->updateOrCreateFromEntity($entity); Chris@0: } Chris@14: // When updating workflow settings for Content Moderation, we need to Chris@14: // rebuild routes as we may be enabling new entity types and the related Chris@14: // entity forms. Chris@14: elseif ($entity instanceof Workflow && $entity->getTypePlugin()->getPluginId() == 'content_moderation') { Chris@14: $this->routerBuilder->setRebuildNeeded(); Chris@14: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Creates or updates the moderation state of an entity. Chris@0: * Chris@0: * @param \Drupal\Core\Entity\EntityInterface $entity Chris@0: * The entity to update or create a moderation state for. Chris@0: */ Chris@0: protected function updateOrCreateFromEntity(EntityInterface $entity) { Chris@0: /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ Chris@0: $entity_revision_id = $entity->getRevisionId(); Chris@0: $workflow = $this->moderationInfo->getWorkflowForEntity($entity); Chris@0: $content_moderation_state = ContentModerationStateEntity::loadFromModeratedEntity($entity); Chris@14: /** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */ Chris@14: $storage = $this->entityTypeManager->getStorage('content_moderation_state'); Chris@0: Chris@0: if (!($content_moderation_state instanceof ContentModerationStateInterface)) { Chris@0: $content_moderation_state = $storage->create([ Chris@0: 'content_entity_type_id' => $entity->getEntityTypeId(), Chris@0: 'content_entity_id' => $entity->id(), Chris@0: // Make sure that the moderation state entity has the same language code Chris@0: // as the moderated entity. Chris@0: 'langcode' => $entity->language()->getId(), Chris@0: ]); Chris@0: $content_moderation_state->workflow->target_id = $workflow->id(); Chris@0: } Chris@0: Chris@0: // Sync translations. Chris@0: if ($entity->getEntityType()->hasKey('langcode')) { Chris@0: $entity_langcode = $entity->language()->getId(); Chris@17: if ($entity->isDefaultTranslation()) { Chris@17: $content_moderation_state->langcode = $entity_langcode; Chris@0: } Chris@17: else { Chris@17: if (!$content_moderation_state->hasTranslation($entity_langcode)) { Chris@17: $content_moderation_state->addTranslation($entity_langcode); Chris@17: } Chris@17: if ($content_moderation_state->language()->getId() !== $entity_langcode) { Chris@17: $content_moderation_state = $content_moderation_state->getTranslation($entity_langcode); Chris@17: } Chris@0: } Chris@0: } Chris@0: Chris@14: // If a new revision of the content has been created, add a new content Chris@14: // moderation state revision. Chris@14: if (!$content_moderation_state->isNew() && $content_moderation_state->content_entity_revision_id->value != $entity_revision_id) { Chris@14: $content_moderation_state = $storage->createRevision($content_moderation_state, $entity->isDefaultRevision()); Chris@14: } Chris@14: Chris@0: // Create the ContentModerationState entity for the inserted entity. Chris@0: $moderation_state = $entity->moderation_state->value; Chris@0: /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ Chris@0: if (!$moderation_state) { Chris@0: $moderation_state = $workflow->getTypePlugin()->getInitialState($entity)->id(); Chris@0: } Chris@0: Chris@0: $content_moderation_state->set('content_entity_revision_id', $entity_revision_id); Chris@0: $content_moderation_state->set('moderation_state', $moderation_state); Chris@0: ContentModerationStateEntity::updateOrCreateFromEntity($content_moderation_state); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @param \Drupal\Core\Entity\EntityInterface $entity Chris@0: * The entity being deleted. Chris@0: * Chris@0: * @see hook_entity_delete() Chris@0: */ Chris@0: public function entityDelete(EntityInterface $entity) { Chris@0: $content_moderation_state = ContentModerationStateEntity::loadFromModeratedEntity($entity); Chris@0: if ($content_moderation_state) { Chris@0: $content_moderation_state->delete(); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * @param \Drupal\Core\Entity\EntityInterface $entity Chris@0: * The entity revision being deleted. Chris@0: * Chris@0: * @see hook_entity_revision_delete() Chris@0: */ Chris@0: public function entityRevisionDelete(EntityInterface $entity) { Chris@18: if ($content_moderation_state = ContentModerationStateEntity::loadFromModeratedEntity($entity)) { Chris@18: if ($content_moderation_state->isDefaultRevision()) { Chris@18: $content_moderation_state->delete(); Chris@18: } Chris@18: else { Chris@0: $this->entityTypeManager Chris@0: ->getStorage('content_moderation_state') Chris@0: ->deleteRevision($content_moderation_state->getRevisionId()); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * @param \Drupal\Core\Entity\EntityInterface $translation Chris@0: * The entity translation being deleted. Chris@0: * Chris@0: * @see hook_entity_translation_delete() Chris@0: */ Chris@0: public function entityTranslationDelete(EntityInterface $translation) { Chris@0: /** @var \Drupal\Core\Entity\ContentEntityInterface $translation */ Chris@0: if (!$translation->isDefaultTranslation()) { Chris@0: $langcode = $translation->language()->getId(); Chris@0: $content_moderation_state = ContentModerationStateEntity::loadFromModeratedEntity($translation); Chris@0: if ($content_moderation_state && $content_moderation_state->hasTranslation($langcode)) { Chris@0: $content_moderation_state->removeTranslation($langcode); Chris@0: ContentModerationStateEntity::updateOrCreateFromEntity($content_moderation_state); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Act on entities being assembled before rendering. Chris@0: * Chris@0: * @see hook_entity_view() Chris@0: * @see EntityFieldManagerInterface::getExtraFields() Chris@0: */ Chris@0: public function entityView(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) { Chris@14: /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ Chris@0: if (!$this->moderationInfo->isModeratedEntity($entity)) { Chris@0: return; Chris@0: } Chris@14: if (isset($entity->in_preview) && $entity->in_preview) { Chris@0: return; Chris@0: } Chris@14: // If the component is not defined for this display, we have nothing to do. Chris@14: if (!$display->getComponent('content_moderation_control')) { Chris@0: return; Chris@0: } Chris@14: // The moderation form should be displayed only when viewing the latest Chris@14: // (translation-affecting) revision, unless it was created as published Chris@14: // default revision. Chris@17: if (($entity->isDefaultRevision() || $entity->wasDefaultRevision()) && $this->isPublished($entity)) { Chris@17: return; Chris@17: } Chris@14: if (!$entity->isLatestRevision() && !$entity->isLatestTranslationAffectedRevision()) { Chris@0: return; Chris@0: } Chris@0: Chris@14: $build['content_moderation_control'] = $this->formBuilder->getForm(EntityModerationForm::class, $entity); Chris@0: } Chris@0: Chris@17: /** Chris@17: * Checks if the entity is published. Chris@17: * Chris@17: * This method is optimized to not have to unnecessarily load the moderation Chris@17: * state and workflow if it is not required. Chris@17: * Chris@17: * @param \Drupal\Core\Entity\ContentEntityInterface $entity Chris@17: * The entity to check. Chris@17: * Chris@17: * @return bool Chris@17: * TRUE if the entity is published, FALSE otherwise. Chris@17: */ Chris@17: protected function isPublished(ContentEntityInterface $entity) { Chris@17: // If the entity implements EntityPublishedInterface directly, check that Chris@17: // first, otherwise fall back to check through the workflow state. Chris@17: if ($entity instanceof EntityPublishedInterface) { Chris@17: return $entity->isPublished(); Chris@17: } Chris@17: if ($moderation_state = $entity->get('moderation_state')->value) { Chris@17: $workflow = $this->moderationInfo->getWorkflowForEntity($entity); Chris@17: return $workflow->getTypePlugin()->getState($moderation_state)->isPublishedState(); Chris@17: } Chris@17: return FALSE; Chris@17: } Chris@17: Chris@0: }