Hi,

I created issues in the two widely used clone modules, which are both affected by the same problem:
Cloning a node containing a layout paragraph results in broken structure as the contained paragraphs are moved into "Disabled Items" region.

See

Does the maintainer or anyone else have an idea
a) why this happens
b) where and how this could be fixed
c) how it affects 2.x?

Thank you very very much in advance!

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

Anybody created an issue. See original summary.

anybody’s picture

The two hidden fields in each layout paragraph have the wrong value in the clone. How are they filled typically?

  • layout-paragraphs-region
  • layout-paragraphs-parent-uuid

For example, all paragraph contents have:
layout-paragraphs-region="_disabled"

And I found this code mentioning the "replicate" module in layout_paragraphs.module, line 83:

/**
 * Implements hook_ENTITY_presave().
 *
 * Updates references to parent layout section uuids in cases
 * where paragraphs are duplicated, for example when using the
 * Replicate module.
 *
 * @todo Consider changing approach if this issue is addressed in core:
 * https://www.drupal.org/project/drupal/issues/3040556
 * (It is not possible to react to an entity being duplicated)
 */
function layout_paragraphs_paragraph_presave(Paragraph $paragraph) {
  $behavior_settings = $paragraph->getAllBehaviorSettings();
  $layout_paragraphs_settings = $behavior_settings['layout_paragraphs'] ?? [];
  if (!empty($layout_paragraphs_settings['parent_uuid']) && $parent = $paragraph->getParentEntity()) {
    $parent_field = $paragraph->get('parent_field_name');
    $field_name = $parent_field->first()->getString();
    $item_list = $parent->get($field_name);
    /** @var \Drupal\Core\Field\EntityReferenceFieldItemList $item_list */
    $sibling_paragraphs = $item_list->referencedEntities();
    if (count($sibling_paragraphs)) {
      $uuid_map = [];
      foreach ($sibling_paragraphs as $delta => $item) {
        $uuid_map[$item->uuid()] = $delta;
      }
      $parent_delta = $uuid_map[$layout_paragraphs_settings['parent_uuid']] ?? NULL;
      // If the parent layout section is correctly referenced via
      // parent_uuid, save the parent delta so we can correctly map
      // uuids using the delta if content is duplicated.
      if (isset($parent_delta)) {
        $layout_paragraphs_settings['parent_delta'] = $parent_delta;
      }
      // If the parent layout section cannot be found via parent_uuid,
      // use parent_delta to find and save the correct parent_uuid.
      // This should only happen when the paragraph is being duplicated,
      // and the parent layout section's uuid no longer matches parent_uuid.
      elseif (isset($layout_paragraphs_settings['parent_delta'], $sibling_paragraphs[$layout_paragraphs_settings['parent_delta']])) {
        $updated_parent_uuid = $sibling_paragraphs[$layout_paragraphs_settings['parent_delta']]->uuid();
        $layout_paragraphs_settings['parent_uuid'] = $updated_parent_uuid;
      }
      // Since paragraph::preSave() has already been called,
      // we have to set the serialized behavior settings directly
      // rather than using setBehaviorSettings().
      $behavior_settings['layout_paragraphs'] = $layout_paragraphs_settings;
      $paragraph->set('behavior_settings', serialize($behavior_settings));
    }
  }
}

on the one hand I think this should then work with entity_clone (but doesn't) and on the other hand it won't work for quick_node_clone as it prefills the clone form with copies. So we'd need a different strategy there?

Replicate seems pretty dead for Drupal 8 / 9?

anybody’s picture

Ok to me it seems that Paragraph::createDuplicate() which is for exmaple used in Quick Node Clone does NOT clone behaviourSettings, is that correct?

While Layout Paragraphs uses behaviourSettings to store this information. Furthermore would the parent_uuid have to be updated after getting the $paragraphs->createDuplicate() happened?

anybody’s picture

Yes that was very close to the reason :)

Indeed createDuplicate() also duplicates the Behaviour Settings, but createDuplicate() also correctly renews UUIDs so that parent_uuid values are pointing to the old paragraphs and have to be updated!

So what has to be done here is to save the old paragraph UUIDs from before the cloning and after the cloning and replace the old ones with the new ones.

I created example working code directly in Quick node clone, see https://www.drupal.org/project/quick_node_clone/issues/3218222#comment-1...

But I guess it shouldn't be Quick Node Clone specific. What we need is a hook on Entity::createDuplicate() (e.g. Paragraph::createDuplicate()) which provides us with the old and the duplicate paragraph entity and put the parent_uuid logic there.

justin2pin’s picture

Category: Support request » Bug report
Status: Active » Needs work

