From 1c8c846ac101e77a200043f700fa40f3c8a11e0a Mon Sep 17 00:00:00 2001
From: Xaq Rothman <xaq@radcampaign.com>
Date: Mon, 22 Jan 2024 10:43:32 -0500
Subject: [PATCH 01/20] Apply patch from issue #3100117

---
 quick_node_clone.services.yml                 |   2 +-
 .../QuickNodeCloneEntityFormBuilder.php       | 148 ++++++++++++++----
 src/Form/QuickNodeCloneNodeForm.php           |  38 +++++
 3 files changed, 154 insertions(+), 34 deletions(-)

diff --git a/quick_node_clone.services.yml b/quick_node_clone.services.yml
index 3f7322c..ac46814 100644
--- a/quick_node_clone.services.yml
+++ b/quick_node_clone.services.yml
@@ -1,7 +1,7 @@
 services:
   quick_node_clone.entity.form_builder:
     class: Drupal\quick_node_clone\Entity\QuickNodeCloneEntityFormBuilder
-    arguments: ['@form_builder', '@entity_type.bundle.info', '@config.factory', '@module_handler', '@entity_type.manager', '@current_user', '@tempstore.private', '@string_translation']
+    arguments: ['@form_builder', '@entity_type.bundle.info', '@config.factory', '@module_handler', '@entity_type.manager', '@current_user', '@tempstore.private', '@string_translation', '@uuid']
   quick_node_clone.address_event_subscriber:
     class: Drupal\quick_node_clone\EventSubscriber\AddressEventSubscriber
     arguments: ['@tempstore.private', '@quick_node_clone.node_finder']
diff --git a/src/Entity/QuickNodeCloneEntityFormBuilder.php b/src/Entity/QuickNodeCloneEntityFormBuilder.php
index 361dbdf..37197d2 100755
--- a/src/Entity/QuickNodeCloneEntityFormBuilder.php
+++ b/src/Entity/QuickNodeCloneEntityFormBuilder.php
@@ -2,11 +2,13 @@
 
 namespace Drupal\quick_node_clone\Entity;
 
+use Drupal\Component\Uuid\UuidInterface;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Entity\EntityFormBuilder;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\FieldableEntityInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Form\FormBuilderInterface;
 use Drupal\Core\Form\FormState;
@@ -15,7 +17,9 @@ use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\StringTranslation\TranslationInterface;
 use Drupal\Core\TempStore\PrivateTempStoreFactory;
 use Drupal\group\Entity\GroupContent;
-use Drupal\node\Entity\Node;
+use Drupal\layout_builder\Plugin\Block\InlineBlock;
+use Drupal\layout_builder\SectionComponent;
+use Drupal\node\NodeInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -74,17 +78,27 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
    */
   protected $stringTranslation;
 
+  /**
+   * The UUID service.
+   *
+   * @var \Drupal\Component\Uuid\UuidInterface
+   */
+  protected $uuid;
+
   /**
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container) {
     return new static(
+      $container->get('form_builder'),
       $container->get('entity_type.bundle.info'),
       $container->get('config.factory'),
       $container->get('module_handler'),
       $container->get('entity_type.manager'),
       $container->get('current_user'),
-      $container->get('tempstore.private')
+      $container->get('tempstore.private'),
+      $container->get('string_translation'),
+      $container->get('uuid')
     );
   }
 
@@ -107,8 +121,10 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
    *   Private temp store factory.
    * @param \Drupal\Core\StringTranslation\TranslationInterface $stringTranslation
    *   The string translation service.
+   * @param \Drupal\Component\Uuid\UuidInterface $uuid
+   *   The UUID service.
    */
