mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	downloading note revisions
This commit is contained in:
		| @@ -6,6 +6,7 @@ const $dialog = $("#note-revisions-dialog"); | |||||||
| const $list = $("#note-revision-list"); | const $list = $("#note-revision-list"); | ||||||
| const $content = $("#note-revision-content"); | const $content = $("#note-revision-content"); | ||||||
| const $title = $("#note-revision-title"); | const $title = $("#note-revision-title"); | ||||||
|  | const $titleButtons = $("#note-revision-title-buttons"); | ||||||
|  |  | ||||||
| let revisionItems = []; | let revisionItems = []; | ||||||
| let note; | let note; | ||||||
| @@ -53,6 +54,14 @@ $list.on('change', async () => { | |||||||
|  |  | ||||||
|     $title.html(revisionItem.title); |     $title.html(revisionItem.title); | ||||||
|  |  | ||||||
|  |     const $downloadButton = $('<button class="btn btn-sm btn-primary" type="button">Download</button>'); | ||||||
|  |  | ||||||
|  |     $downloadButton.on('click', () => { | ||||||
|  |         utils.download(utils.getHost() + `/api/notes/${revisionItem.noteId}/revisions/${revisionItem.noteRevisionId}/download`); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     $titleButtons.html($downloadButton); | ||||||
|  |  | ||||||
|     const fullNoteRevision = await server.get(`notes/${revisionItem.noteId}/revisions/${revisionItem.noteRevisionId}`); |     const fullNoteRevision = await server.get(`notes/${revisionItem.noteId}/revisions/${revisionItem.noteRevisionId}`); | ||||||
|  |  | ||||||
|     if (note.type === 'text') { |     if (note.type === 'text') { | ||||||
| @@ -63,6 +72,8 @@ $list.on('change', async () => { | |||||||
|     } |     } | ||||||
|     else if (note.type === 'image') { |     else if (note.type === 'image') { | ||||||
|         $content.html($("<img>") |         $content.html($("<img>") | ||||||
|  |             // reason why we put this inline as base64 is that we do not want to let user to copy this | ||||||
|  |             // as a URL to be used in a note. Instead if they copy and paste it into a note, it will be a uploaded as a new note | ||||||
|             .attr("src", `data:${note.mime};base64,` + fullNoteRevision.content) |             .attr("src", `data:${note.mime};base64,` + fullNoteRevision.content) | ||||||
|             .css("width", "100%")); |             .css("width", "100%")); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -47,7 +47,7 @@ class NoteRevisionsWidget extends StandardWidget { | |||||||
|             }).text(item.dateLastEdited.substr(0, 16))); |             }).text(item.dateLastEdited.substr(0, 16))); | ||||||
|  |  | ||||||
|             if (item.contentLength !== null) { |             if (item.contentLength !== null) { | ||||||
|                 $listItem.append($("<span>").text(` (${item.contentLength} characters)`)) |                 $listItem.append($("<span>").text(` (${item.contentLength} bytes)`)) | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             $list.append($listItem); |             $list.append($listItem); | ||||||
|   | |||||||
| @@ -45,10 +45,9 @@ async function downloadNoteFile(noteId, res) { | |||||||
|         return res.status(401).send("Protected session not available"); |         return res.status(401).send("Protected session not available"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const originalFileName = await note.getLabel('originalFileName'); |     // (one) reason we're not using the originFileName (available as label) is that it's not | ||||||
|     const fileName = originalFileName ? originalFileName.value : note.title; |     // available for older note revisions and thus would be inconsistent | ||||||
|  |     res.setHeader('Content-Disposition', utils.getContentDisposition(note.title || "untitled")); | ||||||
|     res.setHeader('Content-Disposition', utils.getContentDisposition(fileName)); |  | ||||||
|     res.setHeader('Content-Type', note.mime); |     res.setHeader('Content-Type', note.mime); | ||||||
|  |  | ||||||
|     res.send(await note.getContent()); |     res.send(await note.getContent()); | ||||||
|   | |||||||
| @@ -2,6 +2,9 @@ | |||||||
|  |  | ||||||
| const repository = require('../../services/repository'); | const repository = require('../../services/repository'); | ||||||
| const noteCacheService = require('../../services/note_cache'); | const noteCacheService = require('../../services/note_cache'); | ||||||
|  | const protectedSessionService = require('../../services/protected_session'); | ||||||
|  | const utils = require('../../services/utils'); | ||||||
|  | const path = require('path'); | ||||||
|  |  | ||||||
| async function getNoteRevisions(req) { | async function getNoteRevisions(req) { | ||||||
|     const {noteId} = req.params; |     const {noteId} = req.params; | ||||||
| @@ -16,15 +19,66 @@ async function getNoteRevisions(req) { | |||||||
| async function getNoteRevision(req) { | async function getNoteRevision(req) { | ||||||
|     const noteRevision = await repository.getNoteRevision(req.params.noteRevisionId); |     const noteRevision = await repository.getNoteRevision(req.params.noteRevisionId); | ||||||
|  |  | ||||||
|     await noteRevision.getContent(); |     if (noteRevision.type !== 'file') { | ||||||
|  |         await noteRevision.getContent(); | ||||||
|  |  | ||||||
|     if (noteRevision.content && ['file', 'image'].includes(noteRevision.type)) { |         if (noteRevision.content && noteRevision.type === 'image') { | ||||||
|         noteRevision.content = noteRevision.content.toString('base64'); |             noteRevision.content = noteRevision.content.toString('base64'); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return noteRevision; |     return noteRevision; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @param {NoteRevision} noteRevision | ||||||
|  |  * @return {string} | ||||||
|  |  */ | ||||||
|  | function getRevisionFilename(noteRevision) { | ||||||
|  |     let filename = noteRevision.title || "untitled"; | ||||||
|  |  | ||||||
|  |     if (noteRevision.type === 'text') { | ||||||
|  |         filename += '.html'; | ||||||
|  |     } else if (['relation-map', 'search'].includes(noteRevision.type)) { | ||||||
|  |         filename += '.json'; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const extension = path.extname(filename); | ||||||
|  |     const date = noteRevision.dateCreated | ||||||
|  |         .substr(0, 19) | ||||||
|  |         .replace(' ', '_') | ||||||
|  |         .replace(/[^0-9_]/g, ''); | ||||||
|  |  | ||||||
|  |     if (extension) { | ||||||
|  |         filename = filename.substr(0, filename.length - extension.length) | ||||||
|  |             + '-' + date + extension; | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         filename += '-' + date; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return filename; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async function downloadNoteRevision(req, res) { | ||||||
|  |     const noteRevision = await repository.getNoteRevision(req.params.noteRevisionId); | ||||||
|  |  | ||||||
|  |     if (noteRevision.noteId !== req.params.noteId) { | ||||||
|  |         return res.status(400).send(`Note revision ${req.params.noteRevisionId} does not belong to note ${req.params.noteId}`); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (noteRevision.isProtected && !protectedSessionService.isProtectedSessionAvailable()) { | ||||||
|  |         return res.status(401).send("Protected session not available"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const filename = getRevisionFilename(noteRevision); | ||||||
|  |  | ||||||
|  |     res.setHeader('Content-Disposition', utils.getContentDisposition(filename)); | ||||||
|  |     res.setHeader('Content-Type', noteRevision.mime); | ||||||
|  |  | ||||||
|  |     res.send(await noteRevision.getContent()); | ||||||
|  | } | ||||||
|  |  | ||||||
| async function getEditedNotesOnDate(req) { | async function getEditedNotesOnDate(req) { | ||||||
|     const date = req.params.date; |     const date = req.params.date; | ||||||
|  |  | ||||||
| @@ -48,5 +102,6 @@ async function getEditedNotesOnDate(req) { | |||||||
| module.exports = { | module.exports = { | ||||||
|     getNoteRevisions, |     getNoteRevisions, | ||||||
|     getNoteRevision, |     getNoteRevision, | ||||||
|  |     downloadNoteRevision, | ||||||
|     getEditedNotesOnDate |     getEditedNotesOnDate | ||||||
| }; | }; | ||||||
| @@ -133,6 +133,7 @@ function register(app) { | |||||||
|     apiRoute(PUT, /\/api\/notes\/(.*)\/type\/(.*)\/mime\/(.*)/, notesApiRoute.setNoteTypeMime); |     apiRoute(PUT, /\/api\/notes\/(.*)\/type\/(.*)\/mime\/(.*)/, notesApiRoute.setNoteTypeMime); | ||||||
|     apiRoute(GET, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.getNoteRevisions); |     apiRoute(GET, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.getNoteRevisions); | ||||||
|     apiRoute(GET, '/api/notes/:noteId/revisions/:noteRevisionId', noteRevisionsApiRoute.getNoteRevision); |     apiRoute(GET, '/api/notes/:noteId/revisions/:noteRevisionId', noteRevisionsApiRoute.getNoteRevision); | ||||||
|  |     route(GET, '/api/notes/:noteId/revisions/:noteRevisionId/download', [auth.checkApiAuthOrElectron], noteRevisionsApiRoute.downloadNoteRevision); | ||||||
|     apiRoute(POST, '/api/notes/relation-map', notesApiRoute.getRelationMap); |     apiRoute(POST, '/api/notes/relation-map', notesApiRoute.getRelationMap); | ||||||
|     apiRoute(PUT, '/api/notes/:noteId/change-title', notesApiRoute.changeTitle); |     apiRoute(PUT, '/api/notes/:noteId/change-title', notesApiRoute.changeTitle); | ||||||
|     apiRoute(POST, '/api/notes/:noteId/duplicate/:parentNoteId', notesApiRoute.duplicateNote); |     apiRoute(POST, '/api/notes/:noteId/duplicate/:parentNoteId', notesApiRoute.duplicateNote); | ||||||
|   | |||||||
| @@ -1,10 +1,10 @@ | |||||||
| <div class="note-detail-image note-detail-component"> | <div class="note-detail-image note-detail-component"> | ||||||
|     <div style="display: flex; justify-content: space-evenly; margin: 10px;"> |     <div style="display: flex; justify-content: space-evenly; margin: 10px;"> | ||||||
|         <button class="image-download btn btn-primary" type="button">Download</button> |         <button class="image-download btn btn-sm btn-primary" type="button">Download</button> | ||||||
|  |  | ||||||
|         <button class="image-copy-to-clipboard btn btn-primary" type="button">Copy to clipboard</button> |         <button class="image-copy-to-clipboard btn btn-sm btn-primary" type="button">Copy to clipboard</button> | ||||||
|  |  | ||||||
|         <button class="image-upload-new-revision btn btn-primary" type="button">Upload new revision</button> |         <button class="image-upload-new-revision btn btn-sm btn-primary" type="button">Upload new revision</button> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|     <div class="note-detail-image-wrapper"> |     <div class="note-detail-image-wrapper"> | ||||||
|   | |||||||
| @@ -15,8 +15,10 @@ | |||||||
|                 </select> |                 </select> | ||||||
|  |  | ||||||
|                 <div id="note-revision-content-wrapper" style="flex-grow: 1; margin-left: 20px;"> |                 <div id="note-revision-content-wrapper" style="flex-grow: 1; margin-left: 20px;"> | ||||||
|                     <div style="display: flex"> |                     <div style="display: flex; justify-content: space-between;"> | ||||||
|                         <h3 id="note-revision-title" style="margin: 3px; flex-grow: 100;"></h3> |                         <h3 id="note-revision-title" style="margin: 3px; flex-grow: 100;"></h3> | ||||||
|  |  | ||||||
|  |                         <div id="note-revision-title-buttons"></div> | ||||||
|                     </div> |                     </div> | ||||||
|  |  | ||||||
|                     <div id="note-revision-content" style="height: 600px; overflow: auto;"></div> |                     <div id="note-revision-content" style="height: 600px; overflow: auto;"></div> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user