diff --git a/apps/server/src/services/export/zip.ts b/apps/server/src/services/export/zip.ts
index 7034f8e18..6caffac86 100644
--- a/apps/server/src/services/export/zip.ts
+++ b/apps/server/src/services/export/zip.ts
@@ -2,11 +2,11 @@
import html from "html";
import dateUtils from "../date_utils.js";
-import path, { join } from "path";
+import path from "path";
import mimeTypes from "mime-types";
import mdService from "./markdown.js";
import packageInfo from "../../../package.json" with { type: "json" };
-import { getContentDisposition, escapeHtml, getResourceDir, isDev } from "../utils.js";
+import { getContentDisposition, escapeHtml } from "../utils.js";
import protectedSessionService from "../protected_session.js";
import sanitize from "sanitize-filename";
import fs from "fs";
@@ -19,12 +19,10 @@ import type NoteMeta from "../meta/note_meta.js";
import type AttachmentMeta from "../meta/attachment_meta.js";
import type AttributeMeta from "../meta/attribute_meta.js";
import type BBranch from "../../becca/entities/bbranch.js";
-import type BNote from "../../becca/entities/bnote.js";
import type { Response } from "express";
import type { NoteMetaFile } from "../meta/note_meta.js";
-//import cssContent from "@triliumnext/ckeditor5/content.css";
-import { renderNoteForExport } from "../../share/content_renderer.js";
-import { RESOURCE_DIR } from "../resource_dir.js";
+import HtmlExportProvider from "./zip/html.js";
+import { ZipExportProvider } from "./zip/abstract_provider.js";
type RewriteLinksFn = (content: string, noteMeta: NoteMeta) => string;
@@ -317,7 +315,7 @@ async function exportToZip(taskContext: TaskContext, branch: BBranch, format: "h
}
}
- function prepareContent(note: BNote | undefined, title: string, content: string | Buffer, noteMeta: NoteMeta): string | Buffer {
+ function prepareContent(title: string, content: string | Buffer, noteMeta: NoteMeta): string | Buffer {
if (["html", "markdown"].includes(noteMeta?.format || "")) {
content = content.toString();
content = rewriteFn(content, noteMeta);
@@ -329,18 +327,11 @@ async function exportToZip(taskContext: TaskContext, branch: BBranch, format: "h
throw new Error("Missing note path.");
}
- const basePath = "../".repeat(noteMeta.notePath.length - 1);
+ const cssUrl = `${"../".repeat(noteMeta.notePath.length - 1)}style.css`;
const htmlTitle = escapeHtml(title);
- if (note) {
- content = renderNoteForExport(note, branch, basePath);
-
- // TODO: Fix double rewrite.
- content = rewriteFn(content, noteMeta);
- } else {
- const cssUrl = basePath + "style.css";
- // element will make sure external links are openable - https://github.com/zadam/trilium/issues/1289#issuecomment-704066809
- content = `
+ // element will make sure external links are openable - https://github.com/zadam/trilium/issues/1289#issuecomment-704066809
+ content = `
@@ -356,7 +347,6 @@ async function exportToZip(taskContext: TaskContext, branch: BBranch, format: "h
`;
- }
}
return content.length < 100_000 ? html.prettyPrint(content, { indent_size: 2 }) : content;
@@ -386,7 +376,7 @@ ${markdownContent}`;
let content: string | Buffer = `This is a clone of a note. Go to its primary location.
`;
- content = prepareContent(undefined, noteMeta.title, content, noteMeta);
+ content = prepareContent(noteMeta.title, content, noteMeta);
archive.append(content, { name: filePathPrefix + noteMeta.dataFileName });
@@ -402,7 +392,7 @@ ${markdownContent}`;
}
if (noteMeta.dataFileName) {
- const content = prepareContent(note, noteMeta.title, note.getContent(), noteMeta);
+ const content = prepareContent(noteMeta.title, note.getContent(), noteMeta);
archive.append(content, {
name: filePathPrefix + noteMeta.dataFileName,
@@ -438,97 +428,6 @@ ${markdownContent}`;
}
}
- function saveNavigation(rootMeta: NoteMeta, navigationMeta: NoteMeta) {
- if (!navigationMeta.dataFileName) {
- return;
- }
-
- function saveNavigationInner(meta: NoteMeta) {
- let html = "";
-
- const escapedTitle = escapeHtml(`${meta.prefix ? `${meta.prefix} - ` : ""}${meta.title}`);
-
- if (meta.dataFileName && meta.noteId) {
- const targetUrl = getNoteTargetUrl(meta.noteId, rootMeta);
-
- html += `${escapedTitle}`;
- } else {
- html += escapedTitle;
- }
-
- if (meta.children && meta.children.length > 0) {
- html += "";
-
- for (const child of meta.children) {
- html += saveNavigationInner(child);
- }
-
- html += "
";
- }
-
- return `${html}`;
- }
-
- const fullHtml = `
-
-
-
-
-
- ${saveNavigationInner(rootMeta)}
-
-`;
- const prettyHtml = fullHtml.length < 100_000 ? html.prettyPrint(fullHtml, { indent_size: 2 }) : fullHtml;
-
- archive.append(prettyHtml, { name: navigationMeta.dataFileName });
- }
-
- function saveIndex(rootMeta: NoteMeta, indexMeta: NoteMeta) {
- let firstNonEmptyNote;
- let curMeta = rootMeta;
-
- if (!indexMeta.dataFileName) {
- return;
- }
-
- while (!firstNonEmptyNote) {
- if (curMeta.dataFileName && curMeta.noteId) {
- firstNonEmptyNote = getNoteTargetUrl(curMeta.noteId, rootMeta);
- }
-
- if (curMeta.children && curMeta.children.length > 0) {
- curMeta = curMeta.children[0];
- } else {
- break;
- }
- }
-
- const fullHtml = `
-
-
-
-
-
-
-`;
-
- archive.append(fullHtml, { name: indexMeta.dataFileName });
- }
-
- function saveAssets(rootMeta: NoteMeta, assetsMeta: NoteMeta[]) {
- for (const assetMeta of assetsMeta) {
- if (!assetMeta.dataFileName) {
- continue;
- }
-
- let cssContent = getShareThemeAssets(assetMeta.dataFileName);
- archive.append(cssContent, { name: assetMeta.dataFileName });
- }
- }
-
const existingFileNames: Record = format === "html" ? { navigation: 0, index: 1 } : {};
const rootMeta = createNoteMeta(branch, { notePath: [] }, existingFileNames);
if (!rootMeta) {
@@ -541,47 +440,23 @@ ${markdownContent}`;
files: [rootMeta]
};
- let navigationMeta: NoteMeta | null = null;
- let indexMeta: NoteMeta | null = null;
- let assetsMeta: NoteMeta[] = [];
-
- if (format === "html") {
- navigationMeta = {
- noImport: true,
- dataFileName: "navigation.html"
- };
-
- metaFile.files.push(navigationMeta);
-
- indexMeta = {
- noImport: true,
- dataFileName: "index.html"
- };
-
- metaFile.files.push(indexMeta);
-
- const assets = [
- "style.css",
- "script.js",
- "boxicons.css",
- "boxicons.eot",
- "boxicons.woff2",
- "boxicons.woff",
- "boxicons.ttf",
- "boxicons.svg",
- "icon-color.svg"
- ];
-
- for (const asset of assets) {
- const assetMeta = {
- noImport: true,
- dataFileName: asset
- };
- assetsMeta.push(assetMeta);
- metaFile.files.push(assetMeta);
- }
+ let provider: ZipExportProvider;
+ switch (format) {
+ case "html":
+ provider = new HtmlExportProvider({
+ getNoteTargetUrl,
+ metaFile,
+ archive,
+ rootMeta
+ });
+ break;
+ case "markdown":
+ default:
+ throw new Error();
}
+ provider.prepareMeta();
+
for (const noteMeta of Object.values(noteIdToMeta)) {
// filter out relations which are not inside this export
noteMeta.attributes = (noteMeta.attributes || []).filter((attr) => {
@@ -612,15 +487,7 @@ ${markdownContent}`;
saveNote(rootMeta, "");
- if (format === "html") {
- if (!navigationMeta || !indexMeta || !assetsMeta) {
- throw new Error("Missing meta.");
- }
-
- saveNavigation(rootMeta, navigationMeta);
- saveIndex(rootMeta, indexMeta);
- saveAssets(rootMeta, assetsMeta);
- }
+ provider.afterDone();
const note = branch.getNote();
const zipFileName = `${branch.prefix ? `${branch.prefix} - ` : ""}${note.getTitleOrProtected()}.zip`;
@@ -651,28 +518,6 @@ async function exportToZipFile(noteId: string, format: "markdown" | "html", zipF
log.info(`Exported '${noteId}' with format '${format}' to '${zipFilePath}'`);
}
-function getShareThemeAssets(nameWithExtension: string) {
- // Rename share.css to style.css.
- if (nameWithExtension === "style.css") {
- nameWithExtension = "share.css";
- } else if (nameWithExtension === "script.js") {
- nameWithExtension = "share.js";
- }
-
- let path: string | undefined;
- if (nameWithExtension === "icon-color.svg") {
- path = join(RESOURCE_DIR, "images", nameWithExtension);
- } else if (isDev) {
- path = join(getResourceDir(), "..", "..", "client", "dist", "src", nameWithExtension);
- }
-
- if (!path) {
- throw new Error("Not yet defined.");
- }
-
- return fs.readFileSync(path);
-}
-
export default {
exportToZip,
exportToZipFile
diff --git a/apps/server/src/services/export/zip/abstract_provider.ts b/apps/server/src/services/export/zip/abstract_provider.ts
new file mode 100644
index 000000000..264dde0a7
--- /dev/null
+++ b/apps/server/src/services/export/zip/abstract_provider.ts
@@ -0,0 +1,27 @@
+import { Archiver } from "archiver";
+import type { default as NoteMeta, NoteMetaFile } from "../../meta/note_meta.js";
+
+interface ZipExportProviderData {
+ getNoteTargetUrl: (targetNoteId: string, sourceMeta: NoteMeta) => string | null;
+ metaFile: NoteMetaFile;
+ rootMeta: NoteMeta;
+ archive: Archiver;
+}
+
+export abstract class ZipExportProvider {
+
+ metaFile: NoteMetaFile;
+ getNoteTargetUrl: (targetNoteId: string, sourceMeta: NoteMeta) => string | null;
+ rootMeta: NoteMeta;
+ archive: Archiver;
+
+ constructor(data: ZipExportProviderData) {
+ this.metaFile = data.metaFile;
+ this.getNoteTargetUrl = data.getNoteTargetUrl;
+ this.rootMeta = data.rootMeta;
+ this.archive = data.archive;
+ }
+
+ abstract prepareMeta(): void;
+ abstract afterDone(): void;
+}
diff --git a/apps/server/src/services/export/zip/html.ts b/apps/server/src/services/export/zip/html.ts
new file mode 100644
index 000000000..517552e1d
--- /dev/null
+++ b/apps/server/src/services/export/zip/html.ts
@@ -0,0 +1,135 @@
+import type NoteMeta from "../../meta/note_meta.js";
+import { escapeHtml } from "../../utils";
+import cssContent from "@triliumnext/ckeditor5/content.css";
+import html from "html";
+import { ZipExportProvider } from "./abstract_provider.js";
+
+export default class HtmlExportProvider extends ZipExportProvider {
+
+ private navigationMeta: NoteMeta | null = null;
+ private indexMeta: NoteMeta | null = null;
+ private cssMeta: NoteMeta | null = null;
+
+ prepareMeta() {
+ this.navigationMeta = {
+ noImport: true,
+ dataFileName: "navigation.html"
+ };
+
+ this.metaFile.files.push(this.navigationMeta);
+
+ this.indexMeta = {
+ noImport: true,
+ dataFileName: "index.html"
+ };
+
+ this.metaFile.files.push(this.indexMeta);
+
+ this.cssMeta = {
+ noImport: true,
+ dataFileName: "style.css"
+ };
+
+ this.metaFile.files.push(this.cssMeta);
+ }
+
+ afterDone() {
+ if (!this.navigationMeta || !this.indexMeta || !this.cssMeta) {
+ throw new Error("Missing meta.");
+ }
+
+ this.#saveNavigation(this.rootMeta, this.navigationMeta);
+ this.#saveIndex(this.rootMeta, this.indexMeta);
+ this.#saveCss(this.rootMeta, this.cssMeta);
+ }
+
+ #saveNavigationInner(meta: NoteMeta) {
+ let html = "";
+
+ const escapedTitle = escapeHtml(`${meta.prefix ? `${meta.prefix} - ` : ""}${meta.title}`);
+
+ if (meta.dataFileName && meta.noteId) {
+ const targetUrl = this.getNoteTargetUrl(meta.noteId, this.rootMeta);
+
+ html += `${escapedTitle}`;
+ } else {
+ html += escapedTitle;
+ }
+
+ if (meta.children && meta.children.length > 0) {
+ html += "";
+
+ for (const child of meta.children) {
+ html += this.#saveNavigationInner(child);
+ }
+
+ html += "
";
+ }
+
+ return `${html}`;
+ }
+
+ #saveNavigation(rootMeta: NoteMeta, navigationMeta: NoteMeta) {
+ if (!navigationMeta.dataFileName) {
+ return;
+ }
+
+ const fullHtml = `
+
+
+
+
+
+ ${this.#saveNavigationInner(rootMeta)}
+
+ `;
+ const prettyHtml = fullHtml.length < 100_000 ? html.prettyPrint(fullHtml, { indent_size: 2 }) : fullHtml;
+
+ this.archive.append(prettyHtml, { name: navigationMeta.dataFileName });
+ }
+
+ #saveIndex(rootMeta: NoteMeta, indexMeta: NoteMeta) {
+ let firstNonEmptyNote;
+ let curMeta = rootMeta;
+
+ if (!indexMeta.dataFileName) {
+ return;
+ }
+
+ while (!firstNonEmptyNote) {
+ if (curMeta.dataFileName && curMeta.noteId) {
+ firstNonEmptyNote = this.getNoteTargetUrl(curMeta.noteId, rootMeta);
+ }
+
+ if (curMeta.children && curMeta.children.length > 0) {
+ curMeta = curMeta.children[0];
+ } else {
+ break;
+ }
+ }
+
+ const fullHtml = `
+
+
+
+
+
+
+`;
+
+ this.archive.append(fullHtml, { name: indexMeta.dataFileName });
+ }
+
+ #saveCss(rootMeta: NoteMeta, cssMeta: NoteMeta) {
+ if (!cssMeta.dataFileName) {
+ return;
+ }
+
+ this.archive.append(cssContent, { name: cssMeta.dataFileName });
+ }
+
+}
+