mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-04 05:28:59 +01:00 
			
		
		
		
	added links to the import/export + fixing internal links inside note content
This commit is contained in:
		
							parent
							
								
									46c7901e1f
								
							
						
					
					
						commit
						e0f9a7fc6a
					
				@ -115,8 +115,10 @@ async function exportToTar(branch, res) {
 | 
				
			|||||||
            noteId: note.noteId,
 | 
					            noteId: note.noteId,
 | 
				
			||||||
            title: note.title,
 | 
					            title: note.title,
 | 
				
			||||||
            prefix: branch.prefix,
 | 
					            prefix: branch.prefix,
 | 
				
			||||||
 | 
					            isExpanded: branch.isExpanded,
 | 
				
			||||||
            type: note.type,
 | 
					            type: note.type,
 | 
				
			||||||
            mime: note.mime,
 | 
					            mime: note.mime,
 | 
				
			||||||
 | 
					            // we don't export dateCreated and dateModified of any entity since that would be a bit misleading
 | 
				
			||||||
            attributes: (await note.getOwnedAttributes()).map(attribute => {
 | 
					            attributes: (await note.getOwnedAttributes()).map(attribute => {
 | 
				
			||||||
                return {
 | 
					                return {
 | 
				
			||||||
                    type: attribute.type,
 | 
					                    type: attribute.type,
 | 
				
			||||||
@ -125,6 +127,12 @@ async function exportToTar(branch, res) {
 | 
				
			|||||||
                    isInheritable: attribute.isInheritable,
 | 
					                    isInheritable: attribute.isInheritable,
 | 
				
			||||||
                    position: attribute.position
 | 
					                    position: attribute.position
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					            links: (await note.getLinks()).map(link => {
 | 
				
			||||||
 | 
					                return {
 | 
				
			||||||
 | 
					                    type: link.type,
 | 
				
			||||||
 | 
					                    targetNoteId: link.targetNoteId
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,11 @@
 | 
				
			|||||||
"use strict";
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Attribute = require('../../entities/attribute');
 | 
				
			||||||
 | 
					const Link = require('../../entities/link');
 | 
				
			||||||
const repository = require('../../services/repository');
 | 
					const repository = require('../../services/repository');
 | 
				
			||||||
const log = require('../../services/log');
 | 
					const log = require('../../services/log');
 | 
				
			||||||
const enex = require('../../services/enex');
 | 
					const utils = require('../../services/utils');
 | 
				
			||||||
const attributeService = require('../../services/attributes');
 | 
					const enex = require('../../services/import/enex');
 | 
				
			||||||
const noteService = require('../../services/notes');
 | 
					const noteService = require('../../services/notes');
 | 
				
			||||||
const Branch = require('../../entities/branch');
 | 
					const Branch = require('../../entities/branch');
 | 
				
			||||||
const tar = require('tar-stream');
 | 
					const tar = require('tar-stream');
 | 
				
			||||||
@ -93,33 +95,64 @@ async function importOpml(file, parentNote) {
 | 
				
			|||||||
    return returnNote;
 | 
					    return returnNote;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Complication of this export is the need to balance two needs:
 | 
				
			||||||
 | 
					 * -
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
async function importTar(file, parentNote) {
 | 
					async function importTar(file, parentNote) {
 | 
				
			||||||
    const files = await parseImportFile(file);
 | 
					    const files = await parseImportFile(file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const ctx = {
 | 
					    const ctx = {
 | 
				
			||||||
        // maps from original noteId (in tar file) to newly generated noteId
 | 
					        // maps from original noteId (in tar file) to newly generated noteId
 | 
				
			||||||
        noteIdMap: {},
 | 
					        noteIdMap: {},
 | 
				
			||||||
 | 
					        // new noteIds of notes which were actually created (not just referenced)
 | 
				
			||||||
 | 
					        createdNoteIds: [],
 | 
				
			||||||
        attributes: [],
 | 
					        attributes: [],
 | 
				
			||||||
 | 
					        links: [],
 | 
				
			||||||
        reader: new commonmark.Parser(),
 | 
					        reader: new commonmark.Parser(),
 | 
				
			||||||
        writer: new commonmark.HtmlRenderer()
 | 
					        writer: new commonmark.HtmlRenderer()
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ctx.getNewNoteId = function(origNoteId) {
 | 
				
			||||||
 | 
					        // in case the original noteId is empty. This probably shouldn't happen, but still good to have this precaution
 | 
				
			||||||
 | 
					        if (!origNoteId.trim()) {
 | 
				
			||||||
 | 
					            return "";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!ctx.noteIdMap[origNoteId]) {
 | 
				
			||||||
 | 
					            ctx.noteIdMap[origNoteId] = utils.newEntityId();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return ctx.noteIdMap[origNoteId];
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const note = await importNotes(ctx, files, parentNote.noteId);
 | 
					    const note = await importNotes(ctx, files, parentNote.noteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // we save attributes after importing notes because we need to have all the relation
 | 
					    // we save attributes and links after importing notes because we need to check that target noteIds
 | 
				
			||||||
    // targets already existing
 | 
					    // have been really created (relation/links with targets outside of the export are not created)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (const attr of ctx.attributes) {
 | 
					    for (const attr of ctx.attributes) {
 | 
				
			||||||
        if (attr.type === 'relation') {
 | 
					        if (attr.type === 'relation') {
 | 
				
			||||||
            // map to local noteId
 | 
					            attr.value = ctx.getNewNoteId(attr.value);
 | 
				
			||||||
            attr.value = ctx.noteIdMap[attr.value];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!attr.value) {
 | 
					            if (!ctx.createdNoteIds.includes(attr.value)) {
 | 
				
			||||||
                // relation is targeting note not present in the import
 | 
					                // relation targets note outside of the export
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await attributeService.createAttribute(attr);
 | 
					        await new Attribute(attr).save();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const link of ctx.links) {
 | 
				
			||||||
 | 
					        link.targetNoteId = ctx.getNewNoteId(link.targetNoteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!ctx.createdNoteIds.includes(link.targetNoteId)) {
 | 
				
			||||||
 | 
					            // link targets note outside of the export
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await new Link(link).save();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return note;
 | 
					    return note;
 | 
				
			||||||
@ -252,26 +285,35 @@ async function importNotes(ctx, files, parentNoteId) {
 | 
				
			|||||||
            if (file.meta.clone) {
 | 
					            if (file.meta.clone) {
 | 
				
			||||||
                await new Branch({
 | 
					                await new Branch({
 | 
				
			||||||
                    parentNoteId: parentNoteId,
 | 
					                    parentNoteId: parentNoteId,
 | 
				
			||||||
                    noteId: ctx.noteIdMap[file.meta.noteId],
 | 
					                    noteId: ctx.getNewNoteId(file.meta.noteId),
 | 
				
			||||||
                    prefix: file.meta.prefix
 | 
					                    prefix: file.meta.prefix,
 | 
				
			||||||
 | 
					                    isExpanded: !!file.meta.isExpanded
 | 
				
			||||||
                }).save();
 | 
					                }).save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (file.meta.type !== 'file') {
 | 
					            if (file.meta.type !== 'file' && file.meta.type !== 'image') {
 | 
				
			||||||
                file.data = file.data.toString("UTF-8");
 | 
					                file.data = file.data.toString("UTF-8");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // this will replace all internal links (<a> and <img>) inside the body
 | 
				
			||||||
 | 
					                // links pointing outside the export will be broken and changed (ctx.getNewNoteId() will still assign new noteId)
 | 
				
			||||||
 | 
					                for (const link of file.meta.links || []) {
 | 
				
			||||||
 | 
					                    // no need to escape the regexp find string since it's a noteId which doesn't contain any special characters
 | 
				
			||||||
 | 
					                    file.data = file.data.replace(new RegExp(link.targetNoteId, "g"), ctx.getNewNoteId(link.targetNoteId));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            note = (await noteService.createNote(parentNoteId, file.meta.title, file.data, {
 | 
					            note = (await noteService.createNote(parentNoteId, file.meta.title, file.data, {
 | 
				
			||||||
 | 
					                noteId: ctx.getNewNoteId(file.meta.noteId),
 | 
				
			||||||
                type: file.meta.type,
 | 
					                type: file.meta.type,
 | 
				
			||||||
                mime: file.meta.mime,
 | 
					                mime: file.meta.mime,
 | 
				
			||||||
                prefix: file.meta.prefix
 | 
					                prefix: file.meta.prefix
 | 
				
			||||||
            })).note;
 | 
					            })).note;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ctx.noteIdMap[file.meta.noteId] = note.noteId;
 | 
					            ctx.createdNoteIds.push(note.noteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for (const attribute of file.meta.attributes) {
 | 
					            for (const attribute of file.meta.attributes || []) {
 | 
				
			||||||
                ctx.attributes.push({
 | 
					                ctx.attributes.push({
 | 
				
			||||||
                    noteId: note.noteId,
 | 
					                    noteId: note.noteId,
 | 
				
			||||||
                    type: attribute.type,
 | 
					                    type: attribute.type,
 | 
				
			||||||
@ -281,6 +323,14 @@ async function importNotes(ctx, files, parentNoteId) {
 | 
				
			|||||||
                    position: attribute.position
 | 
					                    position: attribute.position
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (const link of file.meta.links || []) {
 | 
				
			||||||
 | 
					                ctx.links.push({
 | 
				
			||||||
 | 
					                    noteId: note.noteId,
 | 
				
			||||||
 | 
					                    type: link.type,
 | 
				
			||||||
 | 
					                    targetNoteId: link.targetNoteId
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // first created note will be activated after import
 | 
					        // first created note will be activated after import
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,10 @@
 | 
				
			|||||||
const sax = require("sax");
 | 
					const sax = require("sax");
 | 
				
			||||||
const stream = require('stream');
 | 
					const stream = require('stream');
 | 
				
			||||||
const xml2js = require('xml2js');
 | 
					const xml2js = require('xml2js');
 | 
				
			||||||
const log = require("./log");
 | 
					const log = require("../log");
 | 
				
			||||||
const utils = require("./utils");
 | 
					const utils = require("../utils");
 | 
				
			||||||
const noteService = require("./notes");
 | 
					const noteService = require("../notes");
 | 
				
			||||||
const imageService = require("./image");
 | 
					const imageService = require("../image");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// date format is e.g. 20181121T193703Z
 | 
					// date format is e.g. 20181121T193703Z
 | 
				
			||||||
function parseDate(text) {
 | 
					function parseDate(text) {
 | 
				
			||||||
@ -69,6 +69,7 @@ async function createNewNote(parentNoteId, noteData) {
 | 
				
			|||||||
    noteData.mime = noteData.mime || parentNote.mime;
 | 
					    noteData.mime = noteData.mime || parentNote.mime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const note = await new Note({
 | 
					    const note = await new Note({
 | 
				
			||||||
 | 
					        noteId: noteData.noteId, // optionally can force specific noteId
 | 
				
			||||||
        title: noteData.title,
 | 
					        title: noteData.title,
 | 
				
			||||||
        content: noteData.content,
 | 
					        content: noteData.content,
 | 
				
			||||||
        isProtected: noteData.isProtected,
 | 
					        isProtected: noteData.isProtected,
 | 
				
			||||||
@ -116,6 +117,7 @@ async function createNote(parentNoteId, title, content = "", extraOptions = {})
 | 
				
			|||||||
        title: title,
 | 
					        title: title,
 | 
				
			||||||
        content: extraOptions.json ? JSON.stringify(content, null, '\t') : content,
 | 
					        content: extraOptions.json ? JSON.stringify(content, null, '\t') : content,
 | 
				
			||||||
        target: 'into',
 | 
					        target: 'into',
 | 
				
			||||||
 | 
					        noteId: extraOptions.noteId,
 | 
				
			||||||
        isProtected: !!extraOptions.isProtected,
 | 
					        isProtected: !!extraOptions.isProtected,
 | 
				
			||||||
        type: extraOptions.type,
 | 
					        type: extraOptions.type,
 | 
				
			||||||
        mime: extraOptions.mime,
 | 
					        mime: extraOptions.mime,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user