mirror of
https://github.com/zadam/trilium.git
synced 2024-11-11 09:46:25 +08:00
export & import work correctly with clones
This commit is contained in:
parent
b277a250e5
commit
1f96a6beab
3 changed files with 85 additions and 51 deletions
|
@ -13,50 +13,31 @@ async function exportNote(req, res) {
|
||||||
|
|
||||||
const pack = tar.pack();
|
const pack = tar.pack();
|
||||||
|
|
||||||
const name = await exportNoteInner(branchId, '', pack);
|
const exportedNoteIds = [];
|
||||||
|
const name = await exportNoteInner(branchId, '');
|
||||||
|
|
||||||
pack.finalize();
|
async function exportNoteInner(branchId, directory) {
|
||||||
|
|
||||||
res.setHeader('Content-Disposition', 'file; filename="' + name + '.tar"');
|
|
||||||
res.setHeader('Content-Type', 'application/tar');
|
|
||||||
|
|
||||||
pack.pipe(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function exportNoteInner(branchId, directory, pack) {
|
|
||||||
const branch = await repository.getBranch(branchId);
|
const branch = await repository.getBranch(branchId);
|
||||||
const note = await branch.getNote();
|
const note = await branch.getNote();
|
||||||
|
|
||||||
if (note.isProtected) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const metadata = await getMetadata(note);
|
|
||||||
|
|
||||||
if (metadata.labels.find(label => label.name === 'excludeFromExport')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const metadataJson = JSON.stringify(metadata, null, '\t');
|
|
||||||
const childFileName = directory + sanitize(note.title);
|
const childFileName = directory + sanitize(note.title);
|
||||||
|
|
||||||
pack.entry({ name: childFileName + ".meta", size: metadataJson.length }, metadataJson);
|
if (exportedNoteIds.includes(note.noteId)) {
|
||||||
|
saveMetadataFile(childFileName, {
|
||||||
|
version: 1,
|
||||||
|
clone: true,
|
||||||
|
noteId: note.noteId,
|
||||||
|
prefix: branch.prefix
|
||||||
|
});
|
||||||
|
|
||||||
const content = note.type === 'text' ? html.prettyPrint(note.content, {indent_size: 2}) : note.content;
|
return;
|
||||||
|
|
||||||
pack.entry({ name: childFileName + ".dat", size: content.length }, content);
|
|
||||||
|
|
||||||
for (const child of await note.getChildBranches()) {
|
|
||||||
await exportNoteInner(child.branchId, childFileName + "/", pack);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return childFileName;
|
const metadata = {
|
||||||
}
|
|
||||||
|
|
||||||
async function getMetadata(note) {
|
|
||||||
return {
|
|
||||||
version: 1,
|
version: 1,
|
||||||
|
clone: false,
|
||||||
|
noteId: note.noteId,
|
||||||
title: note.title,
|
title: note.title,
|
||||||
|
prefix: branch.prefix,
|
||||||
type: note.type,
|
type: note.type,
|
||||||
mime: note.mime,
|
mime: note.mime,
|
||||||
labels: (await note.getLabels()).map(label => {
|
labels: (await note.getLabels()).map(label => {
|
||||||
|
@ -66,6 +47,41 @@ async function getMetadata(note) {
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (metadata.labels.find(label => label.name === 'excludeFromExport')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveMetadataFile(childFileName, metadata);
|
||||||
|
saveDataFile(childFileName, note);
|
||||||
|
|
||||||
|
exportedNoteIds.push(note.noteId);
|
||||||
|
|
||||||
|
for (const child of await note.getChildBranches()) {
|
||||||
|
await exportNoteInner(child.branchId, childFileName + "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
return childFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveDataFile(childFileName, note) {
|
||||||
|
const content = note.type === 'text' ? html.prettyPrint(note.content, {indent_size: 2}) : note.content;
|
||||||
|
|
||||||
|
pack.entry({name: childFileName + ".dat", size: content.length}, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveMetadataFile(childFileName, metadata) {
|
||||||
|
const metadataJson = JSON.stringify(metadata, null, '\t');
|
||||||
|
|
||||||
|
pack.entry({name: childFileName + ".meta", size: metadataJson.length}, metadataJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
pack.finalize();
|
||||||
|
|
||||||
|
res.setHeader('Content-Disposition', 'file; filename="' + name + '.tar"');
|
||||||
|
res.setHeader('Content-Type', 'application/tar');
|
||||||
|
|
||||||
|
pack.pipe(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
const repository = require('../../services/repository');
|
const repository = require('../../services/repository');
|
||||||
const labelService = require('../../services/labels');
|
const labelService = require('../../services/labels');
|
||||||
const noteService = require('../../services/notes');
|
const noteService = require('../../services/notes');
|
||||||
|
const Branch = require('../../entities/branch');
|
||||||
const tar = require('tar-stream');
|
const tar = require('tar-stream');
|
||||||
const stream = require('stream');
|
const stream = require('stream');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
@ -31,7 +32,7 @@ async function parseImportFile(file) {
|
||||||
const extract = tar.extract();
|
const extract = tar.extract();
|
||||||
|
|
||||||
extract.on('entry', function(header, stream, next) {
|
extract.on('entry', function(header, stream, next) {
|
||||||
let {name, key} = getFileName(header.name);
|
const {name, key} = getFileName(header.name);
|
||||||
|
|
||||||
let file = fileMap[name];
|
let file = fileMap[name];
|
||||||
|
|
||||||
|
@ -97,30 +98,46 @@ async function importTar(req) {
|
||||||
|
|
||||||
const files = await parseImportFile(file);
|
const files = await parseImportFile(file);
|
||||||
|
|
||||||
await importNotes(files, parentNoteId);
|
// maps from original noteId (in tar file) to newly generated noteId
|
||||||
|
const noteIdMap = {};
|
||||||
|
|
||||||
|
await importNotes(files, parentNoteId, noteIdMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function importNotes(files, parentNoteId) {
|
async function importNotes(files, parentNoteId, noteIdMap) {
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
if (file.meta.version !== 1) {
|
if (file.meta.version !== 1) {
|
||||||
throw new Error("Can't read meta data version " + file.meta.version);
|
throw new Error("Can't read meta data version " + file.meta.version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (file.meta.clone) {
|
||||||
|
await new Branch({
|
||||||
|
parentNoteId: parentNoteId,
|
||||||
|
noteId: noteIdMap[file.meta.noteId],
|
||||||
|
prefix: file.meta.prefix
|
||||||
|
}).save();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (file.meta.type !== 'file') {
|
if (file.meta.type !== 'file') {
|
||||||
file.data = file.data.toString("UTF-8");
|
file.data = file.data.toString("UTF-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
const {note} = await noteService.createNote(parentNoteId, file.meta.title, file.data, {
|
const {note} = await noteService.createNote(parentNoteId, file.meta.title, file.data, {
|
||||||
type: file.meta.type,
|
type: file.meta.type,
|
||||||
mime: file.meta.mime
|
mime: file.meta.mime,
|
||||||
|
prefix: file.meta.prefix
|
||||||
});
|
});
|
||||||
|
|
||||||
|
noteIdMap[file.meta.noteId] = note.noteId;
|
||||||
|
|
||||||
for (const label of file.meta.labels) {
|
for (const label of file.meta.labels) {
|
||||||
await labelService.createLabel(note.noteId, label.name, label.value);
|
await labelService.createLabel(note.noteId, label.name, label.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.children.length > 0) {
|
if (file.children.length > 0) {
|
||||||
await importNotes(file.children, note.noteId);
|
await importNotes(file.children, note.noteId, noteIdMap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ async function createNewNote(parentNoteId, noteData) {
|
||||||
noteId: note.noteId,
|
noteId: note.noteId,
|
||||||
parentNoteId: parentNoteId,
|
parentNoteId: parentNoteId,
|
||||||
notePosition: newNotePos,
|
notePosition: newNotePos,
|
||||||
|
prefix: noteData.prefix,
|
||||||
isExpanded: 0
|
isExpanded: 0
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue