EntityOperations.php

Same filename in this branch
  1. 10 core/modules/workspaces/src/EntityOperations.php
  2. 10 core/modules/views/src/Plugin/views/field/EntityOperations.php
Same filename in other branches
  1. 9 core/modules/content_moderation/src/EntityOperations.php
  2. 9 core/modules/workspaces/src/EntityOperations.php
  3. 9 core/modules/views/src/Plugin/views/field/EntityOperations.php
  4. 8.9.x core/modules/content_moderation/src/EntityOperations.php
  5. 8.9.x core/modules/workspaces/src/EntityOperations.php
  6. 8.9.x core/modules/views/src/Plugin/views/field/EntityOperations.php
  7. 11.x core/modules/content_moderation/src/EntityOperations.php
  8. 11.x core/modules/workspaces/src/EntityOperations.php
  9. 11.x core/modules/views/src/Plugin/views/field/EntityOperations.php

Namespace

Drupal\content_moderation

File

core/modules/content_moderation/src/EntityOperations.php

View source
<?php

namespace Drupal\content_moderation;

use Drupal\content_moderation\Entity\ContentModerationState as ContentModerationStateEntity;
use Drupal\content_moderation\Entity\ContentModerationStateInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityPublishedInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\content_moderation\Form\EntityModerationForm;
use Drupal\Core\Routing\RouteBuilderInterface;
use Drupal\workflows\Entity\Workflow;
use Symfony\Component\DependencyInjection\ContainerInterface;

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

}

Classes

Title Deprecated Summary
EntityOperations Defines a class for reacting to entity events.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.