dotCMS / dotCMS/core

Block Editor (legacy): dotAudio and aiContent nodes are deleted on load when Allowed Blocks is restricted

Open
#37,619 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

dotCMS: Block Editor OKR : Customer Support Team : Maintenance Type : Defect
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-805preserveUnknownBlockNodes -> 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

  1. Create (or open) a content type with a Block Editor field.
  2. Open the field's properties -> Settings tab -> Allowed Blocks, tick Audio (and optionally AI Content), and save.
  3. Open a contentlet on that content type whose Block Editor field already contains a stored dotAudio block.
  4. Observe the audio block is absent from the editor on open, while /api/content/query still returns it in the stored JSON.
  5. Make any edit and Save.
  6. 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

  • relatedContent in parser.utils.ts maps audio to the dotAudio node name, and aiContentPrompt to the aiContent node name
  • With allowedBlocks = [paragraph, audio], a stored dotAudio node survives the load path and renders in the legacy editor
  • With allowedBlocks = [aiContentPrompt], a stored aiContent node survives the load path and renders
  • Control cases do not regress: image/video restrictions still resolve to dotImage/dotVideo
  • A node whose key is genuinely absent from Allowed Blocks is still handled by the existing dotUnsupportedBlock preservation 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/query that 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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.