mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	added links to the import/export + fixing internal links inside note content
This commit is contained in:
		| @@ -115,8 +115,10 @@ async function exportToTar(branch, res) { | ||||
|             noteId: note.noteId, | ||||
|             title: note.title, | ||||
|             prefix: branch.prefix, | ||||
|             isExpanded: branch.isExpanded, | ||||
|             type: note.type, | ||||
|             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 => { | ||||
|                 return { | ||||
|                     type: attribute.type, | ||||
| @@ -125,6 +127,12 @@ async function exportToTar(branch, res) { | ||||
|                     isInheritable: attribute.isInheritable, | ||||
|                     position: attribute.position | ||||
|                 }; | ||||
|             }), | ||||
|             links: (await note.getLinks()).map(link => { | ||||
|                 return { | ||||
|                     type: link.type, | ||||
|                     targetNoteId: link.targetNoteId | ||||
|                 } | ||||
|             }) | ||||
|         }; | ||||
|  | ||||
|   | ||||
| @@ -1,9 +1,11 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const Attribute = require('../../entities/attribute'); | ||||
| const Link = require('../../entities/link'); | ||||
| const repository = require('../../services/repository'); | ||||
| const log = require('../../services/log'); | ||||
| const enex = require('../../services/enex'); | ||||
| const attributeService = require('../../services/attributes'); | ||||
| const utils = require('../../services/utils'); | ||||
| const enex = require('../../services/import/enex'); | ||||
| const noteService = require('../../services/notes'); | ||||
| const Branch = require('../../entities/branch'); | ||||
| const tar = require('tar-stream'); | ||||
| @@ -93,33 +95,64 @@ async function importOpml(file, parentNote) { | ||||
|     return returnNote; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Complication of this export is the need to balance two needs: | ||||
|  * - | ||||
|  */ | ||||
| async function importTar(file, parentNote) { | ||||
|     const files = await parseImportFile(file); | ||||
|  | ||||
|     const ctx = { | ||||
|         // maps from original noteId (in tar file) to newly generated noteId | ||||
|         noteIdMap: {}, | ||||
|         // new noteIds of notes which were actually created (not just referenced) | ||||
|         createdNoteIds: [], | ||||
|         attributes: [], | ||||
|         links: [], | ||||
|         reader: new commonmark.Parser(), | ||||
|         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); | ||||
|  | ||||
|     // we save attributes after importing notes because we need to have all the relation | ||||
|     // targets already existing | ||||
|     // we save attributes and links after importing notes because we need to check that target noteIds | ||||
|     // have been really created (relation/links with targets outside of the export are not created) | ||||
|  | ||||
|     for (const attr of ctx.attributes) { | ||||
|         if (attr.type === 'relation') { | ||||
|             // map to local noteId | ||||
|             attr.value = ctx.noteIdMap[attr.value]; | ||||
|             attr.value = ctx.getNewNoteId(attr.value); | ||||
|  | ||||
|             if (!attr.value) { | ||||
|                 // relation is targeting note not present in the import | ||||
|             if (!ctx.createdNoteIds.includes(attr.value)) { | ||||
|                 // relation targets note outside of the export | ||||
|                 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; | ||||
| @@ -252,26 +285,35 @@ async function importNotes(ctx, files, parentNoteId) { | ||||
|             if (file.meta.clone) { | ||||
|                 await new Branch({ | ||||
|                     parentNoteId: parentNoteId, | ||||
|                     noteId: ctx.noteIdMap[file.meta.noteId], | ||||
|                     prefix: file.meta.prefix | ||||
|                     noteId: ctx.getNewNoteId(file.meta.noteId), | ||||
|                     prefix: file.meta.prefix, | ||||
|                     isExpanded: !!file.meta.isExpanded | ||||
|                 }).save(); | ||||
|  | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             if (file.meta.type !== 'file') { | ||||
|             if (file.meta.type !== 'file' && file.meta.type !== 'image') { | ||||
|                 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, { | ||||
|                 noteId: ctx.getNewNoteId(file.meta.noteId), | ||||
|                 type: file.meta.type, | ||||
|                 mime: file.meta.mime, | ||||
|                 prefix: file.meta.prefix | ||||
|             })).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({ | ||||
|                     noteId: note.noteId, | ||||
|                     type: attribute.type, | ||||
| @@ -281,6 +323,14 @@ async function importNotes(ctx, files, parentNoteId) { | ||||
|                     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 | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| const sax = require("sax"); | ||||
| const stream = require('stream'); | ||||
| const xml2js = require('xml2js'); | ||||
| const log = require("./log"); | ||||
| const utils = require("./utils"); | ||||
| const noteService = require("./notes"); | ||||
| const imageService = require("./image"); | ||||
| const log = require("../log"); | ||||
| const utils = require("../utils"); | ||||
| const noteService = require("../notes"); | ||||
| const imageService = require("../image"); | ||||
| 
 | ||||
| // date format is e.g. 20181121T193703Z
 | ||||
| function parseDate(text) { | ||||
| @@ -69,6 +69,7 @@ async function createNewNote(parentNoteId, noteData) { | ||||
|     noteData.mime = noteData.mime || parentNote.mime; | ||||
|  | ||||
|     const note = await new Note({ | ||||
|         noteId: noteData.noteId, // optionally can force specific noteId | ||||
|         title: noteData.title, | ||||
|         content: noteData.content, | ||||
|         isProtected: noteData.isProtected, | ||||
| @@ -116,6 +117,7 @@ async function createNote(parentNoteId, title, content = "", extraOptions = {}) | ||||
|         title: title, | ||||
|         content: extraOptions.json ? JSON.stringify(content, null, '\t') : content, | ||||
|         target: 'into', | ||||
|         noteId: extraOptions.noteId, | ||||
|         isProtected: !!extraOptions.isProtected, | ||||
|         type: extraOptions.type, | ||||
|         mime: extraOptions.mime, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user