Duplicating Drupal content blocks in editor should create a new block entity

Created on 26 May 2024, 6 months ago
Updated 20 July 2024, 5 months ago

Problem/Motivation

We have the ability now to create Drupal content blocks from within the editor. These blocks are non resuable so they only appear in the editor and are only intended to be used in that one instance.

If there is a content block in the editor, the user can select duplicate block, wich duplicates the block in the editor but it still has the same block Id, so that block is being reused, wich is incorrect.

Duplicating a block from within the editor should clone that block as a starting point (create a new block entity) which would generate a new ID and allow the editor to further edit the new instance.

Proposed resolution

Either save a new block when "duplicate" is selected in the editor, or disable the duplicate functionality for content blocks

Im marking this as a bug since duplicating a block and editing the new one will break the other instance of the block in the editor

πŸ› Bug report
Status

Fixed

Version

3.0

Component

Code

Created by

πŸ‡ΊπŸ‡ΈUnited States loze Los Angeles

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @loze
  • πŸ‡ΊπŸ‡ΈUnited States loze Los Angeles

    I can take a stab at doing this, but I'm unsure how to hook into Gutenberg's duplicate functionality. (act when a block is duplicated) If someone can point me in the right direction, I can work on this.

  • πŸ‡ΊπŸ‡ΈUnited States loze Los Angeles

    We are also going to run into an issue when a block is copy/pasted or added manually in the code editor. Im not sure the best approach here.

    Maybe we can show a warning if a content block has the same ID as another on the same page, with a button to create a duplicate, wich would clone the block entity and rebuild the preview.

  • πŸ‡ΊπŸ‡ΈUnited States loze Los Angeles
  • πŸ‡΅πŸ‡ΉPortugal marcofernandes

    Hmm... I don't think it's enough to just clear the contentBlockId attribute.

    Here's my thoughts on how it should work:
    When duplicating a content block or copy/paste it, at Drupal side, a new content block must be created (cloned from the source) an pass the contentBlockIdto the gutenberg block.

    Unfortunately there isn't a filter/hook to handle the duplicate action nor copy/paste https://stackoverflow.com/questions/67667923/how-to-detect-when-a-block-....
    A possible implementation would be creating a filter for editor.BlockEdit (example at /filters/mapping-fields.jsx) to somehow handle the content block duplication.

  • πŸ‡ΊπŸ‡ΈUnited States loze Los Angeles

    Also, what if a node it cloned completely? Editing a block on one would affect the other.

  • πŸ‡ΊπŸ‡ΈUnited States loze Los Angeles

    Perhaps we need to track content block usage in a custom table? This way, if a contentBlockId used in a node appears in another node, a duplicate is created, and they can be edited independently.

    This would also allow us to clean up orphaned blocks on cron also. Consider someone adding a block in a node, not saving the node, deleting the block, then saving the node. That block would still exist in the db.

    Some effort is made to delete unused blocks on node save, where we compare the old body field to the new body field and delete any blocks that were detected there, but in this case the deleted block was never saved in the body field.

  • πŸ‡ΊπŸ‡ΈUnited States loze Los Angeles

    What about node revisions? Do we want to make revisions of the content blocks as well that correspond to the node revisions?
    Or at least not delete blocks that are present in a revision.

    ehh, theres a lot to consider here.

    Anyway, I have something somewhat working that handles the original issue, of cloning a block when it is copy/pasted/duplicated in a single node. That I will push up in a bit.

  • Merge request !152handle duplicate content blocks β†’ (Closed) created by loze
  • Status changed to Needs review 6 months ago
  • πŸ‡ΊπŸ‡ΈUnited States loze Los Angeles

    This is my first stab at the initial issue.
    See my comment in content-blocks.jsx, I'm not sure how to best handle this. Any thoughts?

        // @todo This doesn't feel safe. Someone can just hit the url editor/content_block/clone/[ID] and clone a block.
        //  Maybe csrf token is the way to protect against it, but I dont know how to get it in javascript.
        //  Also, as it is now, this will clone any block, even if its not a type that is used in gutenberg.
        //  Maybe we should also pass the node type, and check that this block type is enabled in gutenberg for this
        //  node type?
    
  • Pipeline finished with Success
    6 months ago
    Total: 355s
    #188015
  • πŸ‡ΊπŸ‡ΈUnited States loze Los Angeles

    I think a table that tracks usage makes sense. something with fields like: entity_type, entity_id, block_type, block_id, created_timestamp

    With this we could do the following:

    • Change the editor/content_block/clone/[ID] path (that this MR currently adds) to editor/content_block/check_usage/[NODE_ID]/[BLOCK_ID].
      This would check against the usage table if the block is used in any other nodes and duplicate the block it if it is and return the new block id.
    • on saving of the node, we would parse the body and write all the used contentBlockIds to the table, and delete all the records for contentBlockIds on that node that arent used.
    • When a node is cloned programmatically (is new) check that any contentBlockIds arent being used in another node and clone them if they are
    • Then on cron we can delete orphaned blocks older than a certain timestamp

    This would address several of the issues I raised but still not deal with revisions, which could be addressed later.

  • Pipeline finished with Success
    6 months ago
    Total: 248s
    #188287
  • πŸ‡ΊπŸ‡ΈUnited States loze Los Angeles

    Ok, so I think I got this pretty much there and was able to work out some of my questions.

    This latest MR does the following

    • Adds a update hook to create a table to track block usage
    • Adds a update hook to batch process all existing Gutenberg nodes and create usage records for any blocks being used.
    • Changes my original route for cloning a node, to one that checks the usage and clones it if it needs to, returning the block Id
    • Cleans up the javascript filter detecting the duplicates

    I still need to

    • Clean up orphaned blocks on cron
    • Address updating block ids when programmatically cloning an entire node.

    Bigger picture:
    What to do about node revisions, that feels like a separate undertaking full of gotchas.

    It appears to be working well from my initial tests. Be sure to run updb when testing.

  • πŸ‡΅πŸ‡ΉPortugal marcofernandes

    I read all you monologue here, @loze πŸ˜… It was great to follow your thinking.
    The table for block usage seems the best approach.
    IIRC, Layout Builder when saving the layout of a node with revisions will create new content blocks even if the content block type has revisions disabled. Maybe we should follow the same approach? Because IMO since the content blocks are tied to nodes, there's no need to handle block revisions. But we could check how LB handles the insert/update/delete operations regarding content blocks.

  • Pipeline finished with Success
    6 months ago
    Total: 283s
    #188664
  • Pipeline finished with Success
    6 months ago
    Total: 222s
    #188720
  • πŸ‡ΊπŸ‡ΈUnited States loze Los Angeles

    Thanks for the insight @marcofernandes.
    I think this is pretty solid now. Still not addressing revisions, though.
    But it appears to be working pretty good. The latest changes requires running another updb because I made some edits to the usage table.

  • Pipeline finished with Success
    6 months ago
    Total: 250s
    #188776
  • Pipeline finished with Success
    6 months ago
    Total: 222s
    #188792
  • Pipeline finished with Success
    6 months ago
    Total: 219s
    #188805
  • Pipeline finished with Success
    6 months ago
    #188838
  • Pipeline finished with Success
    6 months ago
    Total: 242s
    #188844
  • Pipeline finished with Success
    6 months ago
    Total: 221s
    #188848
  • Pipeline finished with Canceled
    6 months ago
    Total: 333s
    #194191
  • Pipeline finished with Failed
    6 months ago
    Total: 1424s
    #194195
  • Pipeline finished with Success
    6 months ago
    Total: 1554s
    #194212
  • Pipeline finished with Skipped
    6 months ago
    #195297
  • Pipeline finished with Failed
    5 months ago
    Total: 598s
    #207320
  • Merge request !160handle duplicate content blocks β†’ (Merged) created by loze
  • Pipeline finished with Failed
    5 months ago
    Total: 176s
    #207415
  • Pipeline finished with Failed
    5 months ago
    Total: 253s
    #207437
  • Pipeline finished with Failed
    5 months ago
    Total: 2535s
    #207436
  • Pipeline finished with Failed
    5 months ago
    Total: 217s
    #217371
  • Pipeline finished with Success
    5 months ago
    Total: 215s
    #217374
  • Pipeline finished with Success
    5 months ago
    Total: 243s
    #217379
  • Pipeline finished with Success
    5 months ago
    Total: 216s
    #217383
  • Status changed to Fixed 5 months ago
  • πŸ‡΅πŸ‡ΉPortugal marcofernandes

    @loze I reviewed and tested it. Great work!

  • Pipeline finished with Success
    5 months ago
    Total: 155s
    #227890
  • Automatically closed - issue fixed for 2 weeks with no activity.

  • Pipeline finished with Success
    4 months ago
    #233096
  • Pipeline finished with Success
    4 months ago
    #233139
  • Pipeline finished with Success
    4 months ago
    Total: 163s
    #233268
  • Pipeline finished with Success
    4 months ago
    Total: 715s
    #233717
  • Pipeline finished with Success
    4 months ago
    Total: 582s
    #233738
  • Pipeline finished with Canceled
    4 months ago
    Total: 101s
    #234869
  • Pipeline finished with Success
    4 months ago
    Total: 836s
    #234872
  • Pipeline finished with Success
    4 months ago
    Total: 856s
    #234886
  • Pipeline finished with Success
    4 months ago
    Total: 192s
    #235549
  • Pipeline finished with Canceled
    4 months ago
    Total: 68s
    #243365
  • Pipeline finished with Success
    4 months ago
    Total: 243s
    #243366
  • Pipeline finished with Canceled
    4 months ago
    Total: 217s
    #243636
  • Pipeline finished with Success
    4 months ago
    Total: 223s
    #243639
  • Pipeline finished with Canceled
    4 months ago
    Total: 138s
    #243649
  • Pipeline finished with Canceled
    4 months ago
    Total: 246s
    #243652
  • Pipeline finished with Success
    4 months ago
    Total: 209s
    #243655
  • Pipeline finished with Failed
    4 months ago
    Total: 232s
    #246503
  • Pipeline finished with Failed
    4 months ago
    Total: 197s
    #246673
  • Pipeline finished with Failed
    4 months ago
    Total: 203s
    #246923
  • Pipeline finished with Success
    4 months ago
    Total: 393s
    #247600
  • Pipeline finished with Failed
    4 months ago
    Total: 364s
    #249134
  • Pipeline finished with Failed
    4 months ago
    Total: 313s
    #249154
  • Pipeline finished with Success
    4 months ago
    Total: 375s
    #249158
  • Pipeline finished with Success
    4 months ago
    #249202
  • Pipeline finished with Failed
    4 months ago
    Total: 361s
    #249322
  • Pipeline finished with Failed
    4 months ago
    Total: 221s
    #249375
  • Pipeline finished with Success
    4 months ago
    Total: 224s
    #249859
  • Pipeline finished with Success
    4 months ago
    Total: 229s
    #249864
  • Pipeline finished with Success
    4 months ago
    Total: 238s
    #249921
  • Pipeline finished with Success
    4 months ago
    Total: 286s
    #250578
  • Pipeline finished with Success
    4 months ago
    Total: 264s
    #250869
  • Pipeline finished with Success
    4 months ago
    Total: 341s
    #251426
  • Pipeline finished with Skipped
    4 months ago
    #251474
  • Pipeline finished with Failed
    4 months ago
    Total: 1070s
    #252703
  • Pipeline finished with Failed
    4 months ago
    #252825
  • Pipeline finished with Failed
    4 months ago
    #252841
  • Pipeline finished with Failed
    4 months ago
    Total: 316s
    #252855
  • Pipeline finished with Success
    4 months ago
    #253562
  • Pipeline finished with Failed
    about 2 months ago
    Total: 2756s
    #304455
  • Pipeline finished with Success
    about 2 months ago
    Total: 187s
    #304857
  • Pipeline finished with Success
    about 2 months ago
    Total: 214s
    #304867
  • Pipeline finished with Failed
    about 2 months ago
    Total: 233s
    #305032
  • Pipeline finished with Canceled
    about 2 months ago
    #305041
  • Pipeline finished with Canceled
    about 2 months ago
    Total: 117s
    #305040
  • Pipeline finished with Failed
    about 2 months ago
    Total: 185s
    #305042
  • Pipeline finished with Canceled
    about 2 months ago
    Total: 192s
    #305045
  • Pipeline finished with Failed
    about 2 months ago
    Total: 571s
    #305049
  • Pipeline finished with Success
    about 2 months ago
    Total: 1681s
    #305056
  • Pipeline finished with Failed
    about 2 months ago
    Total: 230s
    #305135
  • Pipeline finished with Success
    about 2 months ago
    Total: 273s
    #305150
  • Pipeline finished with Success
    about 2 months ago
    Total: 362s
    #305187
  • Pipeline finished with Skipped
    about 2 months ago
    #305205
  • Pipeline finished with Success
    about 1 month ago
    #318335
  • Pipeline finished with Failed
    about 1 month ago
    #318337
  • Pipeline finished with Success
    about 1 month ago
    #318349
  • Pipeline finished with Success
    about 1 month ago
    #318433
  • Pipeline finished with Success
    about 1 month ago
    #318478
  • Pipeline finished with Failed
    about 1 month ago
    #318657
  • Pipeline finished with Success
    about 1 month ago
    #318659
  • Pipeline finished with Canceled
    about 1 month ago
    Total: 184s
    #323104
  • Pipeline finished with Failed
    about 1 month ago
    Total: 997s
    #323107
  • Pipeline finished with Failed
    about 1 month ago
    Total: 194s
    #323178
  • Pipeline finished with Canceled
    about 1 month ago
    Total: 402s
    #323238
  • Pipeline finished with Canceled
    about 1 month ago
    Total: 532s
    #323242
  • Pipeline finished with Failed
    about 1 month ago
    Total: 563s
    #323253
  • Pipeline finished with Failed
    about 1 month ago
    Total: 763s
    #323267
  • Pipeline finished with Canceled
    about 1 month ago
    Total: 1153s
    #325042
  • Pipeline finished with Success
    about 1 month ago
    Total: 1196s
    #325061
  • Pipeline finished with Failed
    about 1 month ago
    Total: 4489s
    #325336
  • Pipeline finished with Success
    about 1 month ago
    Total: 827s
    #325439
  • Pipeline finished with Success
    29 days ago
    Total: 166s
    #329995
Production build 0.71.5 2024