mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	share support for attachment images
This commit is contained in:
		| @@ -93,8 +93,6 @@ class AbstractBeccaEntity { | ||||
|  | ||||
|         const pojo = this.getPojoToSave(); | ||||
|  | ||||
|         console.log(pojo); | ||||
|  | ||||
|         sql.transactional(() => { | ||||
|             sql.upsert(entityName, primaryKeyName, pojo); | ||||
|  | ||||
|   | ||||
| @@ -37,13 +37,30 @@ function requestCredentials(res) { | ||||
|         .sendStatus(401); | ||||
| } | ||||
|  | ||||
| /** @returns {SAttachment|boolean} */ | ||||
| function checkAttachmentAccess(attachmentId, req, res) { | ||||
|     const attachment = shaca.getAttachment(attachmentId); | ||||
|  | ||||
|     if (!attachment) { | ||||
|         res.status(404) | ||||
|             .json({ message: `Attachment '${attachmentId}' not found.` }); | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     const note = checkNoteAccess(attachment.parentId, req, res); | ||||
|  | ||||
|     // truthy note means user has access, and we can return the attachment | ||||
|     return note ? attachment : false; | ||||
| } | ||||
|  | ||||
| /** @returns {SNote|boolean} */ | ||||
| function checkNoteAccess(noteId, req, res) { | ||||
|     const note = shaca.getNote(noteId); | ||||
|  | ||||
|     if (!note) { | ||||
|         res.status(404) | ||||
|             .json({ message: `Note '${noteId}' not found` }); | ||||
|             .json({ message: `Note '${noteId}' not found.` }); | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
| @@ -151,7 +168,7 @@ function register(router) { | ||||
|  | ||||
|         addNoIndexHeader(note, res); | ||||
|  | ||||
|         res.json(note.getPojoWithAttributes()); | ||||
|         res.json(note.getPojo()); | ||||
|     }); | ||||
|  | ||||
|     router.get('/share/api/notes/:noteId/download', (req, res, next) => { | ||||
| @@ -216,6 +233,26 @@ function register(router) { | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     // :filename is not used by trilium, but instead used for "save as" to assign a human-readable filename | ||||
|     router.get('/share/api/attachments/:attachmentId/image/:filename', (req, res, next) => { | ||||
|         shacaLoader.ensureLoad(); | ||||
|  | ||||
|         let attachment; | ||||
|  | ||||
|         if (!(attachment = checkAttachmentAccess(req.params.attachmentId, req, res))) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (attachment.role === "image") { | ||||
|             res.set('Content-Type', attachment.mime); | ||||
|             addNoIndexHeader(attachment.note, res); | ||||
|             res.send(attachment.getContent()); | ||||
|         } else { | ||||
|             return res.status(400) | ||||
|                 .json({ message: "Requested attachment is not a shareable image" }); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     // used for PDF viewing | ||||
|     router.get('/share/api/notes/:noteId/view', (req, res, next) => { | ||||
|         shacaLoader.ensureLoad(); | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| let shaca; | ||||
|  | ||||
| class AbstractShacaEntity { | ||||
|     /** @return {Shaca} */ | ||||
|     get shaca() { | ||||
|         if (!shaca) { | ||||
|             shaca = require("../shaca"); | ||||
|   | ||||
							
								
								
									
										77
									
								
								src/share/shaca/entities/sattachment.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/share/shaca/entities/sattachment.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const sql = require('../../sql'); | ||||
| const utils = require('../../../services/utils'); | ||||
| const AbstractShacaEntity = require('./abstract_shaca_entity'); | ||||
|  | ||||
| class SAttachment extends AbstractShacaEntity { | ||||
|     constructor([attachmentId, parentId, role, mime, title, blobId, utcDateModified]) { | ||||
|         super(); | ||||
|  | ||||
|         /** @param {string} */ | ||||
|         this.attachmentId = attachmentId; | ||||
|         /** @param {string} */ | ||||
|         this.parentId = parentId; | ||||
|         /** @param {string} */ | ||||
|         this.title = title; | ||||
|         /** @param {string} */ | ||||
|         this.role = role; | ||||
|         /** @param {string} */ | ||||
|         this.mime = mime; | ||||
|         /** @param {string} */ | ||||
|         this.blobId = blobId; | ||||
|         /** @param {string} */ | ||||
|         this.utcDateModified = utcDateModified; // used for caching of images | ||||
|  | ||||
|         this.shaca.attachments[this.attachmentId] = this; | ||||
|         this.shaca.notes[this.parentId].attachments.push(this); | ||||
|     } | ||||
|  | ||||
|     /** @returns {SNote} */ | ||||
|     get note() { | ||||
|         return this.shaca.notes[this.parentId]; | ||||
|     } | ||||
|  | ||||
|     getContent(silentNotFoundError = false) { | ||||
|         const row = sql.getRow(`SELECT content FROM blobs WHERE blobId = ?`, [this.blobId]); | ||||
|  | ||||
|         if (!row) { | ||||
|             if (silentNotFoundError) { | ||||
|                 return undefined; | ||||
|             } | ||||
|             else { | ||||
|                 throw new Error(`Cannot find blob for attachment '${this.attachmentId}', blob '${this.blobId}'`); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         let content = row.content; | ||||
|  | ||||
|         if (this.hasStringContent()) { | ||||
|             return content === null | ||||
|                 ? "" | ||||
|                 : content.toString("utf-8"); | ||||
|         } | ||||
|         else { | ||||
|             return content; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** @returns {boolean} true if the attachment has string content (not binary) */ | ||||
|     hasStringContent() { | ||||
|         return utils.isStringNote(null, this.mime); | ||||
|     } | ||||
|  | ||||
|     getPojo() { | ||||
|         return { | ||||
|             attachmentId: this.attachmentId, | ||||
|             role: this.role, | ||||
|             mime: this.mime, | ||||
|             title: this.title, | ||||
|             position: this.position, | ||||
|             blobId: this.blobId, | ||||
|             utcDateModified: this.utcDateModified | ||||
|         }; | ||||
|     } | ||||
| } | ||||
|  | ||||
| module.exports = SAttachment; | ||||
| @@ -69,7 +69,7 @@ class SAttribute extends AbstractShacaEntity { | ||||
|         return this.type === 'relation' && ['internalLink', 'imageLink', 'relationMapLink', 'includeNoteLink'].includes(this.name); | ||||
|     } | ||||
|  | ||||
|     /** @returns {SNote|null} */ | ||||
|     /** @returns {SNote} */ | ||||
|     get note() { | ||||
|         return this.shaca.notes[this.noteId]; | ||||
|     } | ||||
|   | ||||
| @@ -47,6 +47,9 @@ class SNote extends AbstractShacaEntity { | ||||
|         /** @param {SAttribute[]} */ | ||||
|         this.targetRelations = []; | ||||
|  | ||||
|         /** @param {SAttachment[]} */ | ||||
|         this.attachments = []; | ||||
|  | ||||
|         this.shaca.notes[this.noteId] = this; | ||||
|     } | ||||
|  | ||||
| @@ -101,7 +104,7 @@ class SNote extends AbstractShacaEntity { | ||||
|                 return undefined; | ||||
|             } | ||||
|             else { | ||||
|                 throw new Error(`Cannot find note content for noteId '${this.noteId}', blobId '${this.blobId}'`); | ||||
|                 throw new Error(`Cannot find note content for note '${this.noteId}', blob '${this.blobId}'`); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -442,6 +445,11 @@ class SNote extends AbstractShacaEntity { | ||||
|         return this.targetRelations; | ||||
|     } | ||||
|  | ||||
|     /** @returns {SAttachment[]} */ | ||||
|     getAttachments() { | ||||
|         return this.attachments; | ||||
|     } | ||||
|  | ||||
|     /** @returns {string} */ | ||||
|     get shareId() { | ||||
|         if (this.hasOwnedLabel('shareRoot')) { | ||||
| @@ -457,7 +465,7 @@ class SNote extends AbstractShacaEntity { | ||||
|         return escape(this.title); | ||||
|     } | ||||
|  | ||||
|     getPojoWithAttributes() { | ||||
|     getPojo() { | ||||
|         return { | ||||
|             noteId: this.noteId, | ||||
|             title: this.title, | ||||
| @@ -469,6 +477,8 @@ class SNote extends AbstractShacaEntity { | ||||
|                 // individual relations might be whitelisted based on needs #3434 | ||||
|                 .filter(attr => attr.type === 'label') | ||||
|                 .map(attr => attr.getPojo()), | ||||
|             attachments: this.getAttachments() | ||||
|                 .map(attachment => attachment.getPojo()), | ||||
|             parentNoteIds: this.parents.map(parentNote => parentNote.noteId), | ||||
|             childNoteIds: this.children.map(child => child.noteId) | ||||
|         }; | ||||
|   | ||||
| @@ -14,6 +14,8 @@ class Shaca { | ||||
|         this.childParentToBranch = {}; | ||||
|         /** @type {Object.<String, SAttribute>} */ | ||||
|         this.attributes = {}; | ||||
|         /** @type {Object.<String, SAttachment>} */ | ||||
|         this.attachments = {}; | ||||
|         /** @type {Object.<String, String>} */ | ||||
|         this.aliasToNote = {}; | ||||
|  | ||||
| @@ -72,6 +74,11 @@ class Shaca { | ||||
|         return this.attributes[attributeId]; | ||||
|     } | ||||
|  | ||||
|     /** @returns {SAttachment|null} */ | ||||
|     getAttachment(attachmentId) { | ||||
|         return this.attachments[attachmentId]; | ||||
|     } | ||||
|  | ||||
|     getEntity(entityName, entityId) { | ||||
|         if (!entityName || !entityId) { | ||||
|             return null; | ||||
|   | ||||
| @@ -6,6 +6,7 @@ const log = require('../../services/log'); | ||||
| const SNote = require('./entities/snote'); | ||||
| const SBranch = require('./entities/sbranch'); | ||||
| const SAttribute = require('./entities/sattribute'); | ||||
| const SAttachment = require("./entities/sattachment"); | ||||
| const shareRoot = require('../share_root'); | ||||
| const eventService = require("../../services/events"); | ||||
|  | ||||
| @@ -65,9 +66,21 @@ function load() { | ||||
|         new SAttribute(row); | ||||
|     } | ||||
|  | ||||
|     const rawAttachmentRows = sql.getRawRows(` | ||||
|         SELECT attachmentId, parentId, role, mime, title, blobId, utcDateModified  | ||||
|         FROM attachments  | ||||
|         WHERE isDeleted = 0  | ||||
|           AND parentId IN (${noteIdStr})`); | ||||
|  | ||||
|     rawAttachmentRows.sort((a, b) => a.position < b.position ? -1 : 1); | ||||
|  | ||||
|     for (const row of rawAttachmentRows) { | ||||
|         new SAttachment(row); | ||||
|     } | ||||
|  | ||||
|     shaca.loaded = true; | ||||
|  | ||||
|     log.info(`Shaca loaded ${rawNoteRows.length} notes, ${rawBranchRows.length} branches, ${rawAttributeRows.length} attributes took ${Date.now() - start}ms`); | ||||
|     log.info(`Shaca loaded ${rawNoteRows.length} notes, ${rawBranchRows.length} branches, ${rawAttachmentRows.length} attributes took ${Date.now() - start}ms`); | ||||
| } | ||||
|  | ||||
| function ensureLoad() { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user