mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-29 02:28:57 +01:00 
			
		
		
		
	attachment upload and download now works for browser
This commit is contained in:
		
							parent
							
								
									fda4146150
								
							
						
					
					
						commit
						78e8c15786
					
				| @ -14,6 +14,10 @@ const noteEditor = (function() { | ||||
|     const $noteIdDisplay = $("#note-id-display"); | ||||
|     const $attributeList = $("#attribute-list"); | ||||
|     const $attributeListInner = $("#attribute-list-inner"); | ||||
|     const $attachmentFileName = $("#attachment-filename"); | ||||
|     const $attachmentFileType = $("#attachment-filetype"); | ||||
|     const $attachmentFileSize = $("#attachment-filesize"); | ||||
|     const $attachmentDownload = $("#attachment-download"); | ||||
| 
 | ||||
|     let editor = null; | ||||
|     let codeEditor = null; | ||||
| @ -83,7 +87,7 @@ const noteEditor = (function() { | ||||
|         else if (note.detail.type === 'code') { | ||||
|             note.detail.content = codeEditor.getValue(); | ||||
|         } | ||||
|         else if (note.detail.type === 'render') { | ||||
|         else if (note.detail.type === 'render' || note.detail.type === 'file') { | ||||
|             // nothing
 | ||||
|         } | ||||
|         else { | ||||
| @ -185,6 +189,10 @@ const noteEditor = (function() { | ||||
|         } | ||||
|         else if (currentNote.detail.type === 'file') { | ||||
|             $noteDetailAttachment.show(); | ||||
| 
 | ||||
|             $attachmentFileName.text(currentNote.attributes.original_file_name); | ||||
|             $attachmentFileSize.text(currentNote.attributes.file_size + " bytes"); | ||||
|             $attachmentFileType.text(currentNote.detail.mime); | ||||
|         } | ||||
|         else { | ||||
|             setContent(currentNote.detail.content); | ||||
| @ -237,7 +245,7 @@ const noteEditor = (function() { | ||||
|         else if (note.detail.type === 'code') { | ||||
|             codeEditor.focus(); | ||||
|         } | ||||
|         else if (note.detail.type === 'render') { | ||||
|         else if (note.detail.type === 'render' || note.detail.type === 'file') { | ||||
|             // do nothing
 | ||||
|         } | ||||
|         else { | ||||
| @ -262,6 +270,10 @@ const noteEditor = (function() { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     $attachmentDownload.click(() => { | ||||
|         window.location.href = "/api/attachments/download/" + getCurrentNoteId(); | ||||
|     }); | ||||
| 
 | ||||
|     $(document).ready(() => { | ||||
|         $noteTitle.on('input', () => { | ||||
|             noteChanged(); | ||||
|  | ||||
| @ -65,11 +65,18 @@ const noteType = (function() { | ||||
|             else if (type === 'render') { | ||||
|                 return 'Render HTML note'; | ||||
|             } | ||||
|             else if (type === 'file') { | ||||
|                 return 'Attachment'; | ||||
|             } | ||||
|             else { | ||||
|                 throwError('Unrecognized type: ' + type); | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         this.isDisabled = function() { | ||||
|             return self.type() === "file"; | ||||
|         }; | ||||
| 
 | ||||
|         async function save() { | ||||
|             const note = noteEditor.getCurrentNote(); | ||||
| 
 | ||||
|  | ||||
| @ -116,6 +116,7 @@ const server = (function() { | ||||
|         put, | ||||
|         remove, | ||||
|         exec, | ||||
|         ajax, | ||||
|         // don't remove, used from CKEditor image upload!
 | ||||
|         getHeaders | ||||
|     } | ||||
|  | ||||
| @ -269,3 +269,8 @@ div.ui-tooltip { | ||||
|     padding: 2px; | ||||
|     margin-right: 5px; | ||||
| } | ||||
| 
 | ||||
| #attachment-table th, #attachment-table td { | ||||
|     padding: 10px; | ||||
|     font-size: large; | ||||
| } | ||||
| @ -5,6 +5,7 @@ const router = express.Router(); | ||||
| const sql = require('../../services/sql'); | ||||
| const auth = require('../../services/auth'); | ||||
| const notes = require('../../services/notes'); | ||||
| const attributes = require('../../services/attributes'); | ||||
| const multer = require('multer')(); | ||||
| const wrap = require('express-promise-wrap').wrap; | ||||
| 
 | ||||
| @ -12,6 +13,8 @@ router.post('/upload/:parentNoteId', auth.checkApiAuthOrElectron, multer.single( | ||||
|     const sourceId = req.headers.source_id; | ||||
|     const parentNoteId = req.params.parentNoteId; | ||||
|     const file = req.file; | ||||
|     const originalName = file.originalname; | ||||
|     const size = file.size; | ||||
| 
 | ||||
|     const note = await sql.getRow("SELECT * FROM notes WHERE noteId = ?", [parentNoteId]); | ||||
| 
 | ||||
| @ -19,18 +22,40 @@ router.post('/upload/:parentNoteId', auth.checkApiAuthOrElectron, multer.single( | ||||
|         return res.status(404).send(`Note ${parentNoteId} doesn't exist.`); | ||||
|     } | ||||
| 
 | ||||
|     const noteId = (await notes.createNewNote(parentNoteId, { | ||||
|         title: "attachment", | ||||
|         content: file.buffer, | ||||
|         target: 'into', | ||||
|         isProtected: false, | ||||
|         type: 'file', | ||||
|         mime: '' | ||||
|     })).noteId; | ||||
|     await sql.doInTransaction(async () => { | ||||
|         const noteId = (await notes.createNewNote(parentNoteId, { | ||||
|             title: originalName, | ||||
|             content: file.buffer, | ||||
|             target: 'into', | ||||
|             isProtected: false, | ||||
|             type: 'file', | ||||
|             mime: file.mimetype | ||||
|         }, req, sourceId)).noteId; | ||||
| 
 | ||||
|     res.send({ | ||||
|         noteId: noteId | ||||
|         await attributes.createAttribute(noteId, "original_file_name", originalName); | ||||
|         await attributes.createAttribute(noteId, "file_size", size); | ||||
| 
 | ||||
|         res.send({ | ||||
|             noteId: noteId | ||||
|         }); | ||||
|     }); | ||||
| })); | ||||
| 
 | ||||
| router.get('/download/:noteId', auth.checkApiAuthOrElectron, wrap(async (req, res, next) => { | ||||
|     const noteId = req.params.noteId; | ||||
|     const note = await sql.getRow("SELECT * FROM notes WHERE noteId = ?", [noteId]); | ||||
| 
 | ||||
|     if (!note) { | ||||
|         return res.status(404).send(`Note ${parentNoteId} doesn't exist.`); | ||||
|     } | ||||
| 
 | ||||
|     const attributeMap = await attributes.getNoteAttributeMap(noteId); | ||||
|     const fileName = attributeMap.original_file_name ? attributeMap.original_file_name : note.title; | ||||
| 
 | ||||
|     res.setHeader('Content-Disposition', 'attachment; filename=' + fileName); | ||||
|     res.setHeader('Content-Type', note.mime); | ||||
| 
 | ||||
|     res.send(note.content); | ||||
| })); | ||||
| 
 | ||||
| module.exports = router; | ||||
| @ -5,6 +5,7 @@ const router = express.Router(); | ||||
| const auth = require('../../services/auth'); | ||||
| const sql = require('../../services/sql'); | ||||
| const notes = require('../../services/notes'); | ||||
| const attributes = require('../../services/attributes'); | ||||
| const log = require('../../services/log'); | ||||
| const utils = require('../../services/utils'); | ||||
| const protected_session = require('../../services/protected_session'); | ||||
| @ -25,8 +26,19 @@ router.get('/:noteId', auth.checkApiAuth, wrap(async (req, res, next) => { | ||||
| 
 | ||||
|     protected_session.decryptNote(req, detail); | ||||
| 
 | ||||
|     let attributeMap = null; | ||||
| 
 | ||||
|     if (detail.type === 'file') { | ||||
|         // no need to transfer attachment payload for this request
 | ||||
|         detail.content = null; | ||||
| 
 | ||||
|         // attributes contain important attachment metadata - filename and size
 | ||||
|         attributeMap = await attributes.getNoteAttributeMap(noteId); | ||||
|     } | ||||
| 
 | ||||
|     res.send({ | ||||
|         detail: detail | ||||
|         detail: detail, | ||||
|         attributes: attributeMap | ||||
|     }); | ||||
| })); | ||||
| 
 | ||||
|  | ||||
| @ -32,7 +32,7 @@ | ||||
|         $date.datepicker('setDate', new Date()); | ||||
| 
 | ||||
|         async function saveWeight() { | ||||
|             await server.exec([$date.val(), $weight.val(), $comment.val()], async (date, weight, comment) => { | ||||
|             await server.exec([$date.val(), parseFloat($weight.val()), $comment.val()], async (date, weight, comment) => { | ||||
|                 const dataNote = await this.getNoteWithAttribute('date_data', date); | ||||
| 
 | ||||
|                 if (dataNote) { | ||||
|  | ||||
| @ -13,7 +13,7 @@ const BUILTIN_ATTRIBUTES = [ | ||||
| ]; | ||||
| 
 | ||||
| async function getNoteAttributeMap(noteId) { | ||||
|     return await sql.getMap(`SELECT name, value FROM attributes WHERE noteId = ?`, [noteId]); | ||||
|     return await sql.getMap(`SELECT name, value FROM attributes WHERE noteId = ? AND isDeleted = 0`, [noteId]); | ||||
| } | ||||
| 
 | ||||
| async function getNoteIdWithAttribute(name, value) { | ||||
|  | ||||
| @ -214,7 +214,7 @@ async function runAllChecks() { | ||||
|           FROM  | ||||
|             notes | ||||
|           WHERE  | ||||
|             type != 'text' AND type != 'code' AND type != 'render'`,
 | ||||
|             type != 'text' AND type != 'code' AND type != 'render' AND type != 'file'`,
 | ||||
|         "Note has invalid type", errorList); | ||||
| 
 | ||||
|     await runSyncRowChecks("notes", "noteId", errorList); | ||||
|  | ||||
| @ -105,7 +105,7 @@ | ||||
|                   onclick="noteEditor.executeCurrentNote()">Execute <kbd>Ctrl+Enter</kbd></button> | ||||
| 
 | ||||
|           <div class="dropdown" id="note-type"> | ||||
|             <button id="dLabel" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-sm"> | ||||
|             <button data-bind="disable: isDisabled()" id="dLabel" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-sm"> | ||||
|               Type: <span data-bind="text: typeString()"></span> | ||||
|               <span class="caret"></span> | ||||
|             </button> | ||||
| @ -144,8 +144,25 @@ | ||||
|         <div id="note-detail-render"></div> | ||||
| 
 | ||||
|         <div id="note-detail-attachment"> | ||||
|           Attachment!!! | ||||
| 
 | ||||
|           <table id="attachment-table"> | ||||
|             <tr> | ||||
|               <th>File name:</th> | ||||
|               <td id="attachment-filename"></td> | ||||
|             </tr> | ||||
|             <tr> | ||||
|               <th>File type:</th> | ||||
|               <td id="attachment-filetype"></td> | ||||
|             </tr> | ||||
|             <tr> | ||||
|               <th>File size:</th> | ||||
|               <td id="attachment-filesize"></td> | ||||
|             </tr> | ||||
|             <tr> | ||||
|               <td> | ||||
|                 <button id="attachment-download" class="btn btn-primary" type="button">Download</button> | ||||
|               </td> | ||||
|             </tr> | ||||
|           </table> | ||||
|         </div> | ||||
| 
 | ||||
|         <input type="file" id="file-upload" style="display: none" /> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 azivner
						azivner