mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	uploading new file revisions
This commit is contained in:
		| @@ -77,6 +77,19 @@ $list.on('change', async () => { | |||||||
|             .attr("src", `data:${note.mime};base64,` + fullNoteRevision.content) |             .attr("src", `data:${note.mime};base64,` + fullNoteRevision.content) | ||||||
|             .css("width", "100%")); |             .css("width", "100%")); | ||||||
|     } |     } | ||||||
|  |     else if (note.type === 'file') { | ||||||
|  |         $content.html( | ||||||
|  |             $("<table cellpadding='10'>") | ||||||
|  |                 .append($("<tr>").append( | ||||||
|  |                     $("<th>").text("MIME: "), | ||||||
|  |                     $("<td>").text(revisionItem.mime) | ||||||
|  |                 )) | ||||||
|  |                 .append($("<tr>").append( | ||||||
|  |                     $("<th>").text("File size:"), | ||||||
|  |                     $("<td>").text(revisionItem.contentLength + " bytes") | ||||||
|  |                 )) | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|     else { |     else { | ||||||
|         $content.text("Preview isn't available for this note type."); |         $content.text("Preview isn't available for this note type."); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,5 +1,7 @@ | |||||||
| import utils from "./utils.js"; | import utils from "./utils.js"; | ||||||
| import server from "./server.js"; | import server from "./server.js"; | ||||||
|  | import toastService from "./toast.js"; | ||||||
|  | import noteDetailService from "./note_detail.js"; | ||||||
|  |  | ||||||
| class NoteDetailFile { | class NoteDetailFile { | ||||||
|     /** |     /** | ||||||
| @@ -16,10 +18,12 @@ class NoteDetailFile { | |||||||
|         this.$previewContent = ctx.$tabContent.find(".file-preview-content"); |         this.$previewContent = ctx.$tabContent.find(".file-preview-content"); | ||||||
|         this.$downloadButton = ctx.$tabContent.find(".file-download"); |         this.$downloadButton = ctx.$tabContent.find(".file-download"); | ||||||
|         this.$openButton = ctx.$tabContent.find(".file-open"); |         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()) { |             if (utils.isElectron()) { | ||||||
|                 const open = require("open"); |                 const open = require("open"); | ||||||
|  |  | ||||||
| @@ -29,6 +33,34 @@ class NoteDetailFile { | |||||||
|                 window.location.href = this.getFileUrl(); |                 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() { |     async render() { | ||||||
|   | |||||||
| @@ -62,14 +62,14 @@ class NoteDetailImage { | |||||||
|             }); |             }); | ||||||
|  |  | ||||||
|             if (result.uploaded) { |             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 utils.clearBrowserCache(); | ||||||
|  |  | ||||||
|                 await noteDetailService.reload(); |                 await noteDetailService.reload(); | ||||||
|             } |             } | ||||||
|             else { |             else { | ||||||
|                 toastService.showError("Could not upload new revision of the image: " + result.message); |                 toastService.showError("Upload of a new image revision failed: " + result.message); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -4,30 +4,30 @@ const noteService = require('../../services/notes'); | |||||||
| const protectedSessionService = require('../../services/protected_session'); | const protectedSessionService = require('../../services/protected_session'); | ||||||
| const repository = require('../../services/repository'); | const repository = require('../../services/repository'); | ||||||
| const utils = require('../../services/utils'); | const utils = require('../../services/utils'); | ||||||
|  | const noteRevisionService = require('../../services/note_revisions'); | ||||||
| 
 | 
 | ||||||
| async function uploadFile(req) { | async function updateFile(req) { | ||||||
|     const parentNoteId = req.params.parentNoteId; |     const {noteId} = req.params; | ||||||
|     const file = req.file; |     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) { |     if (!note) { | ||||||
|         return [404, `Note ${parentNoteId} doesn't exist.`]; |         return [404, `Note ${noteId} doesn't exist.`]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const {note} = await noteService.createNote(parentNoteId, originalName, file.buffer, { |     await noteRevisionService.createNoteRevision(note); | ||||||
|         target: 'into', | 
 | ||||||
|         isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(), |     note.mime = file.mimetype.toLowerCase(); | ||||||
|         type: mime.startsWith("image/") ? 'image' : 'file', | 
 | ||||||
|         mime: file.mimetype, |     await note.setContent(file.buffer); | ||||||
|         attributes: [{ type: "label", name: "originalFileName", value: originalName }] | 
 | ||||||
|     }); |     await note.setLabel('originalFileName', file.originalname); | ||||||
|  | 
 | ||||||
|  |     await noteRevisionService.protectNoteRevisions(note); | ||||||
| 
 | 
 | ||||||
|     return { |     return { | ||||||
|         noteId: note.noteId |         uploaded: true | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -58,7 +58,7 @@ async function downloadFile(req, res) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | module.exports = { | ||||||
|     uploadFile, |     updateFile, | ||||||
|     downloadFile, |     downloadFile, | ||||||
|     downloadNoteFile |     downloadNoteFile | ||||||
| }; | }; | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| const repository = require('../services/repository'); | const repository = require('../services/repository'); | ||||||
| const log = require('../services/log'); | const log = require('../services/log'); | ||||||
| const fileUploadService = require('./api/file_upload'); | const fileUploadService = require('./api/files.js'); | ||||||
| const scriptService = require('../services/script'); | const scriptService = require('../services/script'); | ||||||
|  |  | ||||||
| function register(router) { | function register(router) { | ||||||
|   | |||||||
| @@ -28,7 +28,7 @@ const imageRoute = require('./api/image'); | |||||||
| const attributesRoute = require('./api/attributes'); | const attributesRoute = require('./api/attributes'); | ||||||
| const scriptRoute = require('./api/script'); | const scriptRoute = require('./api/script'); | ||||||
| const senderRoute = require('./api/sender'); | const senderRoute = require('./api/sender'); | ||||||
| const filesRoute = require('./api/file_upload'); | const filesRoute = require('./api/files'); | ||||||
| const searchRoute = require('./api/search'); | const searchRoute = require('./api/search'); | ||||||
| const dateNotesRoute = require('./api/date_notes'); | const dateNotesRoute = require('./api/date_notes'); | ||||||
| const linkMapRoute = require('./api/link_map'); | 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(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/import', [auth.checkApiAuthOrElectron, uploadMiddleware, csrfMiddleware], importRoute.importToBranch, apiResultHandler); | ||||||
|  |  | ||||||
|     route(POST, '/api/notes/:parentNoteId/upload', [auth.checkApiAuthOrElectron, uploadMiddleware, csrfMiddleware], |     route(PUT, '/api/notes/:noteId/file', [auth.checkApiAuthOrElectron, uploadMiddleware, csrfMiddleware], | ||||||
|         filesRoute.uploadFile, apiResultHandler); |         filesRoute.updateFile, apiResultHandler); | ||||||
|  |  | ||||||
|     route(GET, '/api/notes/:noteId/download', [auth.checkApiAuthOrElectron], filesRoute.downloadFile); |     route(GET, '/api/notes/:noteId/download', [auth.checkApiAuthOrElectron], filesRoute.downloadFile); | ||||||
|     // this "hacky" path is used for easier referencing of CSS resources |     // this "hacky" path is used for easier referencing of CSS resources | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ const jimp = require('jimp'); | |||||||
| const imageType = require('image-type'); | const imageType = require('image-type'); | ||||||
| const sanitizeFilename = require('sanitize-filename'); | const sanitizeFilename = require('sanitize-filename'); | ||||||
| const dateUtils = require('./date_utils'); | const dateUtils = require('./date_utils'); | ||||||
|  | const noteRevisionService = require('./note_revisions.js'); | ||||||
| const NoteRevision = require("../entities/note_revision"); | const NoteRevision = require("../entities/note_revision"); | ||||||
|  |  | ||||||
| async function processImage(uploadBuffer, originalName, shrinkImageSwitch) { | async function processImage(uploadBuffer, originalName, shrinkImageSwitch) { | ||||||
| @@ -38,22 +39,7 @@ async function updateImage(noteId, uploadBuffer, originalName) { | |||||||
|  |  | ||||||
|     const note = await repository.getNote(noteId); |     const note = await repository.getNote(noteId); | ||||||
|  |  | ||||||
|     const noteRevision = await new NoteRevision({ |     await noteRevisionService.createNoteRevision(note); | ||||||
|         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()); |  | ||||||
|  |  | ||||||
|     note.mime = 'image/' + imageFormat.ext.toLowerCase(); |     note.mime = 'image/' + imageFormat.ext.toLowerCase(); | ||||||
|  |  | ||||||
| @@ -61,7 +47,7 @@ async function updateImage(noteId, uploadBuffer, originalName) { | |||||||
|  |  | ||||||
|     await note.setLabel('originalFileName', originalName); |     await note.setLabel('originalFileName', originalName); | ||||||
|  |  | ||||||
|     await noteService.protectNoteRevisions(note); |     await noteRevisionService.protectNoteRevisions(note); | ||||||
| } | } | ||||||
|  |  | ||||||
| async function saveImage(parentNoteId, uploadBuffer, originalName, shrinkImageSwitch) { | async function saveImage(parentNoteId, uploadBuffer, originalName, shrinkImageSwitch) { | ||||||
|   | |||||||
							
								
								
									
										47
									
								
								src/services/note_revisions.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/services/note_revisions.js
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
|  | }; | ||||||
| @@ -14,6 +14,7 @@ const Attribute = require('../entities/attribute'); | |||||||
| const hoistedNoteService = require('../services/hoisted_note'); | const hoistedNoteService = require('../services/hoisted_note'); | ||||||
| const protectedSessionService = require('../services/protected_session'); | const protectedSessionService = require('../services/protected_session'); | ||||||
| const log = require('../services/log'); | const log = require('../services/log'); | ||||||
|  | const noteRevisionService = require('../services/note_revisions'); | ||||||
|  |  | ||||||
| async function getNewNotePosition(parentNoteId, noteData) { | async function getNewNotePosition(parentNoteId, noteData) { | ||||||
|     let newNotePos = 0; |     let newNotePos = 0; | ||||||
| @@ -199,17 +200,7 @@ async function protectNote(note, protect) { | |||||||
|         await note.save(); |         await note.save(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     await protectNoteRevisions(note); |     await noteRevisionService.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(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| function findImageLinks(content, foundLinks) { | function findImageLinks(content, foundLinks) { | ||||||
| @@ -383,7 +374,7 @@ async function updateNote(noteId, noteUpdates) { | |||||||
|         await triggerNoteTitleChanged(note); |         await triggerNoteTitleChanged(note); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     await protectNoteRevisions(note); |     await noteRevisionService.protectNoteRevisions(note); | ||||||
|  |  | ||||||
|     return { |     return { | ||||||
|         dateModified: note.dateModified, |         dateModified: note.dateModified, | ||||||
| @@ -538,6 +529,5 @@ module.exports = { | |||||||
|     deleteBranch, |     deleteBranch, | ||||||
|     protectNoteRecursively, |     protectNoteRecursively, | ||||||
|     scanForLinks, |     scanForLinks, | ||||||
|     duplicateNote, |     duplicateNote | ||||||
|     protectNoteRevisions |  | ||||||
| }; | }; | ||||||
| @@ -24,10 +24,14 @@ | |||||||
|         </tr> |         </tr> | ||||||
|         <tr> |         <tr> | ||||||
|             <td colspan="2"> |             <td colspan="2"> | ||||||
|                 <button class="file-download btn btn-primary" type="button">Download</button> |                 <button class="file-download btn btn-sm btn-primary" type="button">Download</button> | ||||||
|                   |                   | ||||||
|                 <button class="file-open btn btn-primary" type="button">Open</button> |                 <button class="file-open btn btn-sm btn-primary" type="button">Open</button> | ||||||
|  |                   | ||||||
|  |                 <button class="file-upload-new-revision btn btn-sm btn-primary">Upload new revision</button> | ||||||
|             </td> |             </td> | ||||||
|         </tr> |         </tr> | ||||||
|     </table> |     </table> | ||||||
|  |  | ||||||
|  |     <input type="file" class="file-upload-new-revision-input" style="display: none"> | ||||||
| </div> | </div> | ||||||
| @@ -27,6 +27,6 @@ | |||||||
|             <span class="image-filesize"></span> |             <span class="image-filesize"></span> | ||||||
|         </span> |         </span> | ||||||
|     </div> |     </div> | ||||||
| </div> |  | ||||||
|  |  | ||||||
|     <input type="file" class="image-upload-new-revision-input" style="display: none"> |     <input type="file" class="image-upload-new-revision-input" style="display: none"> | ||||||
|  | </div> | ||||||
		Reference in New Issue
	
	Block a user