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.
*