mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +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) | ||||
|             .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 { | ||||
|         $content.text("Preview isn't available for this note type."); | ||||
|     } | ||||
|   | ||||
| @@ -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() { | ||||
|   | ||||
| @@ -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); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|   | ||||
| @@ -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 | ||||
| }; | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
							
								
								
									
										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 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 | ||||
| }; | ||||
| @@ -24,10 +24,14 @@ | ||||
|         </tr> | ||||
|         <tr> | ||||
|             <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> | ||||
|         </tr> | ||||
|     </table> | ||||
|  | ||||
|     <input type="file" class="file-upload-new-revision-input" style="display: none"> | ||||
| </div> | ||||
| @@ -27,6 +27,6 @@ | ||||
|             <span class="image-filesize"></span> | ||||
|         </span> | ||||
|     </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