mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
saving export meta file per directory
This commit is contained in:
parent
c881b39860
commit
5b85713bf3
@ -16,6 +16,7 @@ const archiver = require('archiver');
|
||||
const log = require("../log");
|
||||
const TaskContext = require("../task_context");
|
||||
const ValidationError = require("../../errors/validation_error");
|
||||
const MetaFile = require("../meta/meta_file");
|
||||
const NoteMeta = require("../meta/note_meta");
|
||||
const AttachmentMeta = require("../meta/attachment_meta");
|
||||
const AttributeMeta = require("../meta/attribute_meta");
|
||||
@ -39,6 +40,24 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true)
|
||||
/** @type {Object.<string, NoteMeta>} */
|
||||
const noteIdToMeta = {};
|
||||
|
||||
/** @type {Object.<string, NoteMeta>} */
|
||||
const childNoteIdToParentMeta = {};
|
||||
|
||||
/** @type {Object.<string, NoteMeta[]>} */
|
||||
const parentNoteIdToChildrenMeta = {};
|
||||
|
||||
function getNotePath(noteId) {
|
||||
const notePath = [noteId];
|
||||
|
||||
let cur = noteIdToMeta[noteId];
|
||||
while (cur = childNoteIdToParentMeta[cur.noteId]) {
|
||||
notePath.push(cur.noteId);
|
||||
}
|
||||
|
||||
notePath.reverse();
|
||||
return notePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object.<string, int>} existingFileNames
|
||||
* @param {string} fileName
|
||||
@ -116,13 +135,49 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true)
|
||||
return getUniqueFilename(existingFileNames, fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BBranch[]} branches
|
||||
* @param {Object.<string, int>} existingFileNames
|
||||
* @returns {MetaFile}
|
||||
*/
|
||||
function createMetaFileForDirectory(branches, existingFileNames = {}) {
|
||||
// namespace is shared by children in the same note
|
||||
const metas = [];
|
||||
|
||||
for (const branch of branches) {
|
||||
const noteMeta = createNoteMeta(branch, existingFileNames);
|
||||
|
||||
// can be undefined if export is disabled for this note
|
||||
if (noteMeta) {
|
||||
metas.push(noteMeta);
|
||||
|
||||
const parentMeta = noteIdToMeta[branch.parentNoteId];
|
||||
|
||||
if (parentMeta) {
|
||||
childNoteIdToParentMeta[noteMeta.noteId] = parentMeta;
|
||||
parentNoteIdToChildrenMeta[parentMeta.noteId] = parentNoteIdToChildrenMeta[parentMeta.noteId] || [];
|
||||
parentNoteIdToChildrenMeta[parentMeta.noteId].push(noteMeta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!metas.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const metaFile = new MetaFile();
|
||||
metaFile.formatVersion = 3;
|
||||
metaFile.appVersion = packageInfo.version;
|
||||
metaFile.files = metas;
|
||||
return metaFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BBranch} branch
|
||||
* @param {NoteMeta} parentMeta
|
||||
* @param {Object.<string, int>} existingFileNames
|
||||
* @returns {NoteMeta|null}
|
||||
*/
|
||||
function createNoteMeta(branch, parentMeta, existingFileNames) {
|
||||
function createNoteMeta(branch, existingFileNames) {
|
||||
const note = branch.getNote();
|
||||
|
||||
if (note.hasOwnedLabel('excludeFromExport')) {
|
||||
@ -137,15 +192,13 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true)
|
||||
baseFileName = baseFileName.substr(0, 200);
|
||||
}
|
||||
|
||||
const notePath = parentMeta.notePath.concat([note.noteId]);
|
||||
|
||||
if (note.noteId in noteIdToMeta) {
|
||||
const fileName = getUniqueFilename(existingFileNames, `${baseFileName}.clone.${format === 'html' ? 'html' : 'md'}`);
|
||||
const fileName = getUniqueFilename(existingFileNames,
|
||||
`${baseFileName}.clone.${format === 'html' ? 'html' : 'md'}`);
|
||||
|
||||
const meta = new NoteMeta();
|
||||
meta.isClone = true;
|
||||
meta.noteId = note.noteId;
|
||||
meta.notePath = notePath;
|
||||
meta.title = note.getTitleOrProtected();
|
||||
meta.prefix = branch.prefix;
|
||||
meta.dataFileName = fileName;
|
||||
@ -157,7 +210,6 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true)
|
||||
const meta = new NoteMeta();
|
||||
meta.isClone = false;
|
||||
meta.noteId = note.noteId;
|
||||
meta.notePath = notePath;
|
||||
meta.title = note.getTitleOrProtected();
|
||||
meta.notePosition = branch.notePosition;
|
||||
meta.prefix = branch.prefix;
|
||||
@ -212,19 +264,6 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true)
|
||||
|
||||
if (childBranches.length > 0) {
|
||||
meta.dirFileName = getUniqueFilename(existingFileNames, baseFileName);
|
||||
meta.children = [];
|
||||
|
||||
// namespace is shared by children in the same note
|
||||
const childExistingNames = {};
|
||||
|
||||
for (const childBranch of childBranches) {
|
||||
const note = createNoteMeta(childBranch, meta, childExistingNames);
|
||||
|
||||
// can be undefined if export is disabled for this note
|
||||
if (note) {
|
||||
meta.children.push(note);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return meta;
|
||||
@ -242,8 +281,8 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true)
|
||||
return null;
|
||||
}
|
||||
|
||||
const targetPath = targetMeta.notePath.slice();
|
||||
const sourcePath = sourceMeta.notePath.slice();
|
||||
const targetPath = getNotePath(targetMeta.noteId);
|
||||
const sourcePath = getNotePath(sourceMeta.noteId);
|
||||
|
||||
// > 1 for the edge case that targetPath and sourcePath are exact same (a link to itself)
|
||||
while (targetPath.length > 1 && sourcePath.length > 1 && targetPath[0] === sourcePath[0]) {
|
||||
@ -328,7 +367,7 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true)
|
||||
|
||||
if (noteMeta.format === 'html') {
|
||||
if (!content.substr(0, 100).toLowerCase().includes("<html")) {
|
||||
const cssUrl = `${"../".repeat(noteMeta.notePath.length - 1)}style.css`;
|
||||
const cssUrl = `${"../".repeat(getNotePath(noteMeta.noteId).length - 1)}style.css`;
|
||||
const htmlTitle = utils.escapeHtml(title);
|
||||
|
||||
// <base> element will make sure external links are openable - https://github.com/zadam/trilium/issues/1289#issuecomment-704066809
|
||||
@ -409,14 +448,17 @@ ${markdownContent}`;
|
||||
});
|
||||
}
|
||||
|
||||
if (noteMeta.children?.length > 0) {
|
||||
const directoryPath = filePathPrefix + noteMeta.dirFileName;
|
||||
const metaFile = createMetaFileForDirectory(note.getChildBranches());
|
||||
if (metaFile) {
|
||||
const childFilePathPrefix = `${filePathPrefix}${noteMeta.dirFileName}/`;
|
||||
|
||||
// create directory
|
||||
archive.append('', { name: `${directoryPath}/`, date: dateUtils.parseDateTime(note.utcDateModified) });
|
||||
archive.append('', { name: childFilePathPrefix, date: dateUtils.parseDateTime(note.utcDateModified) });
|
||||
|
||||
for (const childMeta of noteMeta.children) {
|
||||
saveNote(childMeta, `${directoryPath}/`);
|
||||
metaFile.save(archive, childFilePathPrefix);
|
||||
|
||||
for (const childMeta of metaFile.files) {
|
||||
saveNote(childMeta, childFilePathPrefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -440,11 +482,13 @@ ${markdownContent}`;
|
||||
html += escapedTitle;
|
||||
}
|
||||
|
||||
if (meta.children && meta.children.length > 0) {
|
||||
const childrenMeta = parentNoteIdToChildrenMeta[meta.noteId];
|
||||
|
||||
if (childrenMeta?.length > 0) {
|
||||
html += '<ul>';
|
||||
|
||||
for (const child of meta.children) {
|
||||
html += saveNavigationInner(child);
|
||||
for (const childMeta of childrenMeta) {
|
||||
html += saveNavigationInner(childMeta);
|
||||
}
|
||||
|
||||
html += '</ul>'
|
||||
@ -482,8 +526,10 @@ ${markdownContent}`;
|
||||
firstNonEmptyNote = getNoteTargetUrl(curMeta.noteId, rootMeta);
|
||||
}
|
||||
|
||||
if (curMeta.children && curMeta.children.length > 0) {
|
||||
curMeta = curMeta.children[0];
|
||||
const childrenMeta = parentNoteIdToChildrenMeta[curMeta.noteId];
|
||||
|
||||
if (childrenMeta?.length > 0) {
|
||||
curMeta = childrenMeta[0];
|
||||
}
|
||||
else {
|
||||
break;
|
||||
@ -515,14 +561,13 @@ ${markdownContent}`;
|
||||
archive.append(cssContent, { name: cssMeta.dataFileName });
|
||||
}
|
||||
|
||||
const existingFileNames = format === 'html' ? ['navigation', 'index'] : [];
|
||||
const rootMeta = createNoteMeta(branch, { notePath: [] }, existingFileNames);
|
||||
const existingFileNames = format === 'html' ? { 'navigation': 1, 'index': 1 } : {};
|
||||
const metaFile = createMetaFileForDirectory([branch], existingFileNames);
|
||||
|
||||
const metaFile = {
|
||||
formatVersion: 2,
|
||||
appVersion: packageInfo.version,
|
||||
files: [ rootMeta ]
|
||||
};
|
||||
if (!metaFile) { // corner case of disabled export for exported note
|
||||
res.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
||||
let navigationMeta, indexMeta, cssMeta;
|
||||
|
||||
@ -565,15 +610,9 @@ ${markdownContent}`;
|
||||
});
|
||||
}
|
||||
|
||||
if (!rootMeta) { // corner case of disabled export for exported note
|
||||
res.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
||||
const metaFileJson = JSON.stringify(metaFile, null, '\t');
|
||||
|
||||
archive.append(metaFileJson, { name: "!!!meta.json" });
|
||||
metaFile.save(archive);
|
||||
|
||||
const rootMeta = metaFile.files[0];
|
||||
saveNote(rootMeta, '');
|
||||
|
||||
if (format === 'html') {
|
||||
|
16
src/services/meta/meta_file.js
Normal file
16
src/services/meta/meta_file.js
Normal file
@ -0,0 +1,16 @@
|
||||
class MetaFile {
|
||||
/** @type {int} */
|
||||
formatVersion;
|
||||
/** @type {string} */
|
||||
appVersion;
|
||||
/** @type {NoteMeta[]} */
|
||||
files;
|
||||
|
||||
save(archive, filePathPrefix = '') {
|
||||
const metaFileJson = JSON.stringify(this, null, '\t');
|
||||
|
||||
archive.append(metaFileJson, { name: filePathPrefix + "!!!meta.json" });
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MetaFile;
|
@ -1,7 +1,10 @@
|
||||
class NoteMeta {
|
||||
/** @type {string} */
|
||||
noteId;
|
||||
/** @type {string} */
|
||||
/**
|
||||
* @type {string[]}
|
||||
* @deprecated unused as of v3
|
||||
*/
|
||||
notePath;
|
||||
/** @type {boolean} */
|
||||
isClone;
|
||||
@ -29,7 +32,10 @@ class NoteMeta {
|
||||
attributes;
|
||||
/** @type {AttachmentMeta[]} */
|
||||
attachments;
|
||||
/** @type {NoteMeta[]|undefined} */
|
||||
/**
|
||||
* @type {NoteMeta[]|undefined}
|
||||
* @deprecated unused as of v3, there's meta file for each directory, avoiding the need to use this
|
||||
*/
|
||||
children;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user