@Anybody - thanks for digging into this. I think the root issue is #3040556: It is not possible to react to an entity being duplicated -- as you pointed out in the comments on that issue. Currently there isn't a consistent way to respond to an entity being duplicated. The goal in layout_paragraphs_paragraph_presave() is provide a work-around that addresses the broadest set of use-cases possible, but obviously it's still not broad enough :).

Also, I'm surprised Quick Node Clone isn't triggering hook_paragraph_presave().

Regardless, we need to get this working for both 1.0.x and 2.0.x. We're focussing exclusively on the 2.0.x branch at this time, but am happy to back-port a solution, once we have one. Also happy to review patches for 1.0.x, and a fix for this would definitely be applied to 2.0.x. Thanks again.

anybody’s picture

Hi @just2pin and thank you very much for your fast reply!

The reason why Quick Node Clone does not call hook_paragraph_presave() is that it only prefills the form with the cloned values, but does not save them yet. It only creates the copies without calling ->save() - which is a quite helpful way to create entity copies. Entity clone creates the clone first. Anyway it has the same problems as written above, I didn't test why as I like the Quick Node Clone strategy.

So what would be your suggestion on how to proceed here? I guess for Entity clone it might be solvable by debugging the presave hook. For Quick Node Clone I have no clue until #3040556: It is not possible to react to an entity being duplicated is fixed. But as you know the layout_paragraphs code better than me, it would be very helpful if you could add some information if the Quick Node Clone patch won't work for 2.x what to change.

Any further ideas welcome! :)

nvaken’s picture

We we're experiencing the same issues. To make sure we're not creating another workaround for this issue, I've been inspecting `layout_paragraphs_paragraph_presave()` and why it does not work on entity_clone.

As far as I can tell, entity_clone is doing the following;

  1. Clone the node, cloned node still has old references in the paragraph field.
  2. Loop and clone every reference (if need be) in the node. In each iteration, layout_paragraphs_paragraph_presave() is trigged. Note that it will see only old references.
  3. Save the newly cloned references into the node.

I'm not entirely sure why the presave hook would work in the replicate module, since the parenting node will probably never have new references until the references have been created and re-referenced in the node and the node has been saved... Still not sure if I'm on the right track here. Will try to find time to investigate further.

anybody’s picture

Version: 1.0.x-dev » 2.0.x-dev

As @justin2pin wrote in #5 we should focus on 2.x version and I think we don't need a backport, as 1.x can hopefully be deprecated with a full and clean upgrade path to 2.x!

So let's use the information from above for 2.x and ensure cloning will work there, at least with any cloning module.

andrewmriv’s picture

StatusFileSize
new4.33 KB

I created a fix for the Entity Clone module.

When cloning using Entity Clone, it causes hook_ENTITY_presave() to trigger in the layout_paragraphs.module file. This patch adds a check to see if $behavior_settings['layout_paragraphs']['clone'] is TRUE. If it is, it skips the usual steps that take place when saving a layout paragraph node.

This patch is required for the solution in #5 to work.

Tested with 8.x-1.x of Entity Clone and 8.x-1.x of Layout Paragraphs.
If I get the chance, I will also test with with 2.x of Layout Paragraphs.

Edit - If you are not using the Replicate module, look at #23 instead of the patch in this comment.

anybody’s picture

Status: Needs work » Needs review

GREAT work @andrewmriv!! :)

Yes would be great if you could test that for 2.x and create a merge request for that. 1.x patch is ready for review I guess?

justin2pin’s picture

StatusFileSize
new8.36 KB

First, thanks for all the work on this. Great to see a working patch for the 1.x branch.

I think ultimately we need a solution that is completely native to Layout Paragraphs, and doesn't require a specific patch to other modules. With that in mind, I created a new service – LayoutParagraphsCloneService – to help (a) determine if we are currently cloning content, and (b) re-map UUIDs when we are.

Patch is attached. Feedback would be greatly appreciated.

Status: Needs review » Needs work

The last submitted patch, 11: 3218226-11.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

justin2pin’s picture

Status: Needs work » Needs review
StatusFileSize
new8.3 KB

Rerolled to fix for coding standards (two unnecessary use statements).

Status: Needs review » Needs work

The last submitted patch, 13: 3218226-12.patch, failed testing. View results

justin2pin’s picture

Status: Needs work » Needs review
StatusFileSize
new12.75 KB

One more time, now correctly including the new file :) ...

anybody’s picture

Hi @justin2pin,
thank you very much for your patch! This is quite complex.

How do we use it? Does it work with Quick Node Clone and Entity Clone?
Or is it about duplicating single paragraphs and not the parent entity / node?

davisben’s picture

