Migration fix for #23224 Converting RichText Block Udis to Keys - #23225
Migration fix for #23224 Converting RichText Block Udis to Keys#23225robertjf wants to merge 1 commit into
Conversation
|
Hi there @robertjf, thank you for this contribution! 👍 While we wait for one of the Core Collaborators team to have a look at your work, we wanted to let you know about that we have a checklist for some of the things we will consider during review:
Don't worry if you got something wrong. We like to think of a pull request as the start of a conversation, we're happy to provide guidance on improving your contribution. If you realize that you might want to make some changes then you can do that by adding new commits to the branch you created for this work and pushing new commits. They should then automatically show up as updates to this pull request. Thanks, from your friendly Umbraco GitHub bot 🤖 🙂 |
|
As discussed on the related issue, I'll close this to use #23259 in preference. |
Prerequisites
If there's an existing issue for this PR then this fixes #23224
This is a draft/proposed PR to resolve the above existing issue - Umbraco team may want to "move" the migration to earlier in the steps or update an existing migration. As Claude generated the code, there may be better ways to resolve this!
Description
Sites upgraded from v13 to the new (Tiptap) Rich Text Editor can end up with half-migrated RTE block values:
blocks.contentData[].keyis stored in the new dashed-GUID format, but themarkupstill references blocks with the deprecateddata-content-udi="umb://element/{guid}"attribute.The v14+ editor and the server-side parser (
RichTextParsingRegexes.BlockRegex) only recognisedata-content-key="{dashed-guid}", and there is no backwards-compatibility handling fordata-content-udi. As a result the inline/block placeholders never bind to their content: on load the blocks are hoisted out of the text flow and the surrounding markup collapses, and saving the content then drops the blocks entirely.This PR adds an upgrade migration that rewrites the legacy attribute in stored property data:
Approach
The conversion is a string replacement performed directly against
umbracoPropertyData.textValue, which makes it:Editor-agnostic and nesting-agnostic — it also fixes Rich Text Editors nested inside a Block List / Block Grid at any depth, because the legacy attribute only ever appears in RTE block markup.
Tolerant of the stored quote form — depending on how the value was serialized (and how deeply it is nested) the attribute-value quote may be stored as a literal
", an escaped quote\", or a unicode escape", each optionally preceded by extra escaping backslashes. The regex captures the delimiter and reproduces it verbatim on both sides, so the stored JSON stays valid:Idempotent — once an attribute is
data-content-keyit is no longer matched, so the migration is safe to re-run.The identifier is normalised to the dashed GUID form (
Guid.Parse(...).ToString("D")) so it matchesblocks.contentData[].key.Files
src/Umbraco.Infrastructure/Migrations/Upgrade/V_17_5_0/ConvertRichTextBlockUdisToKeys.cs— the migration (AsyncMigrationBase), with a testable staticConvertLegacyBlockReferenceshelper.src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs— registered as a// To 17.5.0step.tests/Umbraco.Tests.UnitTests/.../V_17_5_0/ConvertRichTextBlockUdisToKeysTests.cs— unit tests for the transform.tests/Umbraco.Tests.Integration/.../V_17_5_0/ConvertRichTextBlockUdisToKeysTests.cs— DB round-trip test.How to test
markupnow usesdata-content-key.To confirm via SQL that no legacy references remain:
Tests
classattribute, multiple references per value, single- and double-level JSON escaping, the real-world"(and nested\\u0022) unicode-escaped quote form, malformed identifiers left untouched, idempotency, and no-op values.") format, runs the migration, and asserts the conversion (and that an already-migrated value is untouched).All passing locally (unit + integration).
Notes / questions for reviewers
textValuecontains the legacy attribute (current and historic versions), rather than restricting to specific editors — this is intentional so nested cases are covered. Happy to scope it to current/published versions only if preferred.🤖 Generated with Claude Code