reverted incomplete work on export per-directory metadata

This commit is contained in:
zadam 2023-11-03 12:18:38 +01:00
parent f8ade4d0c5
commit a45b801a84
3 changed files with 51 additions and 112 deletions

View File

@ -16,7 +16,6 @@ const archiver = require('archiver');
const log = require("../log"); const log = require("../log");
const TaskContext = require("../task_context"); const TaskContext = require("../task_context");
const ValidationError = require("../../errors/validation_error"); const ValidationError = require("../../errors/validation_error");
const MetaFile = require("../meta/meta_file");
const NoteMeta = require("../meta/note_meta"); const NoteMeta = require("../meta/note_meta");
const AttachmentMeta = require("../meta/attachment_meta"); const AttachmentMeta = require("../meta/attachment_meta");
const AttributeMeta = require("../meta/attribute_meta"); const AttributeMeta = require("../meta/attribute_meta");
@ -40,24 +39,6 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true)
/** @type {Object.<string, NoteMeta>} */ /** @type {Object.<string, NoteMeta>} */
const noteIdToMeta = {}; 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 {Object.<string, int>} existingFileNames
* @param {string} fileName * @param {string} fileName
@ -135,49 +116,13 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true)
return getUniqueFilename(existingFileNames, fileName); 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 {BBranch} branch
* @param {NoteMeta} parentMeta
* @param {Object.<string, int>} existingFileNames * @param {Object.<string, int>} existingFileNames
* @returns {NoteMeta|null} * @returns {NoteMeta|null}
*/ */
function createNoteMeta(branch, existingFileNames) { function createNoteMeta(branch, parentMeta, existingFileNames) {
const note = branch.getNote(); const note = branch.getNote();
if (note.hasOwnedLabel('excludeFromExport')) { if (note.hasOwnedLabel('excludeFromExport')) {
@ -192,13 +137,15 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true)
baseFileName = baseFileName.substr(0, 200); baseFileName = baseFileName.substr(0, 200);
} }
const notePath = parentMeta.notePath.concat([note.noteId]);
if (note.noteId in noteIdToMeta) { if (note.noteId in noteIdToMeta) {
const fileName = getUniqueFilename(existingFileNames, const fileName = getUniqueFilename(existingFileNames, `${baseFileName}.clone.${format === 'html' ? 'html' : 'md'}`);
`${baseFileName}.clone.${format === 'html' ? 'html' : 'md'}`);
const meta = new NoteMeta(); const meta = new NoteMeta();
meta.isClone = true; meta.isClone = true;
meta.noteId = note.noteId; meta.noteId = note.noteId;
meta.notePath = notePath;
meta.title = note.getTitleOrProtected(); meta.title = note.getTitleOrProtected();
meta.prefix = branch.prefix; meta.prefix = branch.prefix;
meta.dataFileName = fileName; meta.dataFileName = fileName;
@ -210,6 +157,7 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true)
const meta = new NoteMeta(); const meta = new NoteMeta();
meta.isClone = false; meta.isClone = false;
meta.noteId = note.noteId; meta.noteId = note.noteId;
meta.notePath = notePath;
meta.title = note.getTitleOrProtected(); meta.title = note.getTitleOrProtected();
meta.notePosition = branch.notePosition; meta.notePosition = branch.notePosition;
meta.prefix = branch.prefix; meta.prefix = branch.prefix;
@ -260,10 +208,23 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true)
existingFileNames existingFileNames
); );
return attMeta; return attMeta;
}); });
if (childBranches.length > 0) { if (childBranches.length > 0) {
meta.dirFileName = getUniqueFilename(existingFileNames, baseFileName); 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; return meta;
@ -281,8 +242,8 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true)
return null; return null;
} }
const targetPath = getNotePath(targetMeta.noteId); const targetPath = targetMeta.notePath.slice();
const sourcePath = getNotePath(sourceMeta.noteId); const sourcePath = sourceMeta.notePath.slice();
// > 1 for the edge case that targetPath and sourcePath are exact same (a link to itself) // > 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]) { while (targetPath.length > 1 && sourcePath.length > 1 && targetPath[0] === sourcePath[0]) {
@ -367,7 +328,7 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true)
if (noteMeta.format === 'html') { if (noteMeta.format === 'html') {
if (!content.substr(0, 100).toLowerCase().includes("<html")) { if (!content.substr(0, 100).toLowerCase().includes("<html")) {
const cssUrl = `${"../".repeat(getNotePath(noteMeta.noteId).length - 1)}style.css`; const cssUrl = `${"../".repeat(noteMeta.notePath.length - 1)}style.css`;
const htmlTitle = utils.escapeHtml(title); const htmlTitle = utils.escapeHtml(title);
// <base> element will make sure external links are openable - https://github.com/zadam/trilium/issues/1289#issuecomment-704066809 // <base> element will make sure external links are openable - https://github.com/zadam/trilium/issues/1289#issuecomment-704066809
@ -448,17 +409,14 @@ ${markdownContent}`;
}); });
} }
const metaFile = createMetaFileForDirectory(note.getChildBranches()); if (noteMeta.children?.length > 0) {
if (metaFile) { const directoryPath = filePathPrefix + noteMeta.dirFileName;
const childFilePathPrefix = `${filePathPrefix}${noteMeta.dirFileName}/`;
// create directory // create directory
archive.append('', { name: childFilePathPrefix, date: dateUtils.parseDateTime(note.utcDateModified) }); archive.append('', { name: `${directoryPath}/`, date: dateUtils.parseDateTime(note.utcDateModified) });
metaFile.save(archive, childFilePathPrefix); for (const childMeta of noteMeta.children) {
saveNote(childMeta, `${directoryPath}/`);
for (const childMeta of metaFile.files) {
saveNote(childMeta, childFilePathPrefix);
} }
} }
} }
@ -482,13 +440,11 @@ ${markdownContent}`;
html += escapedTitle; html += escapedTitle;
} }
const childrenMeta = parentNoteIdToChildrenMeta[meta.noteId]; if (meta.children && meta.children.length > 0) {
if (childrenMeta?.length > 0) {
html += '<ul>'; html += '<ul>';
for (const childMeta of childrenMeta) { for (const child of meta.children) {
html += saveNavigationInner(childMeta); html += saveNavigationInner(child);
} }
html += '</ul>' html += '</ul>'
@ -526,10 +482,8 @@ ${markdownContent}`;
firstNonEmptyNote = getNoteTargetUrl(curMeta.noteId, rootMeta); firstNonEmptyNote = getNoteTargetUrl(curMeta.noteId, rootMeta);
} }
const childrenMeta = parentNoteIdToChildrenMeta[curMeta.noteId]; if (curMeta.children && curMeta.children.length > 0) {
curMeta = curMeta.children[0];
if (childrenMeta?.length > 0) {
curMeta = childrenMeta[0];
} }
else { else {
break; break;
@ -561,13 +515,14 @@ ${markdownContent}`;
archive.append(cssContent, { name: cssMeta.dataFileName }); archive.append(cssContent, { name: cssMeta.dataFileName });
} }
const existingFileNames = format === 'html' ? { 'navigation': 1, 'index': 1 } : {}; const existingFileNames = format === 'html' ? ['navigation', 'index'] : [];
const metaFile = createMetaFileForDirectory([branch], existingFileNames); const rootMeta = createNoteMeta(branch, { notePath: [] }, existingFileNames);
if (!metaFile) { // corner case of disabled export for exported note const metaFile = {
res.sendStatus(400); formatVersion: 2,
return; appVersion: packageInfo.version,
} files: [ rootMeta ]
};
let navigationMeta, indexMeta, cssMeta; let navigationMeta, indexMeta, cssMeta;
@ -610,9 +565,15 @@ ${markdownContent}`;
}); });
} }
metaFile.save(archive); 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" });
const rootMeta = metaFile.files[0];
saveNote(rootMeta, ''); saveNote(rootMeta, '');
if (format === 'html') { if (format === 'html') {

View File

@ -1,16 +0,0 @@
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;

View File

@ -1,10 +1,7 @@
class NoteMeta { class NoteMeta {
/** @type {string} */ /** @type {string} */
noteId; noteId;
/** /** @type {string} */
* @type {string[]}
* @deprecated unused as of v3
*/
notePath; notePath;
/** @type {boolean} */ /** @type {boolean} */
isClone; isClone;
@ -32,10 +29,7 @@ class NoteMeta {
attributes; attributes;
/** @type {AttachmentMeta[]} */ /** @type {AttachmentMeta[]} */
attachments; 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; children;
} }