StatusFileSize
new11.88 KB

The patch isn't applying anymore. Here's a reroll.

anybody’s picture

Title: Cloning Entities with Layout Paragraphs breaks structure and moves subparagraphs into Disabled Items » Cloning Entities containing Layout Paragraphs breaks structure

Clarified the title for 2.x

realityloop made their first commit to this issue’s fork.

realityloop’s picture

Patch no longer applied so I rerolled it as a merge request instead.

realityloop’s picture

Status: Needs review » Needs work

This currently causes the Drupal site to crash when enabling the module (my MR diff)

andrewmriv’s picture

StatusFileSize
new4.35 KB

If anyone was using my 1.x solution from #9, this new patch also includes a fix. It was a 1 line change in 3247496: Cannot place existing paragraphs into new sections without saving new section first..

I only recommend this patch if you are using Entity Clone and not Replicate as it has not been tested with that module.

hudri’s picture

Did some manual tests of justin2pin's patch from #15 (using 2.0.0-beta9):

You just need to add the patch in the composer.json, the presave hook will handle everything, the cloning modules don't need any modifications.

  • Works with Replicate
  • Works with QuickNodeClone
  • Does not work with Entity clone, but this is due critical bugs in Entity clone itself (see issue 3060223, incorrect parent_id on clone source after cloning)
anybody’s picture

Thank you for wrapping this up so great @hudri! So should we set #15 RTBC? Or does it need a reroll against latest 2.0.x-dev? Then it would be cool if someone could do this with an interdiff.

Nice to see this close to be fixed! :)

justin2pin’s picture

After working more on this I think we need to go in a different direction. Until #3040556: It is not possible to react to an entity being duplicated is resolved, there just isn't a good universal way to determine if an entity (and its reference revisions field) is being cloned. For now, we're going to implement hooks (or subscribe to events) for major clone/replicate modules. I'm going to add a separate feature request issue to track this work.

anybody’s picture

@justin2pin: This is a bit off-topic, but please see my comment #22 in #2910237: Deprecate Quick Node Clone in favour of Entity Clone I don't know where, who and how, but as you said in #26 the current situation isn't good for Drupal 8+ at all. Several cloning modules exist, but none of them is able to handle all kind of entities. For Drupal developers it's bad, for Drupal customers this is a mess in UX, so it would be nice if we could push things forwards somewhere ;)

Now BTT! I think #26 is the right way to go for layout_paragraphs until there is one clone module to rule them all.

yonailo’s picture

I have tested patch #15 and it seems that it does not work well with translations.

I use quick_node_clone. After cloning, the cloned node is good but its translations are not.

Any clue ? Has anyone tested #15 with translations ?

guiu.rocafort.ferrer made their first commit to this issue’s fork.

guiu.rocafort.ferrer’s picture

I updated the issue branch with the latest 2.0.x branch, and solved some merge conflicts.
I was not able to configure and run (yet) the Functional Javascript tests locally, so pushing to see if it passes the tests in the Gitlab pipelines.

I am planning on working on this in the following days.

godotislate’s picture

FYI: New hooks hook_entity_duplicate() and hook_ENTITY_TYPE_duplicate() have been committed to Drupal core and will be available in Drupal 11.2. A universal approach that works for all the cloning modules might be possible using those hooks, when sites are running 11.2+.

anybody’s picture

Great news @godotislate!!
@justin2pin what do you think regarding that (#31) and the state of this issue? There still seems no clean way to clone nodes with Layout Builder on it? How should we proceed?

anybody changed the visibility of the branch 3218226-cloning-entities-containing to hidden.

anybody changed the visibility of the branch 2.0.x to hidden.

anybody changed the visibility of the branch 3218226-2.x-cloning-hotfix-based-on-15 to hidden.

anybody changed the visibility of the branch 3218226-cloning-entities-containing to active.

anybody changed the visibility of the branch 2.2.x to hidden.

anybody changed the visibility of the branch 3218226-cloning-entities-containing to hidden.

anybody changed the visibility of the branch 3218226-cloning-entities-containing to active.

anybody changed the visibility of the branch 3.0.x to hidden.

anybody’s picture

@guiu.rocafort.ferrer are you planning to further work on MR!69 in the meantime? I tried replicating it to 2.2.x and 3.0.x but then saw that it seems to be incomplete compared to #15? So definitely needs work, while

#31 seems to be the correct way to go.

Very much hoping for feedback from @justin2pin :)

Update:
In #3546997: Starting with Drupal ^11.2 with latest Paragraph do not need Paragraphs duplicates there's being worked with the new hooks in Quick Node Clone!

Paragraphs also already implemented the new hooks: #3495373: Support duplicate hook