Skip to content
Snippets Groups Projects

2.x 3050027

Open Carlos Romero requested to merge issue/entity_clone-3050027:2.x-3050027 into 2.x
4 files
+ 298
7
Compare changes
  • Side-by-side
  • Inline
Files
4
@@ -2,6 +2,7 @@
namespace Drupal\entity_clone\EntityClone\Content;
use Drupal\block_content\BlockContentInterface;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\content_moderation\Entity\ContentModerationState;
use Drupal\content_moderation\Plugin\Field\ModerationStateFieldItemList;
@@ -9,15 +10,20 @@ use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityChangedInterface;
use Drupal\Core\Entity\EntityHandlerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Entity\TranslatableInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Field\FieldConfigInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\entity_clone\EntityClone\EntityCloneInterface;
use Drupal\entity_clone\EntityCloneClonableFieldInterface;
use Drupal\layout_builder\InlineBlockUsageInterface;
use Drupal\layout_builder\Section;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -60,6 +66,13 @@ class ContentEntityCloneBase implements EntityHandlerInterface, EntityCloneInter
*/
protected $currentUser;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Constructs a new ContentEntityCloneBase.
*
@@ -73,13 +86,16 @@ class ContentEntityCloneBase implements EntityHandlerInterface, EntityCloneInter
* The current user.
* @param \Drupal\entity_clone\EntityCloneClonableFieldInterface $entity_clone_clonable_field
* The entity clone clonable field service.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler service.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, string $entity_type_id, TimeInterface $time_service, AccountProxyInterface $current_user, EntityCloneClonableFieldInterface $entity_clone_clonable_field) {
public function __construct(EntityTypeManagerInterface $entity_type_manager, string $entity_type_id, TimeInterface $time_service, AccountProxyInterface $current_user, EntityCloneClonableFieldInterface $entity_clone_clonable_field, ModuleHandlerInterface $module_handler) {
$this->entityTypeManager = $entity_type_manager;
$this->entityTypeId = $entity_type_id;
$this->timeService = $time_service;
$this->currentUser = $current_user;
$this->entityCloneClonableField = $entity_clone_clonable_field;
$this->moduleHandler = $module_handler;
}
/**
@@ -91,7 +107,8 @@ class ContentEntityCloneBase implements EntityHandlerInterface, EntityCloneInter
$entity_type->id(),
$container->get('datetime.time'),
$container->get('current_user'),
$container->get('entity_clone.clonable_field')
$container->get('entity_clone.clonable_field'),
$container->get('module_handler')
);
}
@@ -101,11 +118,19 @@ class ContentEntityCloneBase implements EntityHandlerInterface, EntityCloneInter
public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = [], array &$already_cloned = []) {
// Clone referenced entities.
$already_cloned[$entity->getEntityTypeId()][$entity->id()] = $cloned_entity;
$languages = $cloned_entity->getTranslationLanguages(TRUE);
if ($cloned_entity instanceof FieldableEntityInterface && $entity instanceof FieldableEntityInterface) {
foreach ($cloned_entity->getFieldDefinitions() as $field_id => $field_definition) {
$field = $entity->get($field_id);
if ($this->entityCloneClonableField->isClonable($field_definition, $field)) {
$cloned_entity->set($field_id, $this->cloneReferencedEntities($field, $field_definition, $properties, $already_cloned));
if ($cloning_method = $this->getCloneCallback($field_definition)) {
foreach ($languages as $language) {
$cloned_translation = $cloned_entity->getTranslation($language->getId());
$entity_translation = $entity->getTranslation($language->getId());
$field = $entity_translation->get($field_id);
/** @var \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem $value */
if ($field->count() > 0) {
$cloned_translation->set($field_id, $this->$cloning_method($field, $field_definition, $properties, $already_cloned));
}
}
}
}
}
@@ -115,7 +140,6 @@ class ContentEntityCloneBase implements EntityHandlerInterface, EntityCloneInter
$cloned_entity->setOwnerId($this->currentUser->id());
// Set owner on translations:
$languages = $cloned_entity->getTranslationLanguages();
foreach ($languages as $langcode => $language) {
$translation = $cloned_entity->getTranslation($langcode);
$translation->setOwnerId($this->currentUser->id());
@@ -131,7 +155,7 @@ class ContentEntityCloneBase implements EntityHandlerInterface, EntityCloneInter
// translation gets its publishing status updated according to the
// moderation state. After the entity is saved, we kick in the creation
// of translations of created moderation state entity.
foreach ($cloned_entity->getTranslationLanguages(TRUE) as $language) {
foreach ($languages as $language) {
$translation = $cloned_entity->getTranslation($language->getId());
$translation->set('moderation_state', $cloned_entity->get('moderation_state')->value);
}
@@ -147,9 +171,34 @@ class ContentEntityCloneBase implements EntityHandlerInterface, EntityCloneInter
$this->setTranslationModerationState($entity, $cloned_entity);
}
// Set inline block usage after setting the field otherwise there will be
// access issue.
$this->checkAndUpdateInlineBlockUsage($cloned_entity);
return $cloned_entity;
}
/**
* Gets the clone callback.
*
* @return bool
* The clone method, or FALSE when not cloneable.
*/
protected function getCloneCallback(FieldDefinitionInterface $field_definition) {
$cloneable_field_types = [
'entity_reference' => 'cloneReferencedEntities',
'entity_reference_revisions' => 'cloneReferencedEntities',
'layout_section' => 'cloneInlineBlockEntities',
];
$type_is_cloneable = isset($cloneable_field_types[$field_definition->getType()]);
if (($field_definition instanceof FieldConfigInterface) && $type_is_cloneable) {
return $cloneable_field_types[$field_definition->getType()];
}
return FALSE;
}
/**
* Sets the cloned entity's label.
*
@@ -176,6 +225,57 @@ class ContentEntityCloneBase implements EntityHandlerInterface, EntityCloneInter
}
}
/**
* Update inline block usage.
*
* @param \Drupal\Core\Entity\EntityInterface $cloned_entity
* The entity.
*/
protected function checkAndUpdateInlineBlockUsage(EntityInterface $cloned_entity) {
if ($this->moduleHandler->moduleExists('layout_builder')
&& $cloned_entity->hasField('layout_builder__layout')) {
// We cannot inject this one.
/** @var \Drupal\layout_builder\InlineBlockUsageInterface $inline_block_usage */
$inline_block_usage = \Drupal::service('inline_block.usage');
$block_content_storage = $this->entityTypeManager->getStorage('block_content');
$this->updateInlineBlockUsage($cloned_entity, $inline_block_usage, $block_content_storage);
}
}
/**
* Update inline block usage.
*
* @param \Drupal\Core\Entity\EntityInterface $cloned_entity
* The entity.
* @param \Drupal\layout_builder\InlineBlockUsageInterface $inline_block_usage
* The inline block usage service.
* @param \Drupal\Core\Entity\EntityStorageInterface $block_content_storage
* The block content's storage.
*/
protected function updateInlineBlockUsage(EntityInterface $cloned_entity, InlineBlockUsageInterface $inline_block_usage, EntityStorageInterface $block_content_storage) {
$sections = $cloned_entity->get('layout_builder__layout');
foreach ($sections as $value) {
$section = $value->section;
$components = $section->getComponents();
/** @var \Drupal\layout_builder\SectionComponent $component */
foreach ($components as $component) {
if (!isset($component->toArray()['configuration']['block_revision_id'])) {
continue;
}
$componentAsArray = $component->toArray();
$referenced_block = $block_content_storage->loadRevision($componentAsArray['configuration']['block_revision_id']);
if ($referenced_block) {
$inline_block_usage->addUsage($referenced_block->id(), $cloned_entity);
}
}
}
}
/**
* Clones referenced entities.
*
@@ -252,6 +352,83 @@ class ContentEntityCloneBase implements EntityHandlerInterface, EntityCloneInter
return $child_properties;
}
/**
* Clone inline block entities.
*
* @param \Drupal\Core\Field\FieldItemListInterface $field
* The field item.
* @param \Drupal\Core\Field\FieldConfigInterface $field_definition
* The field definition.
* @param array $properties
* All new properties to replace old.
* @param array $already_cloned
* List of all already cloned entities, used for circular references.
*
* @return array
* Referenced entities.
*/
protected function cloneInlineBlockEntities(FieldItemListInterface $field, FieldConfigInterface $field_definition, array $properties, array &$already_cloned) {
$referenced_entities = [];
foreach ($field as $sid => $value) {
if ($value->isEmpty()) {
continue;
}
$section = $value->section;
if (!$section) {
continue;
}
$components = $section->getComponents();
$section_array = $section->toArray();
foreach ($components as $component) {
// If it is not a revisionable block, skip.
if (!isset($component->toArray()['configuration']) || !isset($component->toArray()['configuration']['block_revision_id'])) {
continue;
}
$component_array = $component->toArray();
$configuration = $component_array['configuration'];
$referenced_entity = $this->entityTypeManager
->getStorage('block_content')
->loadRevision($configuration['block_revision_id']);
if ($referenced_entity instanceof BlockContentInterface) {
$child_properties = $this->getChildProperties($properties, $field_definition, $referenced_entity);
$component_array['configuration'] = $this->cloneInlineBlock($referenced_entity, $configuration, $child_properties['children'], $already_cloned);
$section_array['components'][$component_array['uuid']] = $component_array;
}
}
$cloned_section = Section::fromArray($section_array);
$referenced_entities[$sid] = $cloned_section;
}
return $referenced_entities;
}
/**
* Clone inline block.
*
* @param \Drupal\block_content\BlockContentInterface $referenced_entity
* The referenced block.
* @param array $block_configuration
* The configuration of the block.
* @param array $properties
* The entity clone properties.
* @param array $already_cloned
* Already cloned entities.
*
* @return array
* The modified block configuration.
*/
protected function cloneInlineBlock(BlockContentInterface $referenced_entity, array $block_configuration, array $properties, array &$already_cloned) {
$cloned_reference = $referenced_entity->createDuplicate();
/** @var \Drupal\entity_clone\EntityClone\EntityCloneInterface $entity_clone_handler */
$entity_clone_handler = $this->entityTypeManager->getHandler($referenced_entity->getEntityTypeId(), 'entity_clone');
$entity_clone_handler->cloneEntity($referenced_entity, $cloned_reference, $properties, $already_cloned);
$block_configuration['block_revision_id'] = $cloned_reference->getRevisionId();
return $block_configuration;
}
/**
* Create moderation_state translations for the cloned entities.
*
Loading