mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	attachment revision upload
This commit is contained in:
		| @@ -75,7 +75,6 @@ module.exports = { | |||||||
|         glob: true, |         glob: true, | ||||||
|         log: true, |         log: true, | ||||||
|         EditorWatchdog: true, |         EditorWatchdog: true, | ||||||
|         baseApiUrl: true, |  | ||||||
|         // \src\share\canvas_share.js |         // \src\share\canvas_share.js | ||||||
|         React: true, |         React: true, | ||||||
|         appState: true, |         appState: true, | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ export async function uploadFiles(parentNoteId, files, options) { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         await $.ajax({ |         await $.ajax({ | ||||||
|             url: `${baseApiUrl}notes/${parentNoteId}/import`, |             url: `${window.glob.baseApiUrl}notes/${parentNoteId}/import`, | ||||||
|             headers: await server.getHeaders(), |             headers: await server.getHeaders(), | ||||||
|             data: formData, |             data: formData, | ||||||
|             dataType: 'json', |             dataType: 'json', | ||||||
|   | |||||||
| @@ -79,7 +79,7 @@ async function call(method, url, data, headers = {}) { | |||||||
|                 requestId: requestId, |                 requestId: requestId, | ||||||
|                 headers: headers, |                 headers: headers, | ||||||
|                 method: method, |                 method: method, | ||||||
|                 url: `/${baseApiUrl}${url}`, |                 url: `/${window.glob.baseApiUrl}${url}`, | ||||||
|                 data: data |                 data: data | ||||||
|             }); |             }); | ||||||
|         }); |         }); | ||||||
| @@ -128,7 +128,7 @@ async function reportError(method, url, statusCode, response) { | |||||||
| function ajax(url, method, data, headers) { | function ajax(url, method, data, headers) { | ||||||
|     return new Promise((res, rej) => { |     return new Promise((res, rej) => { | ||||||
|         const options = { |         const options = { | ||||||
|             url: baseApiUrl + url, |             url: window.glob.baseApiUrl + url, | ||||||
|             type: method, |             type: method, | ||||||
|             headers: headers, |             headers: headers, | ||||||
|             timeout: 60000, |             timeout: 60000, | ||||||
|   | |||||||
| @@ -182,15 +182,15 @@ function randomString(len) { | |||||||
| } | } | ||||||
|  |  | ||||||
| function isMobile() { | function isMobile() { | ||||||
|     return window.device === "mobile" |     return window.glob?.device === "mobile" | ||||||
|         // window.device is not available in setup |         // window.glob.device is not available in setup | ||||||
|         || (!window.device && /Mobi/.test(navigator.userAgent)); |         || (!window.glob?.device && /Mobi/.test(navigator.userAgent)); | ||||||
| } | } | ||||||
|  |  | ||||||
| function isDesktop() { | function isDesktop() { | ||||||
|     return window.device === "desktop" |     return window.glob?.device === "desktop" | ||||||
|         // window.device is not available in setup |         // window.glob.device is not available in setup | ||||||
|         || (!window.device && !/Mobi/.test(navigator.userAgent)); |         || (!window.glob?.device && !/Mobi/.test(navigator.userAgent)); | ||||||
| } | } | ||||||
|  |  | ||||||
| // cookie code below works for simple use cases only - ASCII only | // cookie code below works for simple use cases only - ASCII only | ||||||
|   | |||||||
| @@ -39,6 +39,8 @@ const TPL = ` | |||||||
|         <a data-trigger-command="convertAttachmentIntoNote" class="dropdown-item">Convert attachment into note</a> |         <a data-trigger-command="convertAttachmentIntoNote" class="dropdown-item">Convert attachment into note</a> | ||||||
|         <a data-trigger-command="deleteAttachment" class="dropdown-item">Delete attachment</a> |         <a data-trigger-command="deleteAttachment" class="dropdown-item">Delete attachment</a> | ||||||
|     </div> |     </div> | ||||||
|  |      | ||||||
|  |     <input type="file" class="attachment-upload-new-revision-input" style="display: none"> | ||||||
| </div>`; | </div>`; | ||||||
|  |  | ||||||
| export default class AttachmentActionsWidget extends BasicWidget { | export default class AttachmentActionsWidget extends BasicWidget { | ||||||
| @@ -56,6 +58,31 @@ export default class AttachmentActionsWidget extends BasicWidget { | |||||||
|         this.$widget = $(TPL); |         this.$widget = $(TPL); | ||||||
|         this.$widget.on('click', '.dropdown-item', () => this.$widget.find("[data-toggle='dropdown']").dropdown('toggle')); |         this.$widget.on('click', '.dropdown-item', () => this.$widget.find("[data-toggle='dropdown']").dropdown('toggle')); | ||||||
|         this.$widget.find("[data-trigger-command='copyAttachmentReferenceToClipboard']").toggle(this.attachment.role === 'image'); |         this.$widget.find("[data-trigger-command='copyAttachmentReferenceToClipboard']").toggle(this.attachment.role === 'image'); | ||||||
|  |  | ||||||
|  |         this.$uploadNewRevisionInput = this.$widget.find(".attachment-upload-new-revision-input"); | ||||||
|  |         this.$uploadNewRevisionInput.on('change', async () => { | ||||||
|  |             const fileToUpload = this.$uploadNewRevisionInput[0].files[0]; // copy to allow reset below | ||||||
|  |             this.$uploadNewRevisionInput.val(''); | ||||||
|  |  | ||||||
|  |             const formData = new FormData(); | ||||||
|  |             formData.append('upload', fileToUpload); | ||||||
|  |  | ||||||
|  |             const result = await $.ajax({ | ||||||
|  |                 url: `${window.glob.baseApiUrl}attachments/${this.attachmentId}/file`, | ||||||
|  |                 headers: await 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 attachment revision has been uploaded."); | ||||||
|  |             } else { | ||||||
|  |                 toastService.showError("Upload of a new attachment revision failed."); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async openAttachmentCommand() { |     async openAttachmentCommand() { | ||||||
| @@ -66,6 +93,10 @@ export default class AttachmentActionsWidget extends BasicWidget { | |||||||
|         await openService.downloadAttachment(this.attachmentId); |         await openService.downloadAttachment(this.attachmentId); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     async uploadNewAttachmentRevisionCommand() { | ||||||
|  |         this.$uploadNewRevisionInput.trigger('click'); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     async copyAttachmentReferenceToClipboardCommand() { |     async copyAttachmentReferenceToClipboardCommand() { | ||||||
|         this.parent.copyAttachmentReferenceToClipboard(); |         this.parent.copyAttachmentReferenceToClipboard(); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -104,7 +104,7 @@ export default class FilePropertiesWidget extends NoteContextAwareWidget { | |||||||
|             formData.append('upload', fileToUpload); |             formData.append('upload', fileToUpload); | ||||||
|  |  | ||||||
|             const result = await $.ajax({ |             const result = await $.ajax({ | ||||||
|                 url: `${baseApiUrl}notes/${this.noteId}/file`, |                 url: `${window.glob.baseApiUrl}notes/${this.noteId}/file`, | ||||||
|                 headers: await server.getHeaders(), |                 headers: await server.getHeaders(), | ||||||
|                 data: formData, |                 data: formData, | ||||||
|                 type: 'PUT', |                 type: 'PUT', | ||||||
|   | |||||||
| @@ -88,7 +88,7 @@ export default class ImagePropertiesWidget extends NoteContextAwareWidget { | |||||||
|             formData.append('upload', fileToUpload); |             formData.append('upload', fileToUpload); | ||||||
|  |  | ||||||
|             const result = await $.ajax({ |             const result = await $.ajax({ | ||||||
|                 url: `${baseApiUrl}images/${this.noteId}`, |                 url: `${window.glob.baseApiUrl}images/${this.noteId}`, | ||||||
|                 headers: await server.getHeaders(), |                 headers: await server.getHeaders(), | ||||||
|                 data: formData, |                 data: formData, | ||||||
|                 type: 'PUT', |                 type: 'PUT', | ||||||
|   | |||||||
| @@ -14,15 +14,12 @@ const NotFoundError = require("../../errors/not_found_error"); | |||||||
| const ValidationError = require("../../errors/validation_error.js"); | const ValidationError = require("../../errors/validation_error.js"); | ||||||
|  |  | ||||||
| function updateFile(req) { | function updateFile(req) { | ||||||
|     const {noteId} = req.params; |     const note = becca.getNote(req.params.noteId); | ||||||
|     const file = req.file; |  | ||||||
|  |  | ||||||
|     const note = becca.getNote(noteId); |  | ||||||
|  |  | ||||||
|     if (!note) { |     if (!note) { | ||||||
|         throw new NotFoundError(`Note '${noteId}' doesn't exist.`); |         throw new NotFoundError(`Note '${req.params.noteId}' doesn't exist.`); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     const file = req.file; | ||||||
|     note.saveNoteRevision(); |     note.saveNoteRevision(); | ||||||
|  |  | ||||||
|     note.mime = file.mimetype.toLowerCase(); |     note.mime = file.mimetype.toLowerCase(); | ||||||
| @@ -39,6 +36,23 @@ function updateFile(req) { | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | function updateAttachment(req) { | ||||||
|  |     const attachment = becca.getAttachment(req.params.attachmentId); | ||||||
|  |     if (!attachment) { | ||||||
|  |         throw new NotFoundError(`Attachment '${req.params.attachmentId}' doesn't exist.`); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const file = req.file; | ||||||
|  |     attachment.getNote().saveNoteRevision(); | ||||||
|  |  | ||||||
|  |     attachment.mime = file.mimetype.toLowerCase(); | ||||||
|  |     attachment.setContent(file.buffer, {forceSave: true}); | ||||||
|  |  | ||||||
|  |     return { | ||||||
|  |         uploaded: true | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @param {BNote|BAttachment} noteOrAttachment |  * @param {BNote|BAttachment} noteOrAttachment | ||||||
|  * @param res |  * @param res | ||||||
| @@ -234,6 +248,7 @@ function uploadModifiedFileToAttachment(req) { | |||||||
|  |  | ||||||
| module.exports = { | module.exports = { | ||||||
|     updateFile, |     updateFile, | ||||||
|  |     updateAttachment, | ||||||
|     openFile, |     openFile, | ||||||
|     fileContentProvider, |     fileContentProvider, | ||||||
|     downloadFile, |     downloadFile, | ||||||
|   | |||||||
| @@ -164,6 +164,8 @@ function register(app) { | |||||||
|     route(GET, '/api/attachments/download/:attachmentId', [auth.checkApiAuthOrElectron], filesRoute.downloadAttachment); |     route(GET, '/api/attachments/download/:attachmentId', [auth.checkApiAuthOrElectron], filesRoute.downloadAttachment); | ||||||
|     apiRoute(PST, '/api/attachments/:attachmentId/save-to-tmp-dir', filesRoute.saveAttachmentToTmpDir); |     apiRoute(PST, '/api/attachments/:attachmentId/save-to-tmp-dir', filesRoute.saveAttachmentToTmpDir); | ||||||
|     apiRoute(PST, '/api/attachments/:attachmentId/upload-modified-file', filesRoute.uploadModifiedFileToAttachment); |     apiRoute(PST, '/api/attachments/:attachmentId/upload-modified-file', filesRoute.uploadModifiedFileToAttachment); | ||||||
|  |     route(PUT, '/api/attachments/:attachmentId/file', [auth.checkApiAuthOrElectron, uploadMiddlewareWithErrorHandling, csrfMiddleware], | ||||||
|  |         filesRoute.updateAttachment, apiResultHandler); | ||||||
|  |  | ||||||
|     apiRoute(GET, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.getNoteRevisions); |     apiRoute(GET, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.getNoteRevisions); | ||||||
|     apiRoute(DEL, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.eraseAllNoteRevisions); |     apiRoute(DEL, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.eraseAllNoteRevisions); | ||||||
|   | |||||||
| @@ -21,9 +21,9 @@ | |||||||
| <script type="text/javascript"> | <script type="text/javascript"> | ||||||
|     global = globalThis; /* fixes https://github.com/webpack/webpack/issues/10035 */ |     global = globalThis; /* fixes https://github.com/webpack/webpack/issues/10035 */ | ||||||
|  |  | ||||||
|     window.baseApiUrl = 'api/'; |  | ||||||
|     window.device = "desktop"; |  | ||||||
|     window.glob = { |     window.glob = { | ||||||
|  |         device: "desktop", | ||||||
|  |         baseApiUrl: 'api/', | ||||||
|         activeDialog: null, |         activeDialog: null, | ||||||
|         maxEntityChangeIdAtLoad: <%= maxEntityChangeIdAtLoad %>, |         maxEntityChangeIdAtLoad: <%= maxEntityChangeIdAtLoad %>, | ||||||
|         maxEntityChangeSyncIdAtLoad: <%= maxEntityChangeSyncIdAtLoad %>, |         maxEntityChangeSyncIdAtLoad: <%= maxEntityChangeSyncIdAtLoad %>, | ||||||
|   | |||||||
| @@ -105,9 +105,9 @@ | |||||||
| <script type="text/javascript"> | <script type="text/javascript"> | ||||||
|     global = globalThis; /* fixes https://github.com/webpack/webpack/issues/10035 */ |     global = globalThis; /* fixes https://github.com/webpack/webpack/issues/10035 */ | ||||||
|  |  | ||||||
|     window.baseApiUrl = 'api/'; |  | ||||||
|     window.device = "mobile"; |  | ||||||
|     window.glob = { |     window.glob = { | ||||||
|  |         device: "mobile", | ||||||
|  |         baseApiUrl: 'api/', | ||||||
|         activeDialog: null, |         activeDialog: null, | ||||||
|         maxEntityChangeIdAtLoad: <%= maxEntityChangeIdAtLoad %>, |         maxEntityChangeIdAtLoad: <%= maxEntityChangeIdAtLoad %>, | ||||||
|         maxEntityChangeSyncIdAtLoad: <%= maxEntityChangeSyncIdAtLoad %>, |         maxEntityChangeSyncIdAtLoad: <%= maxEntityChangeSyncIdAtLoad %>, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user