36659 feat(block-editor): dotcms-* rich-node vocabulary on HTML input (parity with the Markdown leg) - #36841
36659 feat(block-editor): dotcms-* rich-node vocabulary on HTML input (parity with the Markdown leg)#36841hassandotcms wants to merge 4 commits into
Conversation
|
Claude finished @hassandotcms's task in 8m 22s —— View job Review: dotcms-* rich-node vocabulary on HTML inputI read the full diff — This is careful, well-tested work. The shared-validator approach (routing HTML element payloads through the same New Issues
Nothing blocking. The routing logic ( |
3f5c5bd to
3317152
Compare
…legs Prepares the Markdown leg's #36658 vocabulary for reuse by the HTML leg (#36659) so both save-path converters validate against the same code and cannot drift into producing different nodes for the same rich content: - DotcmsFences becomes package-visible, with a parse(label, JsonNode) overload for payloads assembled from element attributes (no raw body to size-cap; the String overload still caps before parsing). - parseDecorationAttrs and its limits move out of the commonmark-specific TiptapBuilder to the class level, so the dotcms:attrs comment can be validated identically wherever it arrives. No behavior change: the Markdown suites pass untouched.
…input Closes the parity gap #36658 opened: HTML input could not express an embedded contentlet, image, video, YouTube embed or grid, so a document holding them could not be edited over the HTML leg (#36659). The same seven labels the Markdown fences use are now namespaced custom elements, validated by the very same DotcmsFences code, so both carriers produce identical documents: - Scalar payloads ride as attributes, hyphenated because HTML lowercases attribute names (language-id, mime-type, text-wrap) and mapped back to the camelCase attrs the editor expects; the flattened lowercase spelling is accepted too. - dotcms-ai, dotcms-grid and dotcms-node take the fence-identical JSON as the element's text body, keeping the size cap, depth cap and doc rejection that the verbatim labels rely on. - <!-- dotcms:attrs {...} --> comments decorate the next block with the Markdown leg's exact semantics: next-block-only, last-wins, structural attrs win on collision, consumed but never applied to a rich node. Degrade, never throw: a body label keeps its payload as a visible codeBlock exactly like an invalid fence, a scalar label drops (its attributes carry no text to preserve), and an unknown dotcms-* element stays transparent like any other unknown tag. A self-closed element does not lose the siblings HTML parsing swallows into it. Sanitization does not widen. DROP_SUBTREE is unchanged (YouTube and video ride a sanitized src, never a real iframe/video element), attributes are read by name per element rather than copied, and every URL-bearing field passes sanitizeUrl.
…ML leg The save path's HTML detection regex only matches spec-simple tag names, so a value opening with a hyphenated custom element fell through to the Markdown converter and was stored as literal text — the new vocabulary was unreachable for exactly the values that lead with it. Two routing corrections, both narrow: - A leading <dotcms-*> element routes to the HTML converter, mirroring the startsWithDotcmsAttrsComment carve-out rather than broadening HTML_START to every hyphenated tag (which would silently reroute unrelated values). - The decision is now made on what FOLLOWS any leading dotcms:attrs comments. Before Markdown they still reach the Markdown converter; before HTML they now reach the HTML converter, which honors the decoration instead of dropping it with the markup. An unterminated comment cannot be skipped, so it keeps the Markdown route it had. Integration coverage: an HTML value opening with <dotcms-content> stores and reads back hydrated through checkin; every label stores from one HTML value; comment-then-HTML routes and decorates; an HTML update carrying the stored rich block over produces no replacement warning; the unterminated comment keeps its route.
… contract The fire endpoints described the rich-node vocabulary as Markdown fences only, so the HTML spelling was undiscoverable for the non-interactive clients it exists for. - BLOCK_EDITOR_FIELD_NOTE gains the element forms, the hyphenated-attribute rule, the body-JSON escaping rule, and the always-close-the-tag trap (HTML parsing ignores the / in <dotcms-video ... /> and swallows what follows). - The note becomes public and is attached to ContentResource#saveDraft, which funnels through the same populator seam but documented none of it. - openapi.yaml regenerated from the annotations. - MCP execute guidance updated so agent clients can author either carrier.
3317152 to
cce1039
Compare
What
Closes #36659. HTML input now expresses the same rich nodes the Markdown leg gained in #36658 (dotContent, dotImage, dotVideo, youtube, aiContent, gridBlock, custom blocks), as namespaced elements — HTML is no longer the lossier save-path format.
Markup form: the seven fence labels as element names. Scalars ride as attributes, hyphenated because HTML lowercases attribute names (
language-id→languageId); the verbatim labels anddotcms-aitake the fence-identical JSON as element body (attributes can't carry a subtree, and JSON in an attribute corrupts on the first quote). The editor's own HTML was rejected as a model:dotContenthas noparseHTML, anddotVideo/youtubeparse from<video>/<iframe>— both inDROP_SUBTREE, which stays.Shared validator: both carriers normalize into the same
TiptapMarkdown.DotcmsFences(now package-visible), so parity holds by construction — same schema, node type, 64KB/depth caps,docrejection.Degrade, never throw: invalid body labels keep their payload as a visible
codeBlocklike an invalid fence; scalar labels drop; unknowndotcms-*stays transparent.Sanitization does not widen: no blanket attribute copy, every URL through
sanitizeUrl.Behavior change
Two narrow routing fixes in
MapToContentletPopulator: a value opening with<dotcms-*>used to store as literal text (HTML_STARTmisses hyphenated tags) and now routes to HTML via a<dotcms-carve-out; and routing is decided on what follows leadingdotcms:attrscomments, so comment-then-HTML reaches the HTML converter instead of losing the decoration. Stored JSON still passes through untouched; same status codes, no new rejections.Verification
204 unit (51 new, incl. HTML≡Markdown parity suite + hostile attrs on the new elements; existing sanitization suite unchanged), 20 integration (5 new). Live pass: the HTML-created doc opens in the Block Editor with every rich node rendering, takes a real edit, and re-saves intact; the Markdown-fence sibling renders identically. OpenAPI + MCP guidance updated.