[5.x]: Block added through a draft gets an empty first revision on multi-site, so reverting deletes its nested content
Los mantenedores suelen responder en 1 día
@i-just ya está trabajando en esto.
Desde el 29/10/2026.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 45/100
Línea de trabajo
Start by tracing NestedElementManager::duplicateNestedElements() through Elements::duplicateElement() and the propagation saves described in the report. Then read Entry::beforeSave() and Revisions::createRevision() to follow when the initial revision is created and reused. Reproduce the multi-site setup and confirm the fix by checking that revision 2 retains the nested entry after reverting.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
What happened?
Description
On a multi-site install, when a new nested entry is added to a versioned Matrix field through a draft (any edit of a live entry), Craft saves that nested entry's first revision before its own nested entries are copied. The owner's revision reuses that empty revision, and so does every later owner revision until the nested entry changes. Reverting the owner to any of those revisions deletes the nested content from the canonical entry.
Reproduced on a fresh craftcms/craft project (Craft 5.11.5.1, no plugins, no modules) with:
- 2 sites in one group.
- Channel section "Pages", versioning on, propagation "All sites", enabled for both sites.
- Entry type
page: title + Matrix fieldblocks(view mode "Cards", "Enable versioning for entries in this field" on, propagation "All sites"), entry typeblock. - Entry type
block: title + Matrix fieldinnerBlocks(view mode "Blocks", versioning off, propagation "All sites"), entry typechild. - Entry type
child: title + Plain Text fieldbody.
The same steps in a section enabled for one site only don't reproduce it: the block's revision is then created after its inner entry and is complete.
What happens on save, traced with a backtrace on Revisions::EVENT_BEFORE_CREATE_REVISION:
- Saving the live entry applies its provisional draft.
NestedElementManager::duplicateNestedElements()copies the new block (owned by the draft) withduplicateElement()(NestedElementManager.php:1164), passing'propagating' => falsein$newAttributes(NestedElementManager.php:1104). Elements::duplicateElement()saves the block for the first site, then clones it for each other site and sets$siteClone->propagating = true(Elements.php:2046), butCraft::configure($siteClone, $newAttributes)(Elements.php:2072) sets it back tofalse.- So the second site's save (
Elements.php:2102) is a non-propagating save of an existing entry with no revisions.Entry::beforeSave()("Make sure the entry has at least one revision",Entry.php:2934) creates a revision from the DB, while the block exists in one site only and has no nested entries yet. The revision note is "Revision from {date}". - The block's nested entries are copied afterwards, in
afterPropagate(). The next revision request for the block (fromEntry::afterPropagate(), then from the owner's revision) is short-circuited inRevisions::createRevision()(Revisions.php:109): the block'sdateUpdatedequals the empty revision'sdateCreated, so the empty revision is returned and reused.
Element IDs on the fresh install show the order:
| ID | Element |
|---|---|
| #15 | new block "Block B" (canonical) |
| #16 | its revision, note "Revision from Oct 28, 2026, 3:11:48 PM PDT", no inner entry |
| #17 | its inner child entry "Child B" |
| #18 | owner revision 2 ("Applied “Draft 1”"), which owns #16 |
After "Revert content from this revision" on revision 2, #17 is soft-deleted and the live block #15 has no inner entry.
We also tested the fix for #19594 (commit 53cba141) on 5.11.5.1: it doesn't change this. The nested entry's owners get their dateUpdated bumped, but to the same second, and the comparison uses the in-memory block.
Workaround (verified on our own project): turning off "Enable versioning for entries in this field" on the outer Matrix field. Blocks then get no revision of their own, and the owner revision snapshots them after their nested entries exist.
Steps to reproduce
- On a multi-site install, set up the section and fields above.
- Create a page with one block ("Block A", with an inner entry with some body text) and save it. Revision 1 is complete.
- Edit the live page, add a second block ("Block B"), add its inner entry with some body text, save the block's slideout, then save the page (revision 2).
- View revision 2: "Block B" has no inner entry.
- Revert to revision 2 ("Revert content from this revision").
Expected behavior
Revision 2 contains "Block B" with its inner entry, and reverting to it keeps that content.
Actual behavior
Revision 2's copy of "Block B" has no inner entry, and reverting to it deletes the inner entry (and its body text) from the live page.
Craft CMS version
5.11.5.1 (Pro, trial)
PHP version
8.5.11
Operating system and version
Linux (Docker, Alpine 3.24.2)
Database type and version
MySQL 8.4
Image driver and version
Imagick 3.8.1 (ImageMagick 7.1.2-30)
Installed plugins and versions
None (fresh craftcms/craft project)
- Lenguaje dominante
- PHP
- Estrellas
- 3.6k
- Forks
- 710
- Merge medio
- 12 h 44 min
- PR fusionados (30 d)
- 335
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de craftcms/cms
-
repo:cms
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
craftcms/cms#20235 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
bug repo:cms
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
craftcms/cms#20205 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
[6.x]: Element index View menu: sort direction buttons only update after the table has loadedAbiertobug repo:cms
Dificultad 2/5 1-3 horas Aptitud para principiantes 64/100
craftcms/cms#20203 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
[5.x]: Moving an asset with “Replace it” throws TypeError: volumeUid must be of type string, null givenPosiblemente ocupada @i-just la tomó hace 2 días. Abiertobug repo:cms
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
craftcms/cms#20128 · 2 comentarios · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
repo:cms
Dificultad 3/5 1-2 días Aptitud para principiantes 72/100
craftcms/cms#20234 · 1 comentario ·
Los mantenedores suelen responder en 1 día
Todos los issues de craftcms/cms
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
Los mantenedores suelen responder en 1 día
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
Los mantenedores suelen responder en 1 día
-
0. Needs triage bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
nextcloud/fulltextsearch#1061 ·
Los mantenedores suelen responder en 1 día
-
PHP 8.6 compatibilityAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100
-
[Resource]: Claude KitAbiertoresource-submission validation-passed
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
hesreallyhim/awesome-claude-code#3375 · 1 comentario ·
Los mantenedores suelen responder en 1 día