mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-04 05:28:59 +01:00 
			
		
		
		
	share support for attachment images
This commit is contained in:
		
							parent
							
								
									69c7eb14aa
								
							
						
					
					
						commit
						44bcfd47c0
					
				@ -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() {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user