-
Notifications
You must be signed in to change notification settings - Fork 480
36002 workflow fire convert block editor markdown to prosemirror json on save server side #36253
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
9a96cc0
a47d91a
ae1d0dc
f01cb0a
c6a9c38
04cf19c
146d824
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -118,6 +118,78 @@ public static String toMarkdown(final com.dotmarketing.util.json.JSONObject tipt | |
| return toMarkdown(tiptap.toString()); | ||
| } | ||
|
|
||
| /** | ||
| * Cheap discriminator: is this string already a Tiptap/ProseMirror document | ||
| * ({@code {"type":"doc","content":[...]}})? Used on the save path to leave | ||
| * editor-authored JSON untouched rather than re-parsing it as Markdown. The | ||
| * first non-whitespace character is peeked before any parse, so the common | ||
| * non-JSON (Markdown) case costs nothing. | ||
| */ | ||
| public static boolean isTiptapDoc(final String value) { | ||
| if (value == null) { | ||
| return false; | ||
| } | ||
| final String trimmed = value.stripLeading(); | ||
| if (trimmed.isEmpty() || trimmed.charAt(0) != '{') { | ||
| return false; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. test first, and the last chars seem a bit more like a solid test |
||
| } | ||
| try { | ||
| final JsonNode node = MAPPER.readTree(value); | ||
| return node != null | ||
| && "doc".equals(node.path("type").asText()) | ||
| && node.path("content").isArray(); | ||
| } catch (final java.io.IOException e) { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * The ProseMirror node types {@link #toTiptap(String)} can produce from Markdown. | ||
| * A document built only from these can be expressed as Markdown without losing blocks. | ||
| */ | ||
| private static final Set<String> MARKDOWN_NODE_TYPES = Set.of( | ||
| "doc", "paragraph", "heading", "blockquote", "bulletList", "orderedList", "listItem", | ||
| "codeBlock", "horizontalRule", "hardBreak", "text", | ||
| "table", "tableRow", "tableHeader", "tableCell", "dotImage"); | ||
|
|
||
| /** | ||
| * True when every block in the document is Markdown-representable (see | ||
| * {@link #MARKDOWN_NODE_TYPES}). Used to refuse a Markdown overwrite that would | ||
| * silently destroy rich blocks Markdown cannot express — embedded contentlets | ||
| * ({@code dotContent}), video, layout grids, etc. Marks are intentionally not | ||
| * inspected: an unsupported mark loses styling, not content. A {@code null} or | ||
| * non-JSON value carries no rich blocks to protect, so returns {@code true}. | ||
| */ | ||
| public static boolean isMarkdownRepresentable(final String tiptapJson) { | ||
| if (tiptapJson == null) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. or empty |
||
| return true; | ||
| } | ||
| try { | ||
| return isMarkdownRepresentable(MAPPER.readTree(tiptapJson)); | ||
| } catch (final java.io.IOException e) { | ||
| return true; | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we add some logging here ? in case of a failure |
||
| } | ||
|
|
||
| private static boolean isMarkdownRepresentable(final JsonNode node) { | ||
| if (node == null) { | ||
| return true; | ||
| } | ||
| final String type = node.path("type").asText(""); | ||
| if (!type.isEmpty() && !MARKDOWN_NODE_TYPES.contains(type)) { | ||
| return false; | ||
| } | ||
| final JsonNode content = node.path("content"); | ||
| if (content.isArray()) { | ||
| for (final JsonNode child : content) { | ||
| if (!isMarkdownRepresentable(child)) { | ||
| return false; | ||
| } | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| // ===================================================================== | ||
| // Markdown -> Tiptap JSON (commonmark Visitor) | ||
| // ===================================================================== | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this a bit too aggressive?? Perhaps all we need to do is log the error, but not interrupt the application flow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps all we need to do here is log the error or a Warning