Block Editor (legacy): dotAudio and aiContent nodes are deleted on load when Allowed Blocks is restricted
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 970
- Forks
- 486
- Avg merge
- 3d 33m
- Merged PRs (30d)
- 170
Description
Problem Statement
The legacy Block Editor silently deletes dotAudio and aiContent nodes from a document at load time whenever the field has any Allowed Blocks restriction.
relatedContent (core-web/libs/block-editor/src/lib/shared/utils/parser.utils.ts:57) is a hand-maintained alias table mapping Settings vocabulary to TipTap node names. It has entries for image → dotImage and video → dotVideo. It has no entry for audio → dotAudio, and none for aiContentPrompt → aiContent.
Executing the real logic of parser.utils.ts (imports stripped, constants inlined, functions otherwise verbatim):
blockMap for [paragraph, audio] : {paragraph,text,doc,hardBreak,dotUnsupportedBlock,audio}
AUDIO allowed=[paragraph,audio] -> ['paragraph'] <- dotAudio DELETED
AI allowed=[paragraph,aiContentPrompt] -> ['paragraph'] <- aiContent DELETED
IMAGE allowed=[paragraph,image] (control) -> ['paragraph','dotImage'] <- survives
VIDEO allowed=[paragraph,video] (control) -> ['paragraph','dotVideo'] <- survives
This is strictly worse than the ten ticketed defects in #37601. In those, the stored JSON stayed intact until someone saved. Here the node is removed from the in-memory tree at load, and the next Save makes the loss permanent.
The load path is dot-block-editor.component.ts:774-805 — preserveUnknownBlockNodes -> preserveUnknownBlockMarks -> removeInvalidNodes. A registered node such as dotAudio survives the preservation pass precisely because it is known, and is then deleted by the filter.
Related, same root: the _customNodes map (dot-block-editor.component.ts:148-156) is keyed by Settings vocabulary yet contains aiContent and loader — keys Settings can never emit — so those extensions are never registered on a restricted field either.
Impact. Affects users without FEATURE_FLAG_NEW_BLOCK_EDITOR, i.e. the majority today. No customer ticket has been filed yet, which is precisely the argument of #37601: this class is found by reading the code or by a customer losing data.
Steps to Reproduce
- Create (or open) a content type with a Block Editor field.
- Open the field's properties -> Settings tab -> Allowed Blocks, tick Audio (and optionally AI Content), and save.
- Open a contentlet on that content type whose Block Editor field already contains a stored
dotAudioblock. - Observe the audio block is absent from the editor on open, while
/api/content/querystill returns it in the stored JSON. - Make any edit and Save.
- Re-query the API.
Expected: ticking "Audio" in Allowed Blocks permits audio blocks; stored audio blocks load and render.
Actual: the block is stripped from the document at load, and step 5 writes the loss to storage permanently.
Acceptance Criteria
-
relatedContentinparser.utils.tsmapsaudioto thedotAudionode name, andaiContentPromptto theaiContentnode name - With
allowedBlocks = [paragraph, audio], a storeddotAudionode survives the load path and renders in the legacy editor - With
allowedBlocks = [aiContentPrompt], a storedaiContentnode survives the load path and renders - Control cases do not regress:
image/videorestrictions still resolve todotImage/dotVideo - A node whose key is genuinely absent from Allowed Blocks is still handled by the existing
dotUnsupportedBlockpreservation path, not deleted -
_customNodes(dot-block-editor.component.ts:148-156) no longer contains keys the Settings tab cannot emit (aiContent,loader), or those entries are keyed by the identifier Settings actually writes - A unit test asserts the alias table against
getEditorBlockOptions()so a future producible option without an alias fails the build, mirroring the I1/I2 invariants added in #37609 - Verified by opening a restricted field with stored audio content, saving, and confirming via
/api/content/querythat the node is still present
Deliberately scoped as a targeted fix, not a rewrite. The alias table stays hand-maintained; the new test is what prevents the next omission. Deriving the known set from the live schema (as the new editor does) is the structural fix, but it touches the legacy editor's load path, which runs for most users today — that belongs in its own issue if we want it.
dotCMS Version
main @ 667fc831ee (2026-09-17). Affects the legacy Block Editor, i.e. every environment where FEATURE_FLAG_NEW_BLOCK_EDITOR is not enabled.
Severity
High - Major functionality broken
Links
- Parent / umbrella: https://github.com/dotCMS/core/issues/37601 (Defect A)
- NA — no Freshdesk ticket. Found by code audit while triaging the capability-key family.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with relatedContent in block-editor/src/lib/shared/utils/parser.utils.ts and the preserveUnknownBlockNodes through removeInvalidNodes path in dot-block-editor.component.ts:774-805. Inspect _customNodes at lines 148-156 and compare its keys with getEditorBlockOptions(). Add focused unit coverage for the alias invariant, then verify restricted audio and AI Content documents survive load and save via /api/content/query without regressing image and video controls.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- content, frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100