Problem/Motivation

When paragraphs are reordered in the widget's drag and drop mode, ParagraphsWidget::reorderParagraphs() moves the paragraph item states to their new deltas, but everything else stored below field_storage/#parents/<field>/<delta>/subform stays at the old delta. That storage holds the widget states of the fields on each paragraph.

Some widgets keep the field values themselves in their widget state and apply them to the entity on the next form build. The media library widget does this on every submit: MediaLibraryWidget::extractFormValues() stores the referenced media as items, and MediaLibraryWidget::form() calls $items->setValue($field_state['items']) on rebuild. After a reorder, the paragraph that now sits at a position receives the media of the paragraph that used to sit there. Saving persists the wrong references.

With a translatable field only the language being edited is affected, so the corruption is easy to miss. It surfaced on a multilingual site where logo grids (a container paragraph with nested logo paragraphs, each holding a media library field) lost their logos in one language after an editor reordered the sections with drag and drop.

I found two issues which seemed related but did not address the root cause (shifting values in the paragraph subform storage): #3251383: Paragraphs breaks reordering-then-adding for multi-value fields with widgets that have a dynamic set of form elements and #3155188: Deleting a nested paragraph child and then using the re-order function causes unexpected deletion of sibling paragraphs when node is saved.

Steps to reproduce

  1. Enable paragraphs, media, media_library and create a media type.
  2. Create a paragraph type "Media item" with a media reference field using the media library widget.
  3. Create a paragraph type "Container" with a paragraphs field allowing "Media item". Add a paragraphs field to a content type allowing "Container". Set both paragraphs widgets to edit mode "Open".
  4. Create a node with two containers, each holding one media item that references a different media entity.
  5. Edit the node, click "Drag & drop", swap the two containers, click "Complete drag & drop".
  6. Both media items now show the media of the container that previously sat at their position. Clicking "Save" writes those references.

Expected: every paragraph keeps its own field values after a reorder.

Proposed resolution

Rebuild the storage below the reordered field from the submitted order. Before placing the item states, move the widget states stored under each paragraph's subform (<field>/<old delta>/subform/#fields/*) to the paragraph's new position, recursively for nested lists, emptying only their paragraphs entries. The existing pass that places item states then runs unchanged.

The two passes are needed: moving a widget state replaces anything already placed into it, so an item state written into a container's storage before that storage moved would be lost.

Issue fork paragraphs-3624834

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

pfrenssen created an issue. See original summary.

pfrenssen’s picture

Issue summary: View changes
pfrenssen’s picture

Status: Active » Needs work

I made an initial fix, but there are some pre-existing failures on the 8.x-1.x branch. I am leaving this at needs work because I cannot be sure whether it causes any regressions. We need to retry this after the main branch is stable again.