mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-10-31 04:51:31 +08: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
	 azivner
						azivner