-  public function __construct(FormBuilderInterface $formBuilder, EntityTypeBundleInfoInterface $entityTypeBundleInfo, ConfigFactoryInterface $configFactory, ModuleHandlerInterface $moduleHandler, EntityTypeManagerInterface $entityTypeManager, AccountInterface $currentUser, PrivateTempStoreFactory $privateTempStoreFactory, TranslationInterface $stringTranslation) {
+  public function __construct(FormBuilderInterface $formBuilder, EntityTypeBundleInfoInterface $entityTypeBundleInfo, ConfigFactoryInterface $configFactory, ModuleHandlerInterface $moduleHandler, EntityTypeManagerInterface $entityTypeManager, AccountInterface $currentUser, PrivateTempStoreFactory $privateTempStoreFactory, TranslationInterface $stringTranslation, UuidInterface $uuid) {
     $this->formBuilder = $formBuilder;
     $this->entityTypeBundleInfo = $entityTypeBundleInfo;
     $this->configFactory = $configFactory;
@@ -117,6 +133,7 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
     $this->currentUser = $currentUser;
     $this->privateTempStoreFactory = $privateTempStoreFactory;
     $this->stringTranslation = $stringTranslation;
+    $this->uuid = $uuid;
   }
 
   /**
@@ -150,7 +167,8 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
     foreach ($new_node->getTranslationLanguages() as $langcode => $language) {
       /** @var \Drupal\node\Entity\Node $translated_node */
       $translated_node = $new_node->getTranslation($langcode);
-      $translated_node = $this->cloneParagraphs($translated_node);
+      $this->cloneParagraphs($translated_node);
+      $this->cloneInlineBlocks($translated_node);
       $this->moduleHandler->alter('cloned_node', $translated_node, $original_entity);
 
       // Unset excluded fields.
@@ -209,41 +227,105 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
    * If we do not clone the paragraphs attached to the node, the linked
    * paragraphs would be linked to two nodes which is not ideal.
    *
-   * @param \Drupal\node\Entity\Node $node
-   *   The node to clone.
-   *
-   * @return \Drupal\node\Entity\Node
-   *   The node with cloned paragraph fields.
+   * @param \Drupal\node\Entity\FieldableEntityInterface $entity
+   *   The entity being cloned.
    */
-  public function cloneParagraphs(Node $node) {
-    foreach ($node->getFieldDefinitions() as $field_definition) {
-      $field_storage_definition = $field_definition->getFieldStorageDefinition();
-      $field_settings = $field_storage_definition->getSettings();
-      $field_name = $field_storage_definition->getName();
-      if (isset($field_settings['target_type']) && $field_settings['target_type'] == "paragraph") {
-        if (!$node->get($field_name)->isEmpty()) {
-          foreach ($node->get($field_name) as $value) {
-            if ($value->entity) {
-              $value->entity = $value->entity->createDuplicate();
-              foreach ($value->entity->getFieldDefinitions() as $field_definition) {
-                $field_storage_definition = $field_definition->getFieldStorageDefinition();
-                $pfield_settings = $field_storage_definition->getSettings();
-                $pfield_name = $field_storage_definition->getName();
-
-                // Check whether this field is excluded and if so unset.
-                if ($this->excludeParagraphField($pfield_name, $value->entity->bundle())) {
-                  unset($value->entity->{$pfield_name});
-                }
-
-                $this->moduleHandler->alter('cloned_node_paragraph_field', $value->entity, $pfield_name, $pfield_settings);
-              }
-            }
+  public function cloneParagraphs(FieldableEntityInterface $entity) {
+    foreach ($entity->getFieldDefinitions() as $field_name => $field_definition) {
+      $field_settings = $field_definition->getFieldStorageDefinition()
+        ->getSettings();
+      if (($field_settings['target_type'] ?? NULL) != 'paragraph') {
+        continue;
+      }
+      foreach ($entity->$field_name as $paragraph_item) {
+        $paragraph = $paragraph_item->entity;
+        if (!$paragraph) {
+          continue;
+        }
+        $cloned_paragraph = $paragraph->createDuplicate();
+        $paragraph_item->entity = $cloned_paragraph;
+        foreach ($cloned_paragraph->getFieldDefinitions() as $paragraph_field_name => $paragraph_field_definition) {
+          if ($this->excludeParagraphField($paragraph_field_name, $cloned_paragraph->bundle())) {
+            unset($cloned_paragraph->$paragraph_field_name);
           }
+          $paragraph_field_storage_definition = $paragraph_field_definition
+            ->getFieldStorageDefinition();
+          $paragraph_field_settings = $paragraph_field_storage_definition
+            ->getSettings();
+          $this->moduleHandler->alter('cloned_node_paragraph_field', $cloned_paragraph, $paragraph_field_name, $paragraph_field_settings);
         }
       }
     }
+  }
+
+  /**
+   * Clone the inline blocks of a node's layout.
+   *
+   * For nodes that have layout builder enabled, the inline blocks needs
+   * be to cloned as well.
+   *
+   * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
+   *   The entity being cloned.
+   */
+  public function cloneInlineBlocks(FieldableEntityInterface $entity) {
+    $field_name = 'layout_builder__layout';
+
+    if (!$entity->hasField($field_name)) {
+      return;
+    }
 
-    return $node;
+    /** @var \Drupal\layout_builder\SectionListInterface $layout_field */
+    $layout_field = $entity->$field_name;
+
+    foreach ($layout_field->getSections() as $sid => $section) {
+      // Create a duplicate of each component.
+      foreach ($section->getComponents() as $component) {
+        $block = $component->getPlugin();
+
+        // Only clone inline blocks.
+        if (!$block instanceof InlineBlock) {
+          continue;
+        }
+
+        $component_array = $component->toArray();
+        $configuration = $component_array['configuration'];
+
+        // Fetch the block content.
+        $block_content = NULL;
+        if (!empty($configuration['block_serialized'])) {
+          $block_content = unserialize($configuration['block_serialized']);
+        }
+        elseif (!empty($configuration['block_revision_id'])) {
+          $block_content = $this->entityTypeManager->getStorage('block_content')
+            ->loadRevision($configuration['block_revision_id']);
+        }
+
+        // Create a duplicate block.
+        if ($block_content) {
+          /** @var \Drupal\block_content\BlockContentInterface $block_content */
+          $cloned_block_content = $block_content->createDuplicate();
+          $this->cloneParagraphs($cloned_block_content);
+
+          // Unset the revision and add the serialized block content.
+          $configuration['block_revision_id'] = NULL;
+          $configuration['block_serialized'] = serialize($cloned_block_content);
+        }
+
+        $new_component = new SectionComponent(
+          $this->uuid->generate(),
+          $component_array['region'],
+          $configuration,
+          $component_array['additional']
+        );
+
+        // Remove existing components from the section and append a fresh copy.
+        $section->insertAfterComponent($component->getUuid(), $new_component);
+        $section->removeComponent($component->getUuid());
+      }
+
+      $layout_field->insertSection($sid, $section);
+      $layout_field->removeSection($sid + 1);
+    }
   }
 
   /**
diff --git a/src/Form/QuickNodeCloneNodeForm.php b/src/Form/QuickNodeCloneNodeForm.php
index bd84d39..08595a2 100644
--- a/src/Form/QuickNodeCloneNodeForm.php
+++ b/src/Form/QuickNodeCloneNodeForm.php
@@ -3,6 +3,7 @@
 namespace Drupal\quick_node_clone\Form;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\layout_builder\InlineBlockEntityOperations;
 use Drupal\node\NodeForm;
 
 /**
@@ -38,7 +39,44 @@ class QuickNodeCloneNodeForm extends NodeForm {
     /** @var \Drupal\node\NodeInterface $node */
     $node = $this->entity;
     $insert = $node->isNew();
+
+    // Temporarily create a copy of the layout builder field for all
+    // translations since we need an entity ID to correctly create block usage.
+    // For the first save we clear the field so layout_builder_entity_presave()
+    // doesn't try to save our cloned blocks without an entity ID.
+    $layout_values = [];
+    if ($node->hasField('layout_builder__layout') && !$node->get('layout_builder__layout')->isEmpty()) {
+      foreach (array_keys($node->getTranslationLanguages()) as $langcode) {
+        $translation = $node->getTranslation($langcode);
+        $layout_values[$langcode] = clone $translation->get('layout_builder__layout');
+        $translation->set('layout_builder__layout', NULL);
+      }
+    }
+
     $node->save();
+
+    // When using layout builder, we need to make sure that all cloned blocks
+    // in all translations are saved to the database before we save the node.
+    // Since we need a node ID to properly link the usage, we unfortunately need
+    // to save the node twice. We use SynchronizableEntityTrait to allow modules
+    // to detect the duplicate save in other entity hooks.
+    if ($layout_values && $this->moduleHandler->moduleExists('layout_builder') && $this->moduleHandler->moduleExists('block_content')) {
+      /** @var \Drupal\layout_builder\InlineBlockEntityOperations $entity_operations */
+      $entity_operations = \Drupal::classResolver(InlineBlockEntityOperations::class);
+      foreach (array_keys($node->getTranslationLanguages()) as $langcode) {
+        $translation = $node->getTranslation($langcode);
+        // Restore the cloned layout builder field.
+        $translation->set('layout_builder__layout', $layout_values[$langcode]->getValue());
+        // Skip the presave for the default language since
+        // layout_builder_entity_presave() already handles that.
+        if ($langcode !== $node->language()->getId()) {
+          $entity_operations->handlePreSave($translation);
+        }
+        $translation->setNewRevision(FALSE);
+      }
+      $node->setSyncing(TRUE)->save();
+    }
+
     $node_link = $node->toLink($this->t('View'))->toString();
     $context = [
       '@type' => $node->getType(),
-- 
GitLab


From bf2c1ddbd51a40f7c234a5895f8de8e785f2a5ff Mon Sep 17 00:00:00 2001
From: Roman Paska <40929-Taran2L@users.noreply.drupalcode.org>
Date: Fri, 16 Feb 2024 17:17:43 +0000
Subject: [PATCH 02/20] Inject class_resolver service

---
 src/Form/QuickNodeCloneNodeForm.php | 49 ++++++++++++++++++++++++++++-
 1 file changed, 48 insertions(+), 1 deletion(-)

diff --git a/src/Form/QuickNodeCloneNodeForm.php b/src/Form/QuickNodeCloneNodeForm.php
index 1ccbe24..5d11ec9 100644
--- a/src/Form/QuickNodeCloneNodeForm.php
+++ b/src/Form/QuickNodeCloneNodeForm.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\quick_node_clone\Form;
 
+use Drupal\Core\DependencyInjection\ClassResolverInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\layout_builder\InlineBlockEntityOperations;
 use Drupal\node\NodeForm;
@@ -13,6 +14,52 @@ use Drupal\node\NodeForm;
  */
 class QuickNodeCloneNodeForm extends NodeForm {
 
+  /**
+   * The class resolver service.
+   *
+   * @var \Drupal\Core\DependencyInjection\ClassResolverInterface
+   */
+  protected $classResolver;
+
+  /**
+   * Constructs a NodeForm object.
+   *
+   * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
+   *   The entity repository.
+   * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
+   *   The factory for the temp store object.
+   * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
+   *   The entity type bundle service.
+   * @param \Drupal\Component\Datetime\TimeInterface $time
+   *   The time service.
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   The current user.
+   * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
+   *   The date formatter service.
+   * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
+   *   The class resolver service.
+   */
+  public function __construct(EntityRepositoryInterface $entity_repository, PrivateTempStoreFactory $temp_store_factory, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, TimeInterface $time = NULL, AccountInterface $current_user, DateFormatterInterface $date_formatter, ClassResolverInterface $class_resolver) {
+    parent::__construct($entity_repository, $temp_store_factory, $entity_type_bundle_info, $time, $current_user, $date_formatter);
+    $this->classResolver = $class_resolver;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity.repository'),
+      $container->get('tempstore.private'),
+      $container->get('entity_type.bundle.info'),
+      $container->get('datetime.time'),
+      $container->get('current_user'),
+      $container->get('date.formatter'),
+      $container->get('class_resolver')
+    );
+  }
+
+
   /**
    * {@inheritdoc}
    */
@@ -62,7 +109,7 @@ class QuickNodeCloneNodeForm extends NodeForm {
     // to detect the duplicate save in other entity hooks.
     if ($layout_values && $this->moduleHandler->moduleExists('layout_builder') && $this->moduleHandler->moduleExists('block_content')) {
       /** @var \Drupal\layout_builder\InlineBlockEntityOperations $entity_operations */
-      $entity_operations = \Drupal::classResolver(InlineBlockEntityOperations::class);
+      $entity_operations = $this->classResolver->getInstanceFromDefinition(InlineBlockEntityOperations::class);
       foreach (array_keys($node->getTranslationLanguages()) as $langcode) {
         $translation = $node->getTranslation($langcode);
         // Restore the cloned layout builder field.
-- 
GitLab


From 18fc16450238ff10b71217b03e12b93105daabd3 Mon Sep 17 00:00:00 2001
From: Roman Paska <40929-Taran2L@users.noreply.drupalcode.org>
Date: Fri, 16 Feb 2024 17:25:14 +0000
Subject: [PATCH 03/20] Update QuickNodeCloneNodeForm.php

---
 src/Form/QuickNodeCloneNodeForm.php | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/Form/QuickNodeCloneNodeForm.php b/src/Form/QuickNodeCloneNodeForm.php
index 5d11ec9..72dde93 100644
--- a/src/Form/QuickNodeCloneNodeForm.php
+++ b/src/Form/QuickNodeCloneNodeForm.php
@@ -2,10 +2,17 @@
 
 namespace Drupal\quick_node_clone\Form;
 
+use Drupal\Component\Datetime\TimeInterface;
+use Drupal\Core\Datetime\DateFormatterInterface;
 use Drupal\Core\DependencyInjection\ClassResolverInterface;
+use Drupal\Core\Entity\EntityRepositoryInterface;
+use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\TempStore\PrivateTempStoreFactory;
 use Drupal\layout_builder\InlineBlockEntityOperations;
 use Drupal\node\NodeForm;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Form controller for Quick Node Clone edit forms.
@@ -59,7 +66,6 @@ class QuickNodeCloneNodeForm extends NodeForm {
     );
   }
 
-
   /**
    * {@inheritdoc}
    */
-- 
GitLab


From 1991a06e2376bffde606edb6801f38c20d8a3443 Mon Sep 17 00:00:00 2001
From: Roman Paska <40929-Taran2L@users.noreply.drupalcode.org>
Date: Sat, 17 Feb 2024 10:34:43 +0000
Subject: [PATCH 04/20] Update file QuickNodeCloneEntityFormBuilder.php

---
 .../QuickNodeCloneEntityFormBuilder.php       | 32 +------------------
 1 file changed, 1 insertion(+), 31 deletions(-)

diff --git a/src/Entity/QuickNodeCloneEntityFormBuilder.php b/src/Entity/QuickNodeCloneEntityFormBuilder.php
index ab34a20..908fb71 100755
--- a/src/Entity/QuickNodeCloneEntityFormBuilder.php
+++ b/src/Entity/QuickNodeCloneEntityFormBuilder.php
@@ -28,12 +28,6 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
 class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
   use StringTranslationTrait;
 
-  /**
-   * The Form Builder.
-   *
-   * @var \Drupal\Core\Form\FormBuilderInterface
-   */
-  protected $formBuilder;
   /**
    * The Entity Bundle Type Info.
    *
@@ -52,12 +46,6 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
    * @var \Drupal\Core\Extension\ModuleHandlerInterface
    */
   protected $moduleHandler;
-  /**
-   * The Entity Type Manager.
-   *
-   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
-   */
-  protected $entityTypeManager;
   /**
    * The current user account.
    *
@@ -85,23 +73,6 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
    */
   protected $uuid;
 
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('form_builder'),
-      $container->get('entity_type.bundle.info'),
-      $container->get('config.factory'),
-      $container->get('module_handler'),
-      $container->get('entity_type.manager'),
-      $container->get('current_user'),
-      $container->get('tempstore.private'),
-      $container->get('string_translation'),
-      $container->get('uuid')
-    );
-  }
-
   /**
    * QuickNodeCloneEntityFormBuilder constructor.
    *
@@ -125,11 +96,10 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
    *   The UUID service.
    */
   public function __construct(FormBuilderInterface $formBuilder, EntityTypeBundleInfoInterface $entityTypeBundleInfo, ConfigFactoryInterface $configFactory, ModuleHandlerInterface $moduleHandler, EntityTypeManagerInterface $entityTypeManager, AccountInterface $currentUser, PrivateTempStoreFactory $privateTempStoreFactory, TranslationInterface $stringTranslation, UuidInterface $uuid) {
-    $this->formBuilder = $formBuilder;
+    parent::__construct(entityTypeManager, formBuilder);
     $this->entityTypeBundleInfo = $entityTypeBundleInfo;
     $this->configFactory = $configFactory;
     $this->moduleHandler = $moduleHandler;
-    $this->entityTypeManager = $entityTypeManager;
     $this->currentUser = $currentUser;
     $this->privateTempStoreFactory = $privateTempStoreFactory;
     $this->stringTranslation = $stringTranslation;
-- 
GitLab


From 4222653978e4c6908fdc58d9b37bd8a0567d2a78 Mon Sep 17 00:00:00 2001
From: Roman Paska <40929-Taran2L@users.noreply.drupalcode.org>
Date: Sat, 17 Feb 2024 11:10:13 +0000
Subject: [PATCH 05/20] Update file QuickNodeCloneEntityFormBuilder.php

---
 src/Entity/QuickNodeCloneEntityFormBuilder.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Entity/QuickNodeCloneEntityFormBuilder.php b/src/Entity/QuickNodeCloneEntityFormBuilder.php
index 908fb71..d4500e1 100755
--- a/src/Entity/QuickNodeCloneEntityFormBuilder.php
+++ b/src/Entity/QuickNodeCloneEntityFormBuilder.php
@@ -96,7 +96,7 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
    *   The UUID service.
    */
   public function __construct(FormBuilderInterface $formBuilder, EntityTypeBundleInfoInterface $entityTypeBundleInfo, ConfigFactoryInterface $configFactory, ModuleHandlerInterface $moduleHandler, EntityTypeManagerInterface $entityTypeManager, AccountInterface $currentUser, PrivateTempStoreFactory $privateTempStoreFactory, TranslationInterface $stringTranslation, UuidInterface $uuid) {
-    parent::__construct(entityTypeManager, formBuilder);
+    parent::__construct($entityTypeManager, $formBuilder);
     $this->entityTypeBundleInfo = $entityTypeBundleInfo;
     $this->configFactory = $configFactory;
     $this->moduleHandler = $moduleHandler;
-- 
GitLab


From 195f1c3045ece71797d883c8c2e373109bebeef3 Mon Sep 17 00:00:00 2001
From: Roman Paska <40929-Taran2L@users.noreply.drupalcode.org>
Date: Sat, 17 Feb 2024 11:10:31 +0000
Subject: [PATCH 06/20] Update file QuickNodeCloneEntityFormBuilder.php

---
 src/Entity/QuickNodeCloneEntityFormBuilder.php | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/Entity/QuickNodeCloneEntityFormBuilder.php b/src/Entity/QuickNodeCloneEntityFormBuilder.php
index d4500e1..6a78745 100755
--- a/src/Entity/QuickNodeCloneEntityFormBuilder.php
+++ b/src/Entity/QuickNodeCloneEntityFormBuilder.php
@@ -20,7 +20,6 @@ use Drupal\group\Entity\GroupContent;
 use Drupal\group\Entity\GroupRelationship;
 use Drupal\layout_builder\Plugin\Block\InlineBlock;
 use Drupal\layout_builder\SectionComponent;
-use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Builds entity forms.
-- 
GitLab


From 779a29c489f9fe84b273a4c3b92486595c5783a4 Mon Sep 17 00:00:00 2001
From: Sean Blommaert <info@seanblommaert.nl>
Date: Sat, 18 May 2024 08:12:11 +0200
Subject: [PATCH 07/20] Added a separate cloneLayoutSection method to allow
 other modules to reuse the code.

---
 .../QuickNodeCloneEntityFormBuilder.php       | 93 +++++++++++--------
 1 file changed, 54 insertions(+), 39 deletions(-)

diff --git a/src/Entity/QuickNodeCloneEntityFormBuilder.php b/src/Entity/QuickNodeCloneEntityFormBuilder.php
index 6a78745..a627604 100755
--- a/src/Entity/QuickNodeCloneEntityFormBuilder.php
+++ b/src/Entity/QuickNodeCloneEntityFormBuilder.php
@@ -19,6 +19,7 @@ use Drupal\Core\TempStore\PrivateTempStoreFactory;
 use Drupal\group\Entity\GroupContent;
 use Drupal\group\Entity\GroupRelationship;
 use Drupal\layout_builder\Plugin\Block\InlineBlock;
+use Drupal\layout_builder\Section;
 use Drupal\layout_builder\SectionComponent;
 
 /**
@@ -247,54 +248,68 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
     $layout_field = $entity->$field_name;
 
     foreach ($layout_field->getSections() as $sid => $section) {
-      // Create a duplicate of each component.
-      foreach ($section->getComponents() as $component) {
-        $block = $component->getPlugin();
-
-        // Only clone inline blocks.
-        if (!$block instanceof InlineBlock) {
-          continue;
-        }
+      $section = $this->cloneLayoutSection($section);
+      $layout_field->insertSection($sid, $section);
+      $layout_field->removeSection($sid + 1);
+    }
+  }
 
-        $component_array = $component->toArray();
-        $configuration = $component_array['configuration'];
+  /**
+   * Clone a layout section.
+   *
+   * For nodes that have layout builder enabled, the inline blocks in a section
+   * need be to cloned as well.
+   *
+   * @param \Drupal\layout_builder\Section $section
+   *   The section being cloned.
+   */
+  public function cloneLayoutSection(Section $section) {
+      // Create a duplicate of each component.
+    foreach ($section->getComponents() as $component) {
+      $block = $component->getPlugin();
 
-        // Fetch the block content.
-        $block_content = NULL;
-        if (!empty($configuration['block_serialized'])) {
-          $block_content = unserialize($configuration['block_serialized']);
-        }
-        elseif (!empty($configuration['block_revision_id'])) {
-          $block_content = $this->entityTypeManager->getStorage('block_content')
-            ->loadRevision($configuration['block_revision_id']);
-        }
+      // Only clone inline blocks.
+      if (!$block instanceof InlineBlock) {
+        continue;
+      }
 
-        // Create a duplicate block.
-        if ($block_content) {
-          /** @var \Drupal\block_content\BlockContentInterface $block_content */
-          $cloned_block_content = $block_content->createDuplicate();
-          $this->cloneParagraphs($cloned_block_content);
+      $component_array = $component->toArray();
+      $configuration = $component_array['configuration'];
 
-          // Unset the revision and add the serialized block content.
-          $configuration['block_revision_id'] = NULL;
-          $configuration['block_serialized'] = serialize($cloned_block_content);
-        }
+      // Fetch the block content.
+      $block_content = NULL;
+      if (!empty($configuration['block_serialized'])) {
+        $block_content = unserialize($configuration['block_serialized']);
+      }
+      elseif (!empty($configuration['block_revision_id'])) {
+        $block_content = $this->entityTypeManager->getStorage('block_content')
+          ->loadRevision($configuration['block_revision_id']);
+      }
 
-        $new_component = new SectionComponent(
-          $this->uuid->generate(),
-          $component_array['region'],
-          $configuration,
-          $component_array['additional']
-        );
+      // Create a duplicate block.
+      if ($block_content) {
+        /** @var \Drupal\block_content\BlockContentInterface $block_content */
+        $cloned_block_content = $block_content->createDuplicate();
+        $this->cloneParagraphs($cloned_block_content);
 
-        // Remove existing components from the section and append a fresh copy.
-        $section->insertAfterComponent($component->getUuid(), $new_component);
-        $section->removeComponent($component->getUuid());
+        // Unset the revision and add the serialized block content.
+        $configuration['block_revision_id'] = NULL;
+        $configuration['block_serialized'] = serialize($cloned_block_content);
       }
 
-      $layout_field->insertSection($sid, $section);
-      $layout_field->removeSection($sid + 1);
+      $new_component = new SectionComponent(
+        $this->uuid->generate(),
+        $component_array['region'],
+        $configuration,
+        $component_array['additional']
+      );
+
+      // Remove existing components from the section and append a fresh copy.
+      $section->insertAfterComponent($component->getUuid(), $new_component);
+      $section->removeComponent($component->getUuid());
     }
+
+    return $section;
   }
 
   /**
-- 
GitLab


From da84985f78f67300a2a1d48d756cdb3694e74634 Mon Sep 17 00:00:00 2001
From: Mark Dorison <mark@dorison.org>
Date: Tue, 23 Jul 2024 13:44:34 -0400
Subject: [PATCH 08/20] Fixed PHPCS issue.

---
 src/Entity/QuickNodeCloneEntityFormBuilder.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Entity/QuickNodeCloneEntityFormBuilder.php b/src/Entity/QuickNodeCloneEntityFormBuilder.php
index a627604..b766e24 100755
--- a/src/Entity/QuickNodeCloneEntityFormBuilder.php
+++ b/src/Entity/QuickNodeCloneEntityFormBuilder.php
@@ -264,7 +264,7 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
    *   The section being cloned.
    */
   public function cloneLayoutSection(Section $section) {
-      // Create a duplicate of each component.
+    // Create a duplicate of each component.
     foreach ($section->getComponents() as $component) {
       $block = $component->getPlugin();
 
-- 
GitLab


From 70716c83f69308b7d51a99020c9d6bfa9c44c01a Mon Sep 17 00:00:00 2001
From: Sean Blommaert <info@seanblommaert.nl>
Date: Mon, 4 Nov 2024 05:20:42 +0100
Subject: [PATCH 09/20] Call $entity_operations->handlePreSave for all
 languages since this is not called for syncing nodes.

---
 src/Form/QuickNodeCloneNodeForm.php | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/src/Form/QuickNodeCloneNodeForm.php b/src/Form/QuickNodeCloneNodeForm.php
index 72dde93..0f9e8e2 100644
--- a/src/Form/QuickNodeCloneNodeForm.php
+++ b/src/Form/QuickNodeCloneNodeForm.php
@@ -120,11 +120,7 @@ class QuickNodeCloneNodeForm extends NodeForm {
         $translation = $node->getTranslation($langcode);
         // Restore the cloned layout builder field.
         $translation->set('layout_builder__layout', $layout_values[$langcode]->getValue());
-        // Skip the presave for the default language since
-        // layout_builder_entity_presave() already handles that.
-        if ($langcode !== $node->language()->getId()) {
-          $entity_operations->handlePreSave($translation);
-        }
+        $entity_operations->handlePreSave($translation);
         $translation->setNewRevision(FALSE);
       }
       $node->setSyncing(TRUE)->save();
-- 
GitLab


From c96a59d6a909c09f5dc2f8a57515ba18c4f07a1d Mon Sep 17 00:00:00 2001
From: Roman Paska <40929-Taran2L@users.noreply.drupalcode.org>
Date: Fri, 8 Nov 2024 10:37:47 +0000
Subject: [PATCH 10/20] Make less intrusive changes

---
 src/Entity/QuickNodeCloneEntityFormBuilder.php | 5 ++---
 src/Form/QuickNodeCloneNodeForm.php            | 2 +-
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/src/Entity/QuickNodeCloneEntityFormBuilder.php b/src/Entity/QuickNodeCloneEntityFormBuilder.php
index 04166bc..62b1868 100755
--- a/src/Entity/QuickNodeCloneEntityFormBuilder.php
+++ b/src/Entity/QuickNodeCloneEntityFormBuilder.php
@@ -21,7 +21,6 @@ use Drupal\group\Entity\GroupRelationship;
 use Drupal\layout_builder\Plugin\Block\InlineBlock;
 use Drupal\layout_builder\Section;
 use Drupal\layout_builder\SectionComponent;
-use Drupal\node\Entity\Node;
 
 /**
  * Builds entity forms.
@@ -131,8 +130,8 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
     foreach ($new_node->getTranslationLanguages() as $langcode => $language) {
       /** @var \Drupal\node\Entity\Node $translated_node */
       $translated_node = $new_node->getTranslation($langcode);
-      $this->cloneParagraphs($translated_node);
-      $this->cloneInlineBlocks($translated_node);
+      $translated_node = $this->cloneParagraphs($translated_node);
+      $translated_node = $this->cloneInlineBlocks($translated_node);
       $this->moduleHandler->alter('cloned_node', $translated_node, $original_entity);
 
       // Unset excluded fields.
diff --git a/src/Form/QuickNodeCloneNodeForm.php b/src/Form/QuickNodeCloneNodeForm.php
index 0f9e8e2..d535a6d 100644
--- a/src/Form/QuickNodeCloneNodeForm.php
+++ b/src/Form/QuickNodeCloneNodeForm.php
@@ -46,7 +46,7 @@ class QuickNodeCloneNodeForm extends NodeForm {
    * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
    *   The class resolver service.
    */
-  public function __construct(EntityRepositoryInterface $entity_repository, PrivateTempStoreFactory $temp_store_factory, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, TimeInterface $time = NULL, AccountInterface $current_user, DateFormatterInterface $date_formatter, ClassResolverInterface $class_resolver) {
+  public function __construct(EntityRepositoryInterface $entity_repository, PrivateTempStoreFactory $temp_store_factory, EntityTypeBundleInfoInterface $entity_type_bundle_info, TimeInterface $time, AccountInterface $current_user, DateFormatterInterface $date_formatter, ClassResolverInterface $class_resolver) {
     parent::__construct($entity_repository, $temp_store_factory, $entity_type_bundle_info, $time, $current_user, $date_formatter);
     $this->classResolver = $class_resolver;
   }
-- 
GitLab


From 4b2e8cbbb1f18871d014b5a035e0651f9b925c5d Mon Sep 17 00:00:00 2001
From: Roman Paska <40929-Taran2L@users.noreply.drupalcode.org>
Date: Fri, 8 Nov 2024 10:49:22 +0000
Subject: [PATCH 11/20] Remove unrelated changes to paragraphs cloning

---
 .../QuickNodeCloneEntityFormBuilder.php       | 97 +++++++++++--------
 1 file changed, 59 insertions(+), 38 deletions(-)

diff --git a/src/Entity/QuickNodeCloneEntityFormBuilder.php b/src/Entity/QuickNodeCloneEntityFormBuilder.php
index 62b1868..9df59ee 100755
--- a/src/Entity/QuickNodeCloneEntityFormBuilder.php
+++ b/src/Entity/QuickNodeCloneEntityFormBuilder.php
@@ -21,6 +21,7 @@ use Drupal\group\Entity\GroupRelationship;
 use Drupal\layout_builder\Plugin\Block\InlineBlock;
 use Drupal\layout_builder\Section;
 use Drupal\layout_builder\SectionComponent;
+use Drupal\node\Entity\Node;
 
 /**
  * Builds entity forms.
@@ -28,6 +29,12 @@ use Drupal\layout_builder\SectionComponent;
 class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
   use StringTranslationTrait;
 
+/**
+   * The Form Builder.
+   *
+   * @var \Drupal\Core\Form\FormBuilderInterface
+   */
+  protected $formBuilder;
   /**
    * The Entity Bundle Type Info.
    *
@@ -46,6 +53,12 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
    * @var \Drupal\Core\Extension\ModuleHandlerInterface
    */
   protected $moduleHandler;
+  /**
+   * The Entity Type Manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
   /**
    * The current user account.
    *
@@ -88,11 +101,12 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
    * @param \Drupal\Component\Uuid\UuidInterface $uuid
    *   The UUID service.
    */
-  public function __construct(FormBuilderInterface $formBuilder, EntityTypeBundleInfoInterface $entityTypeBundleInfo, ConfigFactoryInterface $configFactory, ModuleHandlerInterface $moduleHandler, EntityTypeManagerInterface $entityTypeManager, AccountInterface $currentUser, PrivateTempStoreFactory $privateTempStoreFactory, TranslationInterface $stringTranslation, UuidInterface $uuid) {
-    parent::__construct($entityTypeManager, $formBuilder);
+  public function __construct(FormBuilderInterface $formBuilder, EntityTypeBundleInfoInterface $entityTypeBundleInfo, ConfigFactoryInterface $configFactory, ModuleHandlerInterface $moduleHandler, EntityTypeManagerInterface $entityTypeManager, AccountInterface $currentUser, PrivateTempStoreFactory $privateTempStoreFactory, TranslationInterface $stringTranslation, UuidInterface $uuid {
+    $this->formBuilder = $formBuilder;
     $this->entityTypeBundleInfo = $entityTypeBundleInfo;
     $this->configFactory = $configFactory;
     $this->moduleHandler = $moduleHandler;
+    $this->entityTypeManager = $entityTypeManager;
     $this->currentUser = $currentUser;
     $this->privateTempStoreFactory = $privateTempStoreFactory;
     $this->uuid = $uuid;
@@ -184,42 +198,49 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
     return $new_form;
   }
 
-  /**
-   * Clone the paragraphs of a node.
-   *
-   * If we do not clone the paragraphs attached to the node, the linked
-   * paragraphs would be linked to two nodes which is not ideal.
-   *
-   * @param \Drupal\node\Entity\FieldableEntityInterface $entity
-   *   The entity being cloned.
-   */
-  public function cloneParagraphs(FieldableEntityInterface $entity) {
-    foreach ($entity->getFieldDefinitions() as $field_name => $field_definition) {
-      $field_settings = $field_definition->getFieldStorageDefinition()
-        ->getSettings();
-      if (($field_settings['target_type'] ?? NULL) != 'paragraph') {
-        continue;
-      }
-      foreach ($entity->$field_name as $paragraph_item) {
-        $paragraph = $paragraph_item->entity;
-        if (!$paragraph) {
-          continue;
-        }
-        $cloned_paragraph = $paragraph->createDuplicate();
-        $paragraph_item->entity = $cloned_paragraph;
-        foreach ($cloned_paragraph->getFieldDefinitions() as $paragraph_field_name => $paragraph_field_definition) {
-          if ($this->excludeParagraphField($paragraph_field_name, $cloned_paragraph->bundle())) {
-            unset($cloned_paragraph->$paragraph_field_name);
-          }
-          $paragraph_field_storage_definition = $paragraph_field_definition
-            ->getFieldStorageDefinition();
-          $paragraph_field_settings = $paragraph_field_storage_definition
-            ->getSettings();
-          $this->moduleHandler->alter('cloned_node_paragraph_field', $cloned_paragraph, $paragraph_field_name, $paragraph_field_settings);
-        }
-      }
-    }
-  }
+ /**
+  * Clone the paragraphs of a node.
+  *
+  * If we do not clone the paragraphs attached to the node, the linked
+  * paragraphs would be linked to two nodes which is not ideal.
+  *
+  * @param \Drupal\node\Entity\Node $node
+  *   The node to clone.
+  *
+  * @return \Drupal\node\Entity\Node
+  *   The node with cloned paragraph fields.
+  */
+ public function cloneParagraphs(Node $node) {
+   foreach ($node->getFieldDefinitions() as $field_definition) {
+     $field_storage_definition = $field_definition->getFieldStorageDefinition();
+     $field_settings = $field_storage_definition->getSettings();
+     $field_name = $field_storage_definition->getName();
+     if (isset($field_settings['target_type']) && $field_settings['target_type'] == "paragraph") {
+       if (!$node->get($field_name)->isEmpty()) {
+         foreach ($node->get($field_name) as $value) {
+           if ($value->entity) {
+             $value->entity = $value->entity->createDuplicate();
+             foreach ($value->entity->getFieldDefinitions() as $field_definition) {
+               $field_storage_definition = $field_definition->getFieldStorageDefinition();
+               $pfield_settings = $field_storage_definition->getSettings();
+               $pfield_name = $field_storage_definition->getName();
+
+               // Check whether this field is excluded and if so unset.
+               if ($this->excludeParagraphField($pfield_name, $value->entity->bundle())) {
+                 unset($value->entity->{$pfield_name});
+               }
+
+               $this->moduleHandler->alter('cloned_node_paragraph_field', $value->entity, $pfield_name, $pfield_settings);
+             }
+           }
+         }
+       }
+     }
+   }
+
+   return $node;
+ }
+
 
   /**
    * Clone the inline blocks of a node's layout.
-- 
GitLab


From 84ed3fac295db77f84880dfcd44334f6daa3eba6 Mon Sep 17 00:00:00 2001
From: Roman Paska <40929-Taran2L@users.noreply.drupalcode.org>
Date: Fri, 8 Nov 2024 10:52:42 +0000
Subject: [PATCH 12/20] Typo fix

---
 src/Entity/QuickNodeCloneEntityFormBuilder.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Entity/QuickNodeCloneEntityFormBuilder.php b/src/Entity/QuickNodeCloneEntityFormBuilder.php
index 9df59ee..bb439c8 100755
--- a/src/Entity/QuickNodeCloneEntityFormBuilder.php
+++ b/src/Entity/QuickNodeCloneEntityFormBuilder.php
@@ -101,7 +101,7 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
    * @param \Drupal\Component\Uuid\UuidInterface $uuid
    *   The UUID service.
    */
-  public function __construct(FormBuilderInterface $formBuilder, EntityTypeBundleInfoInterface $entityTypeBundleInfo, ConfigFactoryInterface $configFactory, ModuleHandlerInterface $moduleHandler, EntityTypeManagerInterface $entityTypeManager, AccountInterface $currentUser, PrivateTempStoreFactory $privateTempStoreFactory, TranslationInterface $stringTranslation, UuidInterface $uuid {
+  public function __construct(FormBuilderInterface $formBuilder, EntityTypeBundleInfoInterface $entityTypeBundleInfo, ConfigFactoryInterface $configFactory, ModuleHandlerInterface $moduleHandler, EntityTypeManagerInterface $entityTypeManager, AccountInterface $currentUser, PrivateTempStoreFactory $privateTempStoreFactory, TranslationInterface $stringTranslation, UuidInterface $uuid) {
     $this->formBuilder = $formBuilder;
     $this->entityTypeBundleInfo = $entityTypeBundleInfo;
     $this->configFactory = $configFactory;
-- 
GitLab


From 85101235c7a6bf22d0189904d4e5907592d4cfda Mon Sep 17 00:00:00 2001
From: Roman Paska <40929-Taran2L@users.noreply.drupalcode.org>
Date: Fri, 8 Nov 2024 10:58:52 +0000
Subject: [PATCH 13/20] Fix codestyle

---
 .../QuickNodeCloneEntityFormBuilder.php       | 86 +++++++++----------
 1 file changed, 43 insertions(+), 43 deletions(-)

diff --git a/src/Entity/QuickNodeCloneEntityFormBuilder.php b/src/Entity/QuickNodeCloneEntityFormBuilder.php
index bb439c8..da65407 100755
--- a/src/Entity/QuickNodeCloneEntityFormBuilder.php
+++ b/src/Entity/QuickNodeCloneEntityFormBuilder.php
@@ -29,7 +29,7 @@ use Drupal\node\Entity\Node;
 class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
   use StringTranslationTrait;
 
-/**
+  /**
    * The Form Builder.
    *
    * @var \Drupal\Core\Form\FormBuilderInterface
@@ -198,48 +198,48 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
     return $new_form;
   }
 
- /**
-  * Clone the paragraphs of a node.
-  *
-  * If we do not clone the paragraphs attached to the node, the linked
-  * paragraphs would be linked to two nodes which is not ideal.
-  *
-  * @param \Drupal\node\Entity\Node $node
-  *   The node to clone.
-  *
-  * @return \Drupal\node\Entity\Node
-  *   The node with cloned paragraph fields.
-  */
- public function cloneParagraphs(Node $node) {
-   foreach ($node->getFieldDefinitions() as $field_definition) {
-     $field_storage_definition = $field_definition->getFieldStorageDefinition();
-     $field_settings = $field_storage_definition->getSettings();
-     $field_name = $field_storage_definition->getName();
-     if (isset($field_settings['target_type']) && $field_settings['target_type'] == "paragraph") {
-       if (!$node->get($field_name)->isEmpty()) {
-         foreach ($node->get($field_name) as $value) {
-           if ($value->entity) {
-             $value->entity = $value->entity->createDuplicate();
-             foreach ($value->entity->getFieldDefinitions() as $field_definition) {
-               $field_storage_definition = $field_definition->getFieldStorageDefinition();
-               $pfield_settings = $field_storage_definition->getSettings();
-               $pfield_name = $field_storage_definition->getName();
-
-               // Check whether this field is excluded and if so unset.
-               if ($this->excludeParagraphField($pfield_name, $value->entity->bundle())) {
-                 unset($value->entity->{$pfield_name});
-               }
-
-               $this->moduleHandler->alter('cloned_node_paragraph_field', $value->entity, $pfield_name, $pfield_settings);
-             }
-           }
-         }
-       }
-     }
-   }
-
-   return $node;
- }
+  /**
+   * Clone the paragraphs of a node.
+   *
+   * If we do not clone the paragraphs attached to the node, the linked
+   * paragraphs would be linked to two nodes which is not ideal.
+   *
+   * @param \Drupal\node\Entity\Node $node
+   *   The node to clone.
+   *
+   * @return \Drupal\node\Entity\Node
+   *   The node with cloned paragraph fields.
+   */
+  public function cloneParagraphs(Node $node) {
+    foreach ($node->getFieldDefinitions() as $field_definition) {
+      $field_storage_definition = $field_definition->getFieldStorageDefinition();
+      $field_settings = $field_storage_definition->getSettings();
+      $field_name = $field_storage_definition->getName();
+      if (isset($field_settings['target_type']) && $field_settings['target_type'] == "paragraph") {
+        if (!$node->get($field_name)->isEmpty()) {
+          foreach ($node->get($field_name) as $value) {
+            if ($value->entity) {
+              $value->entity = $value->entity->createDuplicate();
+              foreach ($value->entity->getFieldDefinitions() as $field_definition) {
+                $field_storage_definition = $field_definition->getFieldStorageDefinition();
+                $pfield_settings = $field_storage_definition->getSettings();
+                $pfield_name = $field_storage_definition->getName();
+
+                // Check whether this field is excluded and if so unset.
+                if ($this->excludeParagraphField($pfield_name, $value->entity->bundle())) {
+                  unset($value->entity->{$pfield_name});
+                }
+
+                $this->moduleHandler->alter('cloned_node_paragraph_field', $value->entity, $pfield_name, $pfield_settings);
+              }
+            }
+          }
+        }
+      }
+    }
+
+    return $node;
+  }
 
 
   /**
-- 
GitLab


From f22867b68f35885868bc032131b74a204015bb51 Mon Sep 17 00:00:00 2001
From: Roman Paska <40929-Taran2L@users.noreply.drupalcode.org>
Date: Fri, 8 Nov 2024 11:09:02 +0000
Subject: [PATCH 14/20] Codestyle + unit tests fix

---
 src/Entity/QuickNodeCloneEntityFormBuilder.php | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/Entity/QuickNodeCloneEntityFormBuilder.php b/src/Entity/QuickNodeCloneEntityFormBuilder.php
index da65407..7f14588 100755
--- a/src/Entity/QuickNodeCloneEntityFormBuilder.php
+++ b/src/Entity/QuickNodeCloneEntityFormBuilder.php
@@ -145,7 +145,7 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
       /** @var \Drupal\node\Entity\Node $translated_node */
       $translated_node = $new_node->getTranslation($langcode);
       $translated_node = $this->cloneParagraphs($translated_node);
-      $translated_node = $this->cloneInlineBlocks($translated_node);
+      $this->cloneInlineBlocks($translated_node);
       $this->moduleHandler->alter('cloned_node', $translated_node, $original_entity);
 
       // Unset excluded fields.
@@ -241,7 +241,6 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
     return $node;
   }
 
-
   /**
    * Clone the inline blocks of a node's layout.
    *
-- 
GitLab


From 68e383e43416c780d30b1bdaf6c36635477cbe54 Mon Sep 17 00:00:00 2001
From: Roman Paska <40929-Taran2L@users.noreply.drupalcode.org>
Date: Fri, 8 Nov 2024 11:21:36 +0000
Subject: [PATCH 15/20] Add a BC layer for the service constructior agruments
 change

---
 src/Entity/QuickNodeCloneEntityFormBuilder.php | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/Entity/QuickNodeCloneEntityFormBuilder.php b/src/Entity/QuickNodeCloneEntityFormBuilder.php
index 7f14588..6e239ee 100755
--- a/src/Entity/QuickNodeCloneEntityFormBuilder.php
+++ b/src/Entity/QuickNodeCloneEntityFormBuilder.php
@@ -101,7 +101,7 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
    * @param \Drupal\Component\Uuid\UuidInterface $uuid
    *   The UUID service.
    */
-  public function __construct(FormBuilderInterface $formBuilder, EntityTypeBundleInfoInterface $entityTypeBundleInfo, ConfigFactoryInterface $configFactory, ModuleHandlerInterface $moduleHandler, EntityTypeManagerInterface $entityTypeManager, AccountInterface $currentUser, PrivateTempStoreFactory $privateTempStoreFactory, TranslationInterface $stringTranslation, UuidInterface $uuid) {
+  public function __construct(FormBuilderInterface $formBuilder, EntityTypeBundleInfoInterface $entityTypeBundleInfo, ConfigFactoryInterface $configFactory, ModuleHandlerInterface $moduleHandler, EntityTypeManagerInterface $entityTypeManager, AccountInterface $currentUser, PrivateTempStoreFactory $privateTempStoreFactory, TranslationInterface $stringTranslation, ?UuidInterface $uuid = NULL) {
     $this->formBuilder = $formBuilder;
     $this->entityTypeBundleInfo = $entityTypeBundleInfo;
     $this->configFactory = $configFactory;
@@ -109,6 +109,12 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
     $this->entityTypeManager = $entityTypeManager;
     $this->currentUser = $currentUser;
     $this->privateTempStoreFactory = $privateTempStoreFactory;
+    if (is_null($uuid) {
+      @trigger_error('Calling ' . __METHOD__ . '() without the $uuid argument is deprecated in quick_node_clone:1.20.0 and will be required in quick_node_clone:2.0.0. See https://www.drupal.org/node/3486344', E_USER_DEPRECATED);
+
+      // @phpstan-ignore-next-line
+      $uuid = \Drupal::service('uuid');
+    }
     $this->uuid = $uuid;
     $this->setStringTranslation($stringTranslation);
   }
-- 
GitLab


From 871411fa7accc7c18b149c4a948ded71faccbfac Mon Sep 17 00:00:00 2001
From: Roman Paska <40929-Taran2L@users.noreply.drupalcode.org>
Date: Fri, 8 Nov 2024 11:24:29 +0000
Subject: [PATCH 16/20] Typo (again)

---
 src/Entity/QuickNodeCloneEntityFormBuilder.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Entity/QuickNodeCloneEntityFormBuilder.php b/src/Entity/QuickNodeCloneEntityFormBuilder.php
index 6e239ee..03199f7 100755
--- a/src/Entity/QuickNodeCloneEntityFormBuilder.php
+++ b/src/Entity/QuickNodeCloneEntityFormBuilder.php
@@ -109,7 +109,7 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
     $this->entityTypeManager = $entityTypeManager;
     $this->currentUser = $currentUser;
     $this->privateTempStoreFactory = $privateTempStoreFactory;
-    if (is_null($uuid) {
+    if (is_null($uuid)) {
       @trigger_error('Calling ' . __METHOD__ . '() without the $uuid argument is deprecated in quick_node_clone:1.20.0 and will be required in quick_node_clone:2.0.0. See https://www.drupal.org/node/3486344', E_USER_DEPRECATED);
 
       // @phpstan-ignore-next-line
-- 
GitLab


From a1d5696fa7f07e266575afdb06194c9e90fd7967 Mon Sep 17 00:00:00 2001
From: David Blankenship <david.blankenship@yale.edu>
Date: Wed, 20 Nov 2024 17:00:07 -0500
Subject: [PATCH 17/20] fix(cloneParagraph): define mixed parameter

Defining this as only taking nodes is not correct since BlockContent can
be passed into it.  This is a first stab to get this to work on existing
instances while we work through a better way to handle this.
---
 .../QuickNodeCloneEntityFormBuilder.php       | 20 +++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/src/Entity/QuickNodeCloneEntityFormBuilder.php b/src/Entity/QuickNodeCloneEntityFormBuilder.php
index 03199f7..206297a 100755
--- a/src/Entity/QuickNodeCloneEntityFormBuilder.php
+++ b/src/Entity/QuickNodeCloneEntityFormBuilder.php
@@ -205,25 +205,25 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
   }
 
   /**
-   * Clone the paragraphs of a node.
+   * Clone the paragraphs of a node or a block content item.
    *
    * If we do not clone the paragraphs attached to the node, the linked
    * paragraphs would be linked to two nodes which is not ideal.
    *
-   * @param \Drupal\node\Entity\Node $node
-   *   The node to clone.
+   * @param mixed $item
+   *   The item to clone.
    *
-   * @return \Drupal\node\Entity\Node
-   *   The node with cloned paragraph fields.
+   * @return mixed
+   *   The item with cloned paragraph fields.
    */
-  public function cloneParagraphs(Node $node) {
-    foreach ($node->getFieldDefinitions() as $field_definition) {
+  public function cloneParagraphs(mixed $item) {
+    foreach ($item->getFieldDefinitions() as $field_definition) {
       $field_storage_definition = $field_definition->getFieldStorageDefinition();
       $field_settings = $field_storage_definition->getSettings();
       $field_name = $field_storage_definition->getName();
       if (isset($field_settings['target_type']) && $field_settings['target_type'] == "paragraph") {
-        if (!$node->get($field_name)->isEmpty()) {
-          foreach ($node->get($field_name) as $value) {
+        if (!$item->get($field_name)->isEmpty()) {
+          foreach ($item->get($field_name) as $value) {
             if ($value->entity) {
               $value->entity = $value->entity->createDuplicate();
               foreach ($value->entity->getFieldDefinitions() as $field_definition) {
@@ -244,7 +244,7 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
       }
     }
 
-    return $node;
+    return $item;
   }
 
   /**
-- 
GitLab


From 52427144a1ce2c10f3f2c24a4aacff08a98d7353 Mon Sep 17 00:00:00 2001
From: Roman Paska <40929-Taran2L@users.noreply.drupalcode.org>
Date: Thu, 21 Nov 2024 10:15:06 +0000
Subject: [PATCH 18/20] Bring back FieldableEntityInterface

---
 .../QuickNodeCloneEntityFormBuilder.php       | 24 +++++++++----------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/src/Entity/QuickNodeCloneEntityFormBuilder.php b/src/Entity/QuickNodeCloneEntityFormBuilder.php
index 206297a..cd848db 100755
--- a/src/Entity/QuickNodeCloneEntityFormBuilder.php
+++ b/src/Entity/QuickNodeCloneEntityFormBuilder.php
@@ -205,25 +205,25 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
   }
 
   /**
-   * Clone the paragraphs of a node or a block content item.
+   * Clone the paragraphs of an entity.
    *
-   * If we do not clone the paragraphs attached to the node, the linked
-   * paragraphs would be linked to two nodes which is not ideal.
+   * If we do not clone the paragraphs attached to the entity, the linked
+   * paragraphs would be linked to two entities which is not ideal.
    *
-   * @param mixed $item
-   *   The item to clone.
+   * @param \Drupal\node\Entity\FieldableEntityInterface $entity
+   *   The entity to clone.
    *
-   * @return mixed
-   *   The item with cloned paragraph fields.
+   * @return \Drupal\node\Entity\FieldableEntityInterface
+   *   The entity with cloned paragraph fields.
    */
-  public function cloneParagraphs(mixed $item) {
-    foreach ($item->getFieldDefinitions() as $field_definition) {
+  public function cloneParagraphs(FieldableEntityInterface $entity) {
+    foreach ($entity->getFieldDefinitions() as $field_definition) {
       $field_storage_definition = $field_definition->getFieldStorageDefinition();
       $field_settings = $field_storage_definition->getSettings();
       $field_name = $field_storage_definition->getName();
       if (isset($field_settings['target_type']) && $field_settings['target_type'] == "paragraph") {
-        if (!$item->get($field_name)->isEmpty()) {
-          foreach ($item->get($field_name) as $value) {
+        if (!$entity->get($field_name)->isEmpty()) {
+          foreach ($entity->get($field_name) as $value) {
             if ($value->entity) {
               $value->entity = $value->entity->createDuplicate();
               foreach ($value->entity->getFieldDefinitions() as $field_definition) {
@@ -244,7 +244,7 @@ class QuickNodeCloneEntityFormBuilder extends EntityFormBuilder {
       }
     }
 
-    return $item;
+    return $entity;
   }
 
   /**
-- 
GitLab


From 6e46d31c5a05cf14fcfe329628b28e41fe6c9bfd Mon Sep 17 00:00:00 2001
From: Roman Paska <40929-Taran2L@users.noreply.drupalcode.org>
Date: Thu, 21 Nov 2024 10:19:59 +0000
Subject: [PATCH 19/20] Fix PHPCS

---
 src/Entity/QuickNodeCloneEntityFormBuilder.php | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/Entity/QuickNodeCloneEntityFormBuilder.php b/src/Entity/QuickNodeCloneEntityFormBuilder.php
index cd848db..71049f9 100755
--- a/src/Entity/QuickNodeCloneEntityFormBuilder.php
+++ b/src/Entity/QuickNodeCloneEntityFormBuilder.php
@@ -21,7 +21,6 @@ use Drupal\group\Entity\GroupRelationship;
 use Drupal\layout_builder\Plugin\Block\InlineBlock;
 use Drupal\layout_builder\Section;
 use Drupal\layout_builder\SectionComponent;
-use Drupal\node\Entity\Node;
 
 /**
  * Builds entity forms.
-- 
GitLab


From 814e7eb374b9ac06f242e5df91e6b96a835f2e5b Mon Sep 17 00:00:00 2001
From: Tatiana Kiseleva <tatiana.kiseleva@jakala.com>
Date: Mon, 13 Jan 2025 13:33:39 +0700
Subject: [PATCH 20/20] 3100117: Skip restore NULL layout for node translation.

---
 src/Form/QuickNodeCloneNodeForm.php | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/Form/QuickNodeCloneNodeForm.php b/src/Form/QuickNodeCloneNodeForm.php
index d535a6d..889e6d3 100644
--- a/src/Form/QuickNodeCloneNodeForm.php
+++ b/src/Form/QuickNodeCloneNodeForm.php
@@ -119,9 +119,11 @@ class QuickNodeCloneNodeForm extends NodeForm {
       foreach (array_keys($node->getTranslationLanguages()) as $langcode) {
         $translation = $node->getTranslation($langcode);
         // Restore the cloned layout builder field.
-        $translation->set('layout_builder__layout', $layout_values[$langcode]->getValue());
-        $entity_operations->handlePreSave($translation);
-        $translation->setNewRevision(FALSE);
+        if (!$layout_values[$langcode]->isEmpty()) {
+          $translation->set('layout_builder__layout', $layout_values[$langcode]->getValue());
+          $entity_operations->handlePreSave($translation);
+          $translation->setNewRevision(FALSE);
+        }
       }
       $node->setSyncing(TRUE)->save();
     }
-- 
GitLab