From 60ac1a04f4222eaf73fc35f00aea69ad76c92153 Mon Sep 17 00:00:00 2001 From: zadam Date: Sat, 9 Nov 2019 11:58:52 +0100 Subject: [PATCH] uploading new file revisions --- .../javascripts/dialogs/note_revisions.js | 13 +++++ .../javascripts/services/note_detail_file.js | 36 +++++++++++++- .../javascripts/services/note_detail_image.js | 4 +- src/routes/api/{file_upload.js => files.js} | 34 +++++++------- src/routes/custom.js | 2 +- src/routes/routes.js | 6 +-- src/services/image.js | 20 ++------ src/services/note_revisions.js | 47 +++++++++++++++++++ src/services/notes.js | 18 ++----- src/views/details/file.ejs | 8 +++- src/views/details/image.ejs | 4 +- 11 files changed, 132 insertions(+), 60 deletions(-) rename src/routes/api/{file_upload.js => files.js} (60%) create mode 100644 src/services/note_revisions.js diff --git a/src/public/javascripts/dialogs/note_revisions.js b/src/public/javascripts/dialogs/note_revisions.js index fcb646521..4af7e2d7f 100644 --- a/src/public/javascripts/dialogs/note_revisions.js +++ b/src/public/javascripts/dialogs/note_revisions.js @@ -77,6 +77,19 @@ $list.on('change', async () => { .attr("src", `data:${note.mime};base64,` + fullNoteRevision.content) .css("width", "100%")); } + else if (note.type === 'file') { + $content.html( + $("") + .append($("").append( + $("").append( + $("
").text("MIME: "), + $("").text(revisionItem.mime) + )) + .append($("
").text("File size:"), + $("").text(revisionItem.contentLength + " bytes") + )) + ); + } else { $content.text("Preview isn't available for this note type."); } diff --git a/src/public/javascripts/services/note_detail_file.js b/src/public/javascripts/services/note_detail_file.js index a13ab13d9..9724ac376 100644 --- a/src/public/javascripts/services/note_detail_file.js +++ b/src/public/javascripts/services/note_detail_file.js @@ -1,5 +1,7 @@ import utils from "./utils.js"; import server from "./server.js"; +import toastService from "./toast.js"; +import noteDetailService from "./note_detail.js"; class NoteDetailFile { /** @@ -16,10 +18,12 @@ class NoteDetailFile { this.$previewContent = ctx.$tabContent.find(".file-preview-content"); this.$downloadButton = ctx.$tabContent.find(".file-download"); this.$openButton = ctx.$tabContent.find(".file-open"); + this.$uploadNewRevisionButton = ctx.$tabContent.find(".file-upload-new-revision"); + this.$uploadNewRevisionInput = ctx.$tabContent.find(".file-upload-new-revision-input"); - this.$downloadButton.click(() => utils.download(this.getFileUrl())); + this.$downloadButton.on('click', () => utils.download(this.getFileUrl())); - this.$openButton.click(() => { + this.$openButton.on('click', () => { if (utils.isElectron()) { const open = require("open"); @@ -29,6 +33,34 @@ class NoteDetailFile { window.location.href = this.getFileUrl(); } }); + + this.$uploadNewRevisionButton.on("click", () => { + this.$uploadNewRevisionInput.trigger("click"); + }); + + this.$uploadNewRevisionInput.on('change', async () => { + const formData = new FormData(); + formData.append('upload', this.$uploadNewRevisionInput[0].files[0]); + + const result = await $.ajax({ + url: baseApiUrl + 'notes/' + this.ctx.note.noteId + '/file', + headers: server.getHeaders(), + data: formData, + type: 'PUT', + timeout: 60 * 60 * 1000, + contentType: false, // NEEDED, DON'T REMOVE THIS + processData: false, // NEEDED, DON'T REMOVE THIS + }); + + if (result.uploaded) { + toastService.showMessage("New file revision has been uploaded."); + + await noteDetailService.reload(); + } + else { + toastService.showError("Upload of a new file revision failed."); + } + }); } async render() { diff --git a/src/public/javascripts/services/note_detail_image.js b/src/public/javascripts/services/note_detail_image.js index ee4cf00eb..d0baaaa2f 100644 --- a/src/public/javascripts/services/note_detail_image.js +++ b/src/public/javascripts/services/note_detail_image.js @@ -62,14 +62,14 @@ class NoteDetailImage { }); if (result.uploaded) { - toastService.showMessage("New revision of the image has been uploaded.") + toastService.showMessage("New image revision has been uploaded."); await utils.clearBrowserCache(); await noteDetailService.reload(); } else { - toastService.showError("Could not upload new revision of the image: " + result.message); + toastService.showError("Upload of a new image revision failed: " + result.message); } }); } diff --git a/src/routes/api/file_upload.js b/src/routes/api/files.js similarity index 60% rename from src/routes/api/file_upload.js rename to src/routes/api/files.js index e5a9a6a01..5df06f179 100644 --- a/src/routes/api/file_upload.js +++ b/src/routes/api/files.js @@ -4,30 +4,30 @@ const noteService = require('../../services/notes'); const protectedSessionService = require('../../services/protected_session'); const repository = require('../../services/repository'); const utils = require('../../services/utils'); +const noteRevisionService = require('../../services/note_revisions'); -async function uploadFile(req) { - const parentNoteId = req.params.parentNoteId; +async function updateFile(req) { + const {noteId} = req.params; const file = req.file; - const originalName = file.originalname; - const size = file.size; - const mime = file.mimetype.toLowerCase(); - const parentNote = await repository.getNote(parentNoteId); + const note = await repository.getNote(noteId); - if (!parentNote) { - return [404, `Note ${parentNoteId} doesn't exist.`]; + if (!note) { + return [404, `Note ${noteId} doesn't exist.`]; } - const {note} = await noteService.createNote(parentNoteId, originalName, file.buffer, { - target: 'into', - isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(), - type: mime.startsWith("image/") ? 'image' : 'file', - mime: file.mimetype, - attributes: [{ type: "label", name: "originalFileName", value: originalName }] - }); + await noteRevisionService.createNoteRevision(note); + + note.mime = file.mimetype.toLowerCase(); + + await note.setContent(file.buffer); + + await note.setLabel('originalFileName', file.originalname); + + await noteRevisionService.protectNoteRevisions(note); return { - noteId: note.noteId + uploaded: true }; } @@ -58,7 +58,7 @@ async function downloadFile(req, res) { } module.exports = { - uploadFile, + updateFile, downloadFile, downloadNoteFile }; \ No newline at end of file diff --git a/src/routes/custom.js b/src/routes/custom.js index f1076e41b..7527b94b2 100644 --- a/src/routes/custom.js +++ b/src/routes/custom.js @@ -1,6 +1,6 @@ const repository = require('../services/repository'); const log = require('../services/log'); -const fileUploadService = require('./api/file_upload'); +const fileUploadService = require('./api/files.js'); const scriptService = require('../services/script'); function register(router) { diff --git a/src/routes/routes.js b/src/routes/routes.js index 3726481c1..da1e1bbfe 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -28,7 +28,7 @@ const imageRoute = require('./api/image'); const attributesRoute = require('./api/attributes'); const scriptRoute = require('./api/script'); const senderRoute = require('./api/sender'); -const filesRoute = require('./api/file_upload'); +const filesRoute = require('./api/files'); const searchRoute = require('./api/search'); const dateNotesRoute = require('./api/date_notes'); const linkMapRoute = require('./api/link_map'); @@ -146,8 +146,8 @@ function register(app) { route(GET, '/api/notes/:branchId/export/:type/:format/:version/:taskId', [auth.checkApiAuthOrElectron], exportRoute.exportBranch); route(POST, '/api/notes/:parentNoteId/import', [auth.checkApiAuthOrElectron, uploadMiddleware, csrfMiddleware], importRoute.importToBranch, apiResultHandler); - route(POST, '/api/notes/:parentNoteId/upload', [auth.checkApiAuthOrElectron, uploadMiddleware, csrfMiddleware], - filesRoute.uploadFile, apiResultHandler); + route(PUT, '/api/notes/:noteId/file', [auth.checkApiAuthOrElectron, uploadMiddleware, csrfMiddleware], + filesRoute.updateFile, apiResultHandler); route(GET, '/api/notes/:noteId/download', [auth.checkApiAuthOrElectron], filesRoute.downloadFile); // this "hacky" path is used for easier referencing of CSS resources diff --git a/src/services/image.js b/src/services/image.js index 8ac59d79c..34cdb6ec7 100644 --- a/src/services/image.js +++ b/src/services/image.js @@ -13,6 +13,7 @@ const jimp = require('jimp'); const imageType = require('image-type'); const sanitizeFilename = require('sanitize-filename'); const dateUtils = require('./date_utils'); +const noteRevisionService = require('./note_revisions.js'); const NoteRevision = require("../entities/note_revision"); async function processImage(uploadBuffer, originalName, shrinkImageSwitch) { @@ -38,22 +39,7 @@ async function updateImage(noteId, uploadBuffer, originalName) { const note = await repository.getNote(noteId); - const noteRevision = await new NoteRevision({ - noteId: note.noteId, - // title and text should be decrypted now - title: note.title, - contentLength: -1, // will be updated in .setContent() - type: note.type, - mime: note.mime, - isProtected: false, // will be fixed in the protectNoteRevisions() call - utcDateLastEdited: note.utcDateModified, - utcDateCreated: dateUtils.utcNowDateTime(), - utcDateModified: dateUtils.utcNowDateTime(), - dateLastEdited: note.dateModified, - dateCreated: dateUtils.localNowDateTime() - }).save(); - - await noteRevision.setContent(await note.getContent()); + await noteRevisionService.createNoteRevision(note); note.mime = 'image/' + imageFormat.ext.toLowerCase(); @@ -61,7 +47,7 @@ async function updateImage(noteId, uploadBuffer, originalName) { await note.setLabel('originalFileName', originalName); - await noteService.protectNoteRevisions(note); + await noteRevisionService.protectNoteRevisions(note); } async function saveImage(parentNoteId, uploadBuffer, originalName, shrinkImageSwitch) { diff --git a/src/services/note_revisions.js b/src/services/note_revisions.js new file mode 100644 index 000000000..24f40e580 --- /dev/null +++ b/src/services/note_revisions.js @@ -0,0 +1,47 @@ +"use strict"; + +const NoteRevision = require('../entities/note_revision'); +const dateUtils = require('../services/date_utils'); + +/** + * @param {Note} note + */ +async function protectNoteRevisions(note) { + for (const revision of await note.getRevisions()) { + if (note.isProtected !== revision.isProtected) { + revision.isProtected = note.isProtected; + + await revision.save(); + } + } +} + +/** + * @param {Note} note + * @return {NoteRevision} + */ +async function createNoteRevision(note) { + const noteRevision = await new NoteRevision({ + noteId: note.noteId, + // title and text should be decrypted now + title: note.title, + contentLength: -1, // will be updated in .setContent() + type: note.type, + mime: note.mime, + isProtected: false, // will be fixed in the protectNoteRevisions() call + utcDateLastEdited: note.utcDateModified, + utcDateCreated: dateUtils.utcNowDateTime(), + utcDateModified: dateUtils.utcNowDateTime(), + dateLastEdited: note.dateModified, + dateCreated: dateUtils.localNowDateTime() + }).save(); + + await noteRevision.setContent(await note.getContent()); + + return noteRevision; +} + +module.exports = { + protectNoteRevisions, + createNoteRevision +}; \ No newline at end of file diff --git a/src/services/notes.js b/src/services/notes.js index c36ec1ca9..8c9adca95 100644 --- a/src/services/notes.js +++ b/src/services/notes.js @@ -14,6 +14,7 @@ const Attribute = require('../entities/attribute'); const hoistedNoteService = require('../services/hoisted_note'); const protectedSessionService = require('../services/protected_session'); const log = require('../services/log'); +const noteRevisionService = require('../services/note_revisions'); async function getNewNotePosition(parentNoteId, noteData) { let newNotePos = 0; @@ -199,17 +200,7 @@ async function protectNote(note, protect) { await note.save(); } - await protectNoteRevisions(note); -} - -async function protectNoteRevisions(note) { - for (const revision of await note.getRevisions()) { - if (note.isProtected !== revision.isProtected) { - revision.isProtected = note.isProtected; - - await revision.save(); - } - } + await noteRevisionService.protectNoteRevisions(note); } function findImageLinks(content, foundLinks) { @@ -383,7 +374,7 @@ async function updateNote(noteId, noteUpdates) { await triggerNoteTitleChanged(note); } - await protectNoteRevisions(note); + await noteRevisionService.protectNoteRevisions(note); return { dateModified: note.dateModified, @@ -538,6 +529,5 @@ module.exports = { deleteBranch, protectNoteRecursively, scanForLinks, - duplicateNote, - protectNoteRevisions + duplicateNote }; \ No newline at end of file diff --git a/src/views/details/file.ejs b/src/views/details/file.ejs index 1ea689150..dd96c9e4d 100644 --- a/src/views/details/file.ejs +++ b/src/views/details/file.ejs @@ -24,10 +24,14 @@
- +   - + +   +
+ + \ No newline at end of file diff --git a/src/views/details/image.ejs b/src/views/details/image.ejs index b0cd93373..82e4228a2 100644 --- a/src/views/details/image.ejs +++ b/src/views/details/image.ejs @@ -27,6 +27,6 @@ - - \ No newline at end of file + + \ No newline at end of file