Prerequisite
Enable layout builder on Content type(s) and allow user to place custom blocks on node layout.
Problem
When a node with layout builder enabled and has custom (inline) blocks placed in its layout. When that node is cloned, separate instances of inline blocks are not created. If a user edits the inline blocks in the cloned page, then go back to the original page, the blocks on the original page will be locked for edits.
Steps to reproduce
1. Enable layout builder
2. Create a custom block type, with simple text fields.
3. Create a content type
4. In Manage Display, enable "Use Layout Builder" and "Allow each content item to have its layout customized."
5. In "BLOCKS AVAILABLE FOR PLACEMENT", allow placement of custom blocks
6. Create test node, in [node_path]/layout, add a block and select create custom block and select the block type you created
7. Save layout.
8. Clone node
9. In the cloned node, go to [node_path]/layout, edit the block and save
10. Go back to the original node layout ([node_path]/layout), try to edit the block and save. See warning messages

Expected behavior
When a node is cloned, user should be able to edit inline blocks on either the original or the clone version of the node.
What happened instead
When a node is clone, as soon as user edits the inline block on clone version of the node, inline blocks on original version of the node will be locked.
Issue fork quick_node_clone-3100117
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
Comment #2
Weilinggu commentedPatch for fix
Comment #3
Weilinggu commentedComment #4
Weilinggu commentedComment #5
Weilinggu commentedComment #6
Weilinggu commentedComment #7
Weilinggu commentedComment #8
Weilinggu commentedPatch for version 1.12
Comment #9
phjouI confirm I have the same problem.
The patch seems to not work for me. I have a warning and then leading to a fatal error.
Notice: Undefined index: block_revision_id in Drupal\quick_node_clone\Entity\QuickNodeCloneEntityFormBuilder->cloneInlineBlocks() (line 204 of /app/web/modules/contrib/quick_node_clone/src/Entity/QuickNodeCloneEntityFormBuilder.php)It seems I don't have the block_revision_id that is used by the patch.
And the patch needs a reroll for dev.
Comment #10
phjouReroll attached without any fix.
Comment #11
phjouOk so the crash seems happening when using field blocks (extra_field_block):
My configuration is when I arrive where the code crashes:
Fields block doesn't need and actually can't be cloned, so I just added a test to avoid going through the code when having a field block or any kind of a block that is not revisionable.
Comment #12
phjouComment #13
mauhg commentedHave to re-roll patch from #11 since it doesn't apply to version 1.12
Also have some errors on the delta of the components so the logic there was updated.
Comment #14
phjouIf you changed something, please provide a patch for dev with an interdiff.
A patch for 1.12 can't be merged.
Comment #15
mauhg commentedPatch against dev and interdiff
attached
Comment #16
i-trokhanenkoPatch #15 works well. Thanks!
Comment #17
ijsbrandy commentedA few problems occur while using this patch.
Inline content block's are saved when rendering the node clone form. This means that every time you refresh the node clone form, new inline content blocks are created. I first thought it was the paragraph clone implementation mentioned here but totally forgot that this patch was still active on my locale environment :S.
Furthermore layout_builder keeps track of the related inline block content under inline_block_usage. These values are not correctly added (layout_entity_id is NULL).
Comment #18
smustgrave commented#15 unfortunately doesn't work for me.
Also agree with #17 that the clone function should be called on save and not when the form renders.
Comment #19
smustgrave commentedThis ticket may be actually fixed under https://www.drupal.org/project/drupal/issues/3062819#comment-14165004
Comment #20
WebbehTo add on to #19, please check the patch in #3062819-017: Duplicated and new entities with inline blocks do not duplicate their inner blocks and/or track their usage and see if this resolves your issues.
If so, please note the patch in #3062819: Duplicated and new entities with inline blocks do not duplicate their inner blocks and/or track their usage resolves your issue so we have additional evidence and testing to move to RTBC, and reply here so we can scope this fix out of this module and into core. Thanks for helping to test open-source and resolving this issue!
Comment #21
smustgrave commentedThis ticket can probably be closed as the patch from https://www.drupal.org/project/drupal/issues/3062819#comment-14165004 works.
Comment #22
smustgrave commentedComment #23
seanbThe patch in #3062819: Duplicated and new entities with inline blocks do not duplicate their inner blocks and/or track their usage doesn't actually seem to clone the blocks, it creates a new revision of the layout builder blocks, so 2 nodes point to a different revision of the same block. I'm not yet sure whether this should be fixed in core or in the module that actually clones a node. I believe it would be nice if core would take care of it, but I guess that is at least debatable and probably also not likely to be fixed in the short term.
Comment #24
seanbThis patch should do the trick without creating new blocks every time the form loads. The blocks should be saved automatically when the form is submitted.
Comment #26
seanbDoh.. Attached is a new patch without the typo.
Comment #27
seanbAfter some extensive testing I'm still seeing 1 issue with my latest patch. The layout builder modules uses
layout_builder_entity_presave()to callInlineBlockEntityOperations::handlePreSave()which should save all the serialized blocks in the entity. When using translations though, this only seems to run for the current translation, not for all other translations. This causes serialized blocks to be stored in the database, instead of saving the actual block content and just saving a reference to the revision ID in the section configuration.I'm not yet sure how to handle this (or if we should handle this at all and create a core issue). As said in #23: I believe it would be nice if core would take care of it, but I guess that is at least debatable and probably also not likely to be fixed in the short term.
Maybe we should add our own presave hook and call
InlineBlockEntityOperations::handlePreSave()on all translations when saving a clone?Comment #28
seanbSince we need a node ID to properly save blocks and usage, we unfortunately have to save the node twice. I used
SynchronizableEntityTraitin the second save so other modules can detect this in entity hooks and opt out of certain behaviour (just like done in #2803717: Allow 'syncing' content to be updated in content moderation without forcing the creation of a new revision).This means we also can't use a custom presave hook (again, no entity ID available), so I've added the code to fix this in
QuickNodeCloneNodeForm::save(). This seems to work quite well using content moderation and content translations, but I admit doing 2 saves is a bit hacky and might cause issues in other contrib or custom code.Comment #29
wranvaud commentedWhile testing patch #28 it happened that the clone would not contain the blocks in the original node layout builder. Some nodes always work, others always fail. Only found this relevant error:
Comment #30
dorficus commented@wranvaud: I just checked the #28 patch and there are definitely 9 arguments, however one of them is added in/new in this patch compared to the main branch. Usually this is a case of needing to clear caches because no matter what, YAML files never get reloaded unless the caches are cleared.
I have seen that same error multiple times in different scenarios, but each time it had to do with me adding an argument to a service but forgetting to add it to either a construct or create method or similar.
Hopefully you haven't been sitting on this for 2 weeks, but just in case, that should be the fix.
Comment #31
weseze commentedRerolled patch from #28 against latest release.
Comment #32
xaqroxWas using the previous patch on a project that included blocks with paragraphs, and we had a similar problem again: cloned blocks were referencing the same paragraphs as their source nodes. Editing paragraphs seemed to work since the actual field refers to revisions, but if you deleted one of the blocks the paragraph would be deleted as well. Here's a patch that includes cloning the paragraphs of the cloned blocks (and some slightly opinionated refactoring of the cloneParagraphs function).
Comment #33
WebbehPlease include interdiffs when creating a new patch, or use the fork/MR functionality so folks can easily follow what changes have been made between contributions. Thanks!
Comment #34
xaqroxMy bad! Here's the interdiff.
Comment #35
nsciaccaPatch in #32 worked for me. I had Layout Builder blocks referencing paragraphs and found that cloned nodes that were unpublished were setting the source node's paragraphs to be unpublished - so anonymous visitors would see a blank page visiting the source node. This is working, thank you.
Comment #36
seanbJust looked into this issue again. We might be able to remove the duplicate save with #3062819: Duplicated and new entities with inline blocks do not duplicate their inner blocks and/or track their usage.
About cloning entities (nested), our lives would be a lot easier if we have #3040556: It is not possible to react to an entity being duplicated.
To fix the (nested) cloning of paragraphs we have #3211482: Paragraph Library supports Entity Clone module. If we can make the paragraphs module support
hook_entity_duplicate_create(), all the clone related modules would be a lot simpler.We could even have a core issue to make layout builder implement
hook_entity_duplicate_create(), so the layout builder module is responsible for cloning all the blocks when an entity contains a layout builder field.Comment #37
xaqrox#32 updated for 1.17/current 1.x
Comment #38
markdorisonCould we please convert this work to a merge request so that GitLabCI will run against it?
Comment #41
taran2lComment #42
phjouNot sure why the the patch doesn't apply for me:
https://git.drupalcode.org/project/quick_node_clone/-/merge_requests/22/...
Comment #43
randalv commented@phjou Strange, if you use `.diff` instead, then it does work.
https://git.drupalcode.org/project/quick_node_clone/-/merge_requests/22/...
Make sure to add an empty line at EOF, though.
Comment #44
codersrini commentedThe fix #43 worked for me. Thanks a lot.
Comment #45
seanbWould it be possible to move the code to clone a specific section to a separate
cloneLayoutSectionmethod? I'm currently adding a custom controller that duplicates the code to clone a specific section in a layout, and it would be nice to reuse the code we have here.Comment #46
seanbThis seems to do it. Updated the MR.
Comment #47
codechefmarcJust FYI, I needed to update from 1.16 to 1.18 and the original patch we had didn't work for the new version. So, I removed the patch and decided to test this issue and it all seemed to work fine. Was this fixed from 1.16 to 1.18?
Comment #48
seanbAdded a commit to fix an issue while cloning layout builder content for the default language. It appears
InlineBlockEntityOperations::handlePreSavedoes not run for syncing entities.Comment #51
taran2lI've removed all refactoring to make everything a little bit easier to review and added a BC layer for the service constructor arguments change
Comment #52
seanbThe changes in
cloneParagraphsare not entirely unrelated. For example,cloneParagraphsonly allowed cloning paragraphs in nodes, while inline blocks can also contain paragraphs. Can you revert your changes tocloneParagraphs?Comment #53
taran2lComment #55
dblanken commentedI made
cloneParagraph's parametermixedso that it won't bomb on things likeBlockContent, which also calls that method. This still feels like a bandaid, but allows my sites to work. Are there other types we should be looking out for to be explicit and replace mixed?Comment #56
seanbThe parameter should probably be
FieldableEntityInterface, I think we should just revert commit https://git.drupalcode.org/project/quick_node_clone/-/commit/4b2e8cbbb1f...Comment #57
taran2lhi @dblanken @seanb
I've added FieldableEntityInterface back, the original change is doing a good refactoring, but it out of scope for this issue imo and makes it harder to understand and review (plus it's harder to combine patches)
Comment #58
taran2lComment #59
dblanken commentedHi @taran2l
Thank you very much. I can confirm that with the FieldableEntityInterface it's working for us again. Thanks a lot for doing that.
Comment #60
kiseleva.t commentedWhen node has one or several translations and layout builder field is not translatable, layout is lost for whole node after clone.
kiseleva.t made their first commit to this issue’s fork.
Comment #61
kiseleva.t commentedExported MR changes into static file patch.
Comment #62
kmontyOur team found ourselves wanting to use both this patch and the patch from #3049127: Cloning a node with existing translations, clones all translations of the node. The problem is, 3049127 introduced a bug into this patch.
We couldn't figure out a really 'clean' way to reconcile fixing the two patches while adhering to issue encapsulation best practices.
As a result, we'll upload the changes we made to this MR here for other users / future reference for the community. But we didn't want to put undesired changes back into this MR directly.
Essentially, here are our changes.
Line 156 was:
$section = $this->cloneLayoutSection($section);now:
$section = $this->cloneLayoutSection($section, $entity->language()->getId());Line 171 was:
public function cloneLayoutSection(Section $section) {now:
public function cloneLayoutSection(Section $section, string $langcode) {At line 201, we inserted the following conditional logic:
Comment #63
inversed commentedThanks for the work on this one. The patch in #61 solves the main problem. New Block IDs are generated (not just new revision IDs). However, it does not work for nested blocks. In those cases, the top level (parent) blocks get a new ID but the child blocks attached using an entity reference field do not get updated.
This is somewhat of an edge case and a non-trivial thing to fix but I think it is a potential blocker. Nested blocks and nested Paragraphs are not uncommon for some builds.
Comment #64
inversed commentedI updated the MR with a solution to recursively clone blocks that contain
entity_referencefields with atarget_typeset toblock_content.I'm also attaching a patch that breaks this out as a sort of interdiff letting it be applied after either patch #61 or #62 with translation support. The nested blocks fix requires one of those to be applied first.
Comment #65
_renify_ commentedComment #66
anybody