diff --git a/app/models/Document.ts b/app/models/Document.ts index a2bcf9c697..4c4b9f182f 100644 --- a/app/models/Document.ts +++ b/app/models/Document.ts @@ -677,7 +677,13 @@ export default class Document extends ArchivableModel implements Searchable { nodes: extensionManager.nodes, marks: extensionManager.marks, }); - const markdown = serializer.serialize(Node.fromJSON(schema, this.data), { + + const doc = Node.fromJSON( + schema, + ProsemirrorHelper.attachmentsToAbsoluteUrls(this.data) + ); + + const markdown = serializer.serialize(doc, { softBreak: true, }); return markdown; diff --git a/shared/i18n/locales/en_US/translation.json b/shared/i18n/locales/en_US/translation.json index 66c201393a..d3c30946aa 100644 --- a/shared/i18n/locales/en_US/translation.json +++ b/shared/i18n/locales/en_US/translation.json @@ -552,7 +552,6 @@ "API key": "API key", "Show path to document": "Show path to document", "Path to document": "Path to document", - "Group member options": "Group member options", "Export collection": "Export collection", "Rename": "Rename", "Sort in sidebar": "Sort in sidebar", @@ -568,13 +567,13 @@ "Apply template": "Apply template", "Enable embeds": "Enable embeds", "File": "File", + "Group member options": "Group member options", "Group members": "Group members", "Edit group": "Edit group", "Delete group": "Delete group", "Group options": "Group options", "Cancel": "Cancel", "Import menu options": "Import menu options", - "Member options": "Member options", "New document in {{ collectionName }}": "New document in {{ collectionName }}", "New child document": "New child document", "Save in workspace": "Save in workspace", diff --git a/shared/utils/ProsemirrorHelper.ts b/shared/utils/ProsemirrorHelper.ts index 04c23a3d93..122c31ca38 100644 --- a/shared/utils/ProsemirrorHelper.ts +++ b/shared/utils/ProsemirrorHelper.ts @@ -4,6 +4,7 @@ import textBetween from "../editor/lib/textBetween"; import { getTextSerializers } from "../editor/lib/textSerializers"; import { ProsemirrorData } from "../types"; import { TextHelper } from "./TextHelper"; +import env from "../env"; export type Heading = { /* The heading in plain text */ @@ -364,6 +365,53 @@ export class ProsemirrorHelper { return headings; } + /** + * Converts all attachment URLs in the ProsemirrorData to absolute URLs. + * This is useful for ensuring that attachments can be accessed correctly + * when the document is rendered in a different context or environment. + * + * @param data The ProsemirrorData object to process + * @returns The ProsemirrorData with absolute URLs for attachments + */ + static attachmentsToAbsoluteUrls(data: ProsemirrorData): ProsemirrorData { + function replace(node: ProsemirrorData) { + if ( + node.type === "image" && + node.attrs?.src && + String(node.attrs.src).match( + new RegExp("^" + attachmentRedirectRegex.source) + ) + ) { + node.attrs.src = env.URL + node.attrs.src; + } + if ( + node.type === "video" && + node.attrs?.src && + String(node.attrs.src).match( + new RegExp("^" + attachmentRedirectRegex.source) + ) + ) { + node.attrs.src = env.URL + node.attrs.src; + } + if ( + node.type === "attachment" && + node.attrs?.href && + String(node.attrs.src).match( + new RegExp("^" + attachmentRedirectRegex.source) + ) + ) { + node.attrs.href = env.URL + node.attrs.href; + } + if (node.content) { + node.content.forEach(replace); + } + + return node; + } + + return replace(data); + } + /** * Replaces all template variables in the node. *