mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
downloading note revisions
This commit is contained in:
parent
58a857cf79
commit
5aa5ec3af1
@ -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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user