mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02: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 $noteIdDisplay = $("#note-id-display");
|
||||||
const $attributeList = $("#attribute-list");
|
const $attributeList = $("#attribute-list");
|
||||||
const $attributeListInner = $("#attribute-list-inner");
|
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 editor = null;
|
||||||
let codeEditor = null;
|
let codeEditor = null;
|
||||||
@ -83,7 +87,7 @@ const noteEditor = (function() {
|
|||||||
else if (note.detail.type === 'code') {
|
else if (note.detail.type === 'code') {
|
||||||
note.detail.content = codeEditor.getValue();
|
note.detail.content = codeEditor.getValue();
|
||||||
}
|
}
|
||||||
else if (note.detail.type === 'render') {
|
else if (note.detail.type === 'render' || note.detail.type === 'file') {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -185,6 +189,10 @@ const noteEditor = (function() {
|
|||||||
}
|
}
|
||||||
else if (currentNote.detail.type === 'file') {
|
else if (currentNote.detail.type === 'file') {
|
||||||
$noteDetailAttachment.show();
|
$noteDetailAttachment.show();
|
||||||
|
|
||||||
|
$attachmentFileName.text(currentNote.attributes.original_file_name);
|
||||||
|
$attachmentFileSize.text(currentNote.attributes.file_size + " bytes");
|
||||||
|
$attachmentFileType.text(currentNote.detail.mime);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
setContent(currentNote.detail.content);
|
setContent(currentNote.detail.content);
|
||||||
@ -237,7 +245,7 @@ const noteEditor = (function() {
|
|||||||
else if (note.detail.type === 'code') {
|
else if (note.detail.type === 'code') {
|
||||||
codeEditor.focus();
|
codeEditor.focus();
|
||||||
}
|
}
|
||||||
else if (note.detail.type === 'render') {
|
else if (note.detail.type === 'render' || note.detail.type === 'file') {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -262,6 +270,10 @@ const noteEditor = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$attachmentDownload.click(() => {
|
||||||
|
window.location.href = "/api/attachments/download/" + getCurrentNoteId();
|
||||||
|
});
|
||||||
|
|
||||||
$(document).ready(() => {
|
$(document).ready(() => {
|
||||||
$noteTitle.on('input', () => {
|
$noteTitle.on('input', () => {
|
||||||
noteChanged();
|
noteChanged();
|
||||||
|
@ -65,11 +65,18 @@ const noteType = (function() {
|
|||||||
else if (type === 'render') {
|
else if (type === 'render') {
|
||||||
return 'Render HTML note';
|
return 'Render HTML note';
|
||||||
}
|
}
|
||||||
|
else if (type === 'file') {
|
||||||
|
return 'Attachment';
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
throwError('Unrecognized type: ' + type);
|
throwError('Unrecognized type: ' + type);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.isDisabled = function() {
|
||||||
|
return self.type() === "file";
|
||||||
|
};
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
const note = noteEditor.getCurrentNote();
|
const note = noteEditor.getCurrentNote();
|
||||||
|
|
||||||
|
@ -116,6 +116,7 @@ const server = (function() {
|
|||||||
put,
|
put,
|
||||||
remove,
|
remove,
|
||||||
exec,
|
exec,
|
||||||
|
ajax,
|
||||||
// don't remove, used from CKEditor image upload!
|
// don't remove, used from CKEditor image upload!
|
||||||
getHeaders
|
getHeaders
|
||||||
}
|
}
|
||||||
|
@ -268,4 +268,9 @@ div.ui-tooltip {
|
|||||||
#attribute-list button {
|
#attribute-list button {
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
margin-right: 5px;
|
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 sql = require('../../services/sql');
|
||||||
const auth = require('../../services/auth');
|
const auth = require('../../services/auth');
|
||||||
const notes = require('../../services/notes');
|
const notes = require('../../services/notes');
|
||||||
|
const attributes = require('../../services/attributes');
|
||||||
const multer = require('multer')();
|
const multer = require('multer')();
|
||||||
const wrap = require('express-promise-wrap').wrap;
|
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 sourceId = req.headers.source_id;
|
||||||
const parentNoteId = req.params.parentNoteId;
|
const parentNoteId = req.params.parentNoteId;
|
||||||
const file = req.file;
|
const file = req.file;
|
||||||
|
const originalName = file.originalname;
|
||||||
|
const size = file.size;
|
||||||
|
|
||||||
const note = await sql.getRow("SELECT * FROM notes WHERE noteId = ?", [parentNoteId]);
|
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.`);
|
return res.status(404).send(`Note ${parentNoteId} doesn't exist.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const noteId = (await notes.createNewNote(parentNoteId, {
|
await sql.doInTransaction(async () => {
|
||||||
title: "attachment",
|
const noteId = (await notes.createNewNote(parentNoteId, {
|
||||||
content: file.buffer,
|
title: originalName,
|
||||||
target: 'into',
|
content: file.buffer,
|
||||||
isProtected: false,
|
target: 'into',
|
||||||
type: 'file',
|
isProtected: false,
|
||||||
mime: ''
|
type: 'file',
|
||||||
})).noteId;
|
mime: file.mimetype
|
||||||
|
}, req, sourceId)).noteId;
|
||||||
|
|
||||||
res.send({
|
await attributes.createAttribute(noteId, "original_file_name", originalName);
|
||||||
noteId: noteId
|
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;
|
module.exports = router;
|
@ -5,6 +5,7 @@ const router = express.Router();
|
|||||||
const auth = require('../../services/auth');
|
const auth = require('../../services/auth');
|
||||||
const sql = require('../../services/sql');
|
const sql = require('../../services/sql');
|
||||||
const notes = require('../../services/notes');
|
const notes = require('../../services/notes');
|
||||||
|
const attributes = require('../../services/attributes');
|
||||||
const log = require('../../services/log');
|
const log = require('../../services/log');
|
||||||
const utils = require('../../services/utils');
|
const utils = require('../../services/utils');
|
||||||
const protected_session = require('../../services/protected_session');
|
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);
|
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({
|
res.send({
|
||||||
detail: detail
|
detail: detail,
|
||||||
|
attributes: attributeMap
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
$date.datepicker('setDate', new Date());
|
$date.datepicker('setDate', new Date());
|
||||||
|
|
||||||
async function saveWeight() {
|
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);
|
const dataNote = await this.getNoteWithAttribute('date_data', date);
|
||||||
|
|
||||||
if (dataNote) {
|
if (dataNote) {
|
||||||
|
@ -13,7 +13,7 @@ const BUILTIN_ATTRIBUTES = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
async function getNoteAttributeMap(noteId) {
|
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) {
|
async function getNoteIdWithAttribute(name, value) {
|
||||||
|
@ -214,7 +214,7 @@ async function runAllChecks() {
|
|||||||
FROM
|
FROM
|
||||||
notes
|
notes
|
||||||
WHERE
|
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);
|
"Note has invalid type", errorList);
|
||||||
|
|
||||||
await runSyncRowChecks("notes", "noteId", errorList);
|
await runSyncRowChecks("notes", "noteId", errorList);
|
||||||
|
@ -105,7 +105,7 @@
|
|||||||
onclick="noteEditor.executeCurrentNote()">Execute <kbd>Ctrl+Enter</kbd></button>
|
onclick="noteEditor.executeCurrentNote()">Execute <kbd>Ctrl+Enter</kbd></button>
|
||||||
|
|
||||||
<div class="dropdown" id="note-type">
|
<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>
|
Type: <span data-bind="text: typeString()"></span>
|
||||||
<span class="caret"></span>
|
<span class="caret"></span>
|
||||||
</button>
|
</button>
|
||||||
@ -144,8 +144,25 @@
|
|||||||
<div id="note-detail-render"></div>
|
<div id="note-detail-render"></div>
|
||||||
|
|
||||||
<div id="note-detail-attachment">
|
<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>
|
</div>
|
||||||
|
|
||||||
<input type="file" id="file-upload" style="display: none" />
|
<input type="file" id="file-upload" style="display: none" />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user