| 
									
										
										
										
											2018-11-16 12:12:04 +01:00
										 |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const html = require('html'); | 
					
						
							| 
									
										
										
										
											2018-11-24 20:58:38 +01:00
										 |  |  | const tar = require('tar-stream'); | 
					
						
							| 
									
										
										
										
											2018-11-25 15:17:28 +01:00
										 |  |  | const path = require('path'); | 
					
						
							| 
									
										
										
										
											2018-11-16 12:12:04 +01:00
										 |  |  | const sanitize = require("sanitize-filename"); | 
					
						
							| 
									
										
										
										
											2018-11-24 14:44:56 +01:00
										 |  |  | const mimeTypes = require('mime-types'); | 
					
						
							|  |  |  | const TurndownService = require('turndown'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @param format - 'html' or 'markdown' | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | async function exportToTar(branch, format, res) { | 
					
						
							|  |  |  |     const turndownService = new TurndownService(); | 
					
						
							| 
									
										
										
										
											2018-11-16 12:12:04 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-25 10:26:45 +01:00
										 |  |  |     // path -> number of occurences
 | 
					
						
							|  |  |  |     const existingPaths = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-24 20:58:38 +01:00
										 |  |  |     const pack = tar.pack(); | 
					
						
							| 
									
										
										
										
											2018-11-16 12:12:04 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     const exportedNoteIds = []; | 
					
						
							|  |  |  |     const name = await exportNoteInner(branch, ''); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-25 10:26:45 +01:00
										 |  |  |     function getUniqueFilename(fileName) { | 
					
						
							| 
									
										
										
										
											2018-11-25 15:17:28 +01:00
										 |  |  |         const lcFileName = fileName.toLowerCase(); | 
					
						
							| 
									
										
										
										
											2018-11-25 10:26:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-25 15:17:28 +01:00
										 |  |  |         if (lcFileName in existingPaths) { | 
					
						
							|  |  |  |             let index; | 
					
						
							|  |  |  |             let newName; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             do { | 
					
						
							|  |  |  |                 index = existingPaths[lcFileName]++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 newName = lcFileName + "_" + index; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             while (newName in existingPaths); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return fileName + "_" + index; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |             existingPaths[lcFileName] = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return fileName; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-11-25 10:26:45 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async function exportNoteInner(branch, directory, existingNames) { | 
					
						
							| 
									
										
										
										
											2018-11-16 12:12:04 +01:00
										 |  |  |         const note = await branch.getNote(); | 
					
						
							| 
									
										
										
										
											2018-11-25 15:17:28 +01:00
										 |  |  |         const baseFileName = getUniqueFilename(directory + sanitize(note.title)); | 
					
						
							| 
									
										
										
										
											2018-11-16 12:12:04 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (exportedNoteIds.includes(note.noteId)) { | 
					
						
							| 
									
										
										
										
											2018-11-25 10:26:45 +01:00
										 |  |  |             saveMetadataFile(baseFileName, { | 
					
						
							| 
									
										
										
										
											2018-11-16 12:12:04 +01:00
										 |  |  |                 version: 1, | 
					
						
							|  |  |  |                 clone: true, | 
					
						
							|  |  |  |                 noteId: note.noteId, | 
					
						
							|  |  |  |                 prefix: branch.prefix | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const metadata = { | 
					
						
							|  |  |  |             version: 1, | 
					
						
							|  |  |  |             clone: false, | 
					
						
							|  |  |  |             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, | 
					
						
							|  |  |  |                     name: attribute.name, | 
					
						
							|  |  |  |                     value: attribute.value, | 
					
						
							|  |  |  |                     isInheritable: attribute.isInheritable, | 
					
						
							|  |  |  |                     position: attribute.position | 
					
						
							|  |  |  |                 }; | 
					
						
							|  |  |  |             }), | 
					
						
							|  |  |  |             links: (await note.getLinks()).map(link => { | 
					
						
							|  |  |  |                 return { | 
					
						
							|  |  |  |                     type: link.type, | 
					
						
							|  |  |  |                     targetNoteId: link.targetNoteId | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-24 14:44:56 +01:00
										 |  |  |         if (note.type === 'text') { | 
					
						
							|  |  |  |             metadata.format = format; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-16 12:12:04 +01:00
										 |  |  |         if (await note.hasLabel('excludeFromExport')) { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-25 15:17:28 +01:00
										 |  |  |         metadata.dataFilename = saveDataFile(baseFileName, note); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-25 10:26:45 +01:00
										 |  |  |         saveMetadataFile(baseFileName, metadata); | 
					
						
							| 
									
										
										
										
											2018-11-16 12:12:04 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         exportedNoteIds.push(note.noteId); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const childBranches = await note.getChildBranches(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (childBranches.length > 0) { | 
					
						
							| 
									
										
										
										
											2018-11-25 10:26:45 +01:00
										 |  |  |             saveDirectory(baseFileName); | 
					
						
							| 
									
										
										
										
											2018-11-16 12:12:04 +01:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (const childBranch of childBranches) { | 
					
						
							| 
									
										
										
										
											2018-11-25 10:26:45 +01:00
										 |  |  |             await exportNoteInner(childBranch, baseFileName + "/"); | 
					
						
							| 
									
										
										
										
											2018-11-16 12:12:04 +01:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-25 10:26:45 +01:00
										 |  |  |         return baseFileName; | 
					
						
							| 
									
										
										
										
											2018-11-16 12:12:04 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-25 15:17:28 +01:00
										 |  |  |     function saveDataFile(baseFilename, note) { | 
					
						
							| 
									
										
										
										
											2018-11-24 14:44:56 +01:00
										 |  |  |         let content = note.content; | 
					
						
							| 
									
										
										
										
											2018-11-25 10:26:45 +01:00
										 |  |  |         let extension; | 
					
						
							| 
									
										
										
										
											2018-11-24 14:44:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (note.type === 'text') { | 
					
						
							|  |  |  |             if (format === 'html') { | 
					
						
							|  |  |  |                 content = html.prettyPrint(note.content, {indent_size: 2}); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else if (format === 'markdown') { | 
					
						
							|  |  |  |                 content = turndownService.turndown(note.content); | 
					
						
							| 
									
										
										
										
											2018-11-25 10:26:45 +01:00
										 |  |  |                 extension = 'md'; | 
					
						
							| 
									
										
										
										
											2018-11-24 14:44:56 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |             else { | 
					
						
							|  |  |  |                 throw new Error("Unknown format: " + format); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-25 10:26:45 +01:00
										 |  |  |         if (!extension) { | 
					
						
							|  |  |  |             extension = mimeTypes.extension(note.mime) | 
					
						
							|  |  |  |                 || getExceptionalExtension(note.mime) | 
					
						
							|  |  |  |                 || "dat"; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-11-16 12:12:04 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-25 15:17:28 +01:00
										 |  |  |         let filename = baseFilename; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!filename.toLowerCase().endsWith(extension)) { | 
					
						
							|  |  |  |             filename += "." + extension; | 
					
						
							| 
									
										
										
										
											2018-11-24 14:44:56 +01:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-25 15:17:28 +01:00
										 |  |  |         filename = getUniqueFilename(filename); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         pack.entry({name: filename, size: content.length}, content); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return path.basename(filename); | 
					
						
							| 
									
										
										
										
											2018-11-24 14:44:56 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function getExceptionalExtension(mime) { | 
					
						
							|  |  |  |         if (mime === 'application/x-javascript') { | 
					
						
							|  |  |  |             return 'js'; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-11-16 12:12:04 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-25 10:26:45 +01:00
										 |  |  |     function saveMetadataFile(baseFileName, metadata) { | 
					
						
							| 
									
										
										
										
											2018-11-16 12:12:04 +01:00
										 |  |  |         const metadataJson = JSON.stringify(metadata, null, '\t'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-25 15:17:28 +01:00
										 |  |  |         const fileName = getUniqueFilename(baseFileName + ".meta"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         pack.entry({name: fileName, size: metadataJson.length}, metadataJson); | 
					
						
							| 
									
										
										
										
											2018-11-16 12:12:04 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-25 10:26:45 +01:00
										 |  |  |     function saveDirectory(baseFileName) { | 
					
						
							|  |  |  |         pack.entry({name: baseFileName, type: 'directory'}); | 
					
						
							| 
									
										
										
										
											2018-11-16 12:12:04 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pack.finalize(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     res.setHeader('Content-Disposition', 'file; filename="' + name + '.tar"'); | 
					
						
							|  |  |  |     res.setHeader('Content-Type', 'application/tar'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pack.pipe(res); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = { | 
					
						
							|  |  |  |     exportToTar | 
					
						
							|  |  |  | }; |