mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
export/import attachments
This commit is contained in:
parent
0bfb2631df
commit
bd8568809f
@ -2,6 +2,6 @@
|
||||
|
||||
SCHEMA_FILE_PATH=db/schema.sql
|
||||
|
||||
sqlite3 ~/trilium-data/document.db .schema | grep -v "sqlite_sequence" > "$SCHEMA_FILE_PATH"
|
||||
sqlite3 ./data/document.db .schema | grep -v "sqlite_sequence" > "$SCHEMA_FILE_PATH"
|
||||
|
||||
echo "DB schema exported to $SCHEMA_FILE_PATH"
|
@ -61,7 +61,7 @@ CREATE TABLE IF NOT EXISTS "note_revisions" (`noteRevisionId` TEXT NOT NULL PRIM
|
||||
`dateLastEdited` TEXT NOT NULL,
|
||||
`dateCreated` TEXT NOT NULL);
|
||||
CREATE TABLE IF NOT EXISTS "note_revision_contents" (`noteRevisionId` TEXT NOT NULL PRIMARY KEY,
|
||||
`content` TEXT DEFAULT NULL,
|
||||
`content` TEXT,
|
||||
`utcDateModified` TEXT NOT NULL);
|
||||
CREATE TABLE IF NOT EXISTS "options"
|
||||
(
|
||||
@ -112,3 +112,21 @@ CREATE TABLE IF NOT EXISTS "recent_notes"
|
||||
notePath TEXT not null,
|
||||
utcDateCreated TEXT not null
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "note_attachments"
|
||||
(
|
||||
noteAttachmentId TEXT not null primary key,
|
||||
noteId TEXT not null,
|
||||
name TEXT not null,
|
||||
mime TEXT not null,
|
||||
isProtected INT not null DEFAULT 0,
|
||||
contentCheckSum TEXT not null,
|
||||
utcDateModified TEXT not null,
|
||||
isDeleted INT not null,
|
||||
`deleteId` TEXT DEFAULT NULL);
|
||||
CREATE TABLE IF NOT EXISTS "note_attachment_contents" (`noteAttachmentId` TEXT NOT NULL PRIMARY KEY,
|
||||
`content` TEXT DEFAULT NULL,
|
||||
`utcDateModified` TEXT NOT NULL);
|
||||
CREATE INDEX IDX_note_attachments_name
|
||||
on note_attachments (name);
|
||||
CREATE UNIQUE INDEX IDX_note_attachments_noteId_name
|
||||
on note_attachments (noteId, name);
|
||||
|
@ -91,7 +91,7 @@ class BNoteAttachment extends AbstractBeccaEntity {
|
||||
|
||||
setContent(content) {
|
||||
this.contentCheckSum = this.calculateCheckSum(content);
|
||||
this.save();
|
||||
this.save(); // also explicitly save note_attachment to update contentCheckSum
|
||||
|
||||
const pojo = {
|
||||
noteAttachmentId: this.noteAttachmentId,
|
||||
|
@ -58,7 +58,7 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true)
|
||||
}
|
||||
}
|
||||
|
||||
function getDataFileName(note, baseFileName, existingFileNames) {
|
||||
function getDataFileName(type, mime, baseFileName, existingFileNames) {
|
||||
let fileName = baseFileName;
|
||||
|
||||
let existingExtension = path.extname(fileName).toLowerCase();
|
||||
@ -70,24 +70,25 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true)
|
||||
|
||||
// following two are handled specifically since we always want to have these extensions no matter the automatic detection
|
||||
// and/or existing detected extensions in the note name
|
||||
if (note.type === 'text' && format === 'markdown') {
|
||||
if (type === 'text' && format === 'markdown') {
|
||||
newExtension = 'md';
|
||||
}
|
||||
else if (note.type === 'text' && format === 'html') {
|
||||
else if (type === 'text' && format === 'html') {
|
||||
newExtension = 'html';
|
||||
}
|
||||
else if (note.mime === 'application/x-javascript' || note.mime === 'text/javascript') {
|
||||
else if (mime === 'application/x-javascript' || mime === 'text/javascript') {
|
||||
newExtension = 'js';
|
||||
}
|
||||
else if (existingExtension.length > 0) { // if the page already has an extension, then we'll just keep it
|
||||
newExtension = null;
|
||||
}
|
||||
else {
|
||||
if (note.mime?.toLowerCase()?.trim() === "image/jpg") {
|
||||
if (mime?.toLowerCase()?.trim() === "image/jpg") {
|
||||
newExtension = 'jpg';
|
||||
}
|
||||
else {
|
||||
newExtension = mimeTypes.extension(note.mime) || "dat";
|
||||
} else if (mime?.toLowerCase()?.trim() === "text/mermaid") {
|
||||
newExtension = 'txt';
|
||||
} else {
|
||||
newExtension = mimeTypes.extension(mime) || "dat";
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,7 +167,25 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true)
|
||||
|
||||
// if it's a leaf then we'll export it even if it's empty
|
||||
if (available && (note.getContent().length > 0 || childBranches.length === 0)) {
|
||||
meta.dataFileName = getDataFileName(note, baseFileName, existingFileNames);
|
||||
meta.dataFileName = getDataFileName(note.type, note.mime, baseFileName, existingFileNames);
|
||||
}
|
||||
|
||||
const attachments = note.getNoteAttachments();
|
||||
|
||||
if (attachments.length > 0) {
|
||||
meta.attachments = attachments
|
||||
.filter(attachment => ["canvasSvg", "mermaidSvg"].includes(attachment.name))
|
||||
.map(attachment => ({
|
||||
|
||||
name: attachment.name,
|
||||
mime: attachment.mime,
|
||||
dataFileName: getDataFileName(
|
||||
null,
|
||||
attachment.mime,
|
||||
baseFileName + "_" + attachment.name,
|
||||
existingFileNames
|
||||
)
|
||||
}));
|
||||
}
|
||||
|
||||
if (childBranches.length > 0) {
|
||||
@ -215,8 +234,15 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true)
|
||||
|
||||
const meta = noteIdToMeta[targetPath[targetPath.length - 1]];
|
||||
|
||||
// link can target note which is only "folder-note" and as such will not have a file in an export
|
||||
url += encodeURIComponent(meta.dataFileName || meta.dirFileName);
|
||||
// for some note types it's more user-friendly to see the attachment (if exists) instead of source note
|
||||
const preferredAttachment = (meta.attachments || []).find(attachment => ['mermaidSvg', 'canvasSvg'].includes(attachment.name));
|
||||
|
||||
if (preferredAttachment) {
|
||||
url += encodeURIComponent(preferredAttachment.dataFileName);
|
||||
} else {
|
||||
// link can target note which is only "folder-note" and as such will not have a file in an export
|
||||
url += encodeURIComponent(meta.dataFileName || meta.dirFileName);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
@ -310,11 +336,24 @@ ${markdownContent}`;
|
||||
if (noteMeta.dataFileName) {
|
||||
const content = prepareContent(noteMeta.title, note.getContent(), noteMeta);
|
||||
|
||||
archive.append(content, { name: filePathPrefix + noteMeta.dataFileName, date: dateUtils.parseDateTime(note.utcDateModified) });
|
||||
archive.append(content, {
|
||||
name: filePathPrefix + noteMeta.dataFileName,
|
||||
date: dateUtils.parseDateTime(note.utcDateModified)
|
||||
});
|
||||
}
|
||||
|
||||
taskContext.increaseProgressCount();
|
||||
|
||||
for (const attachmentMeta of noteMeta.attachments || []) {
|
||||
const noteAttachment = note.getNoteAttachmentByName(attachmentMeta.name);
|
||||
const content = noteAttachment.getContent();
|
||||
|
||||
archive.append(content, {
|
||||
name: filePathPrefix + attachmentMeta.dataFileName,
|
||||
date: dateUtils.parseDateTime(note.utcDateModified)
|
||||
});
|
||||
}
|
||||
|
||||
if (noteMeta.children && noteMeta.children.length > 0) {
|
||||
const directoryPath = filePathPrefix + noteMeta.dirFileName;
|
||||
|
||||
|
@ -14,6 +14,7 @@ const treeService = require("../tree");
|
||||
const yauzl = require("yauzl");
|
||||
const htmlSanitizer = require('../html_sanitizer');
|
||||
const becca = require("../../becca/becca");
|
||||
const BNoteAttachment = require("../../becca/entities/bnote_attachment");
|
||||
|
||||
/**
|
||||
* @param {TaskContext} taskContext
|
||||
@ -64,6 +65,7 @@ async function importZip(taskContext, fileBuffer, importRootNote) {
|
||||
};
|
||||
|
||||
let parent;
|
||||
let attachmentMeta = false;
|
||||
|
||||
for (const segment of pathSegments) {
|
||||
if (!cursor || !cursor.children || cursor.children.length === 0) {
|
||||
@ -71,12 +73,29 @@ async function importZip(taskContext, fileBuffer, importRootNote) {
|
||||
}
|
||||
|
||||
parent = cursor;
|
||||
cursor = cursor.children.find(file => file.dataFileName === segment || file.dirFileName === segment);
|
||||
cursor = parent.children.find(file => file.dataFileName === segment || file.dirFileName === segment);
|
||||
|
||||
if (!cursor) {
|
||||
for (const file of parent.children) {
|
||||
for (const attachment of file.attachments || []) {
|
||||
if (attachment.dataFileName === segment) {
|
||||
cursor = file;
|
||||
attachmentMeta = attachment;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cursor) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
parentNoteMeta: parent,
|
||||
noteMeta: cursor
|
||||
noteMeta: cursor,
|
||||
attachmentMeta
|
||||
};
|
||||
}
|
||||
|
||||
@ -354,13 +373,25 @@ async function importZip(taskContext, fileBuffer, importRootNote) {
|
||||
}
|
||||
|
||||
function saveNote(filePath, content) {
|
||||
const {parentNoteMeta, noteMeta} = getMeta(filePath);
|
||||
const {parentNoteMeta, noteMeta, attachmentMeta} = getMeta(filePath);
|
||||
|
||||
if (noteMeta?.noImport) {
|
||||
return;
|
||||
}
|
||||
|
||||
const noteId = getNoteId(noteMeta, filePath);
|
||||
|
||||
if (attachmentMeta) {
|
||||
const noteAttachment = new BNoteAttachment({
|
||||
noteId,
|
||||
name: attachmentMeta.name,
|
||||
mime: attachmentMeta.mime
|
||||
});
|
||||
|
||||
noteAttachment.setContent(content);
|
||||
return;
|
||||
}
|
||||
|
||||
const parentNoteId = getParentNoteId(filePath, parentNoteMeta);
|
||||
|
||||
if (!parentNoteId) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user