mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-03 20:06:08 +01:00 
			
		
		
		
	Allow users to use their own share template
This commit is contained in:
		@@ -44,7 +44,8 @@ function renderIndex(result) {
 | 
				
			|||||||
    const rootNote = shaca.getNote(shareRoot.SHARE_ROOT_NOTE_ID);
 | 
					    const rootNote = shaca.getNote(shareRoot.SHARE_ROOT_NOTE_ID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (const childNote of rootNote.getChildNotes()) {
 | 
					    for (const childNote of rootNote.getChildNotes()) {
 | 
				
			||||||
        result.content += `<li><a class="${childNote.type}" href="./${childNote.shareId}">${childNote.escapedTitle}</a></li>`;
 | 
					        const href = childNote.getLabelValue("shareExternal") ?? `./${childNote.shareId}`;
 | 
				
			||||||
 | 
					        result.content += `<li><a class="${childNote.type}" href="${href}">${childNote.escapedTitle}</a></li>`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    result.content += '</ul>';
 | 
					    result.content += '</ul>';
 | 
				
			||||||
@@ -84,7 +85,8 @@ function renderText(result, note) {
 | 
				
			|||||||
                const noteId = notePathSegments[notePathSegments.length - 1];
 | 
					                const noteId = notePathSegments[notePathSegments.length - 1];
 | 
				
			||||||
                const linkedNote = shaca.getNote(noteId);
 | 
					                const linkedNote = shaca.getNote(noteId);
 | 
				
			||||||
                if (linkedNote) {
 | 
					                if (linkedNote) {
 | 
				
			||||||
                    linkEl.setAttribute("href", linkedNote.shareId);
 | 
					                    const href = linkedNote.getLabelValue("shareExternal") ?? `./${linkedNote.shareId}`;
 | 
				
			||||||
 | 
					                    linkEl.setAttribute("href", href);
 | 
				
			||||||
                    linkEl.classList.add(`type-${linkedNote.type}`);
 | 
					                    linkEl.classList.add(`type-${linkedNote.type}`);
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    linkEl.removeAttribute("href");
 | 
					                    linkEl.removeAttribute("href");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
const express = require('express');
 | 
					const express = require('express');
 | 
				
			||||||
const path = require('path');
 | 
					const path = require('path');
 | 
				
			||||||
const safeCompare = require('safe-compare');
 | 
					const safeCompare = require('safe-compare');
 | 
				
			||||||
 | 
					const ejs = require("ejs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const shaca = require("./shaca/shaca");
 | 
					const shaca = require("./shaca/shaca");
 | 
				
			||||||
const shacaLoader = require("./shaca/shaca_loader");
 | 
					const shacaLoader = require("./shaca/shaca_loader");
 | 
				
			||||||
@@ -8,6 +9,9 @@ const shareRoot = require("./share_root");
 | 
				
			|||||||
const contentRenderer = require("./content_renderer");
 | 
					const contentRenderer = require("./content_renderer");
 | 
				
			||||||
const assetPath = require("../services/asset_path");
 | 
					const assetPath = require("../services/asset_path");
 | 
				
			||||||
const appPath = require("../services/app_path");
 | 
					const appPath = require("../services/app_path");
 | 
				
			||||||
 | 
					const searchService = require("../services/search/services/search");
 | 
				
			||||||
 | 
					const SearchContext = require("../services/search/search_context");
 | 
				
			||||||
 | 
					const log = require("../services/log");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @param {SNote} note
 | 
					 * @param {SNote} note
 | 
				
			||||||
@@ -128,18 +132,42 @@ function register(router) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const {header, content, isEmpty} = contentRenderer.getContent(note);
 | 
					        const {header, content, isEmpty} = contentRenderer.getContent(note);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        const subRoot = getSharedSubTreeRoot(note);
 | 
					        const subRoot = getSharedSubTreeRoot(note);
 | 
				
			||||||
 | 
					        const opts = {note, header, content, isEmpty, subRoot, assetPath, appPath};
 | 
				
			||||||
 | 
					        let useDefaultView = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        res.render("share/page", {
 | 
					        // Check if the user has their own template
 | 
				
			||||||
            note,
 | 
					        if (note.hasRelation('shareTemplate')) {
 | 
				
			||||||
            header,
 | 
					            // Get the template note and content
 | 
				
			||||||
            content,
 | 
					            const templateId = note.getRelation('shareTemplate').value;
 | 
				
			||||||
            isEmpty,
 | 
					            const templateNote = shaca.getNote(templateId);
 | 
				
			||||||
            subRoot,
 | 
					
 | 
				
			||||||
            assetPath,
 | 
					            // Make sure the note type is correct
 | 
				
			||||||
            appPath
 | 
					            if (templateNote.type === 'code' && templateNote.mime === 'application/x-ejs') {
 | 
				
			||||||
        });
 | 
					
 | 
				
			||||||
 | 
					                // EJS caches the result of this so we don't need to pre-cache
 | 
				
			||||||
 | 
					                const includer = (path) => {
 | 
				
			||||||
 | 
					                    const childNote = templateNote.children.find(n => path === n.title);
 | 
				
			||||||
 | 
					                    if (!childNote) return null;
 | 
				
			||||||
 | 
					                    if (childNote.type !== 'code' || childNote.mime !== 'application/x-ejs') return null;
 | 
				
			||||||
 | 
					                    return { template: childNote.getContent() };
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Try to render user's template, w/ fallback to default view
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    const ejsResult = ejs.render(templateNote.getContent(), opts, {includer});
 | 
				
			||||||
 | 
					                    res.send(ejsResult);
 | 
				
			||||||
 | 
					                    useDefaultView = false; // Rendering went okay, don't use default view
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                catch (e) {
 | 
				
			||||||
 | 
					                    log.error(`Rendering user provided share template (${templateId}) threw exception ${e.message} with stacktrace: ${e.stack}`);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (useDefaultView) {
 | 
				
			||||||
 | 
					            res.render('share/page', opts);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    router.get('/share/', (req, res, next) => {
 | 
					    router.get('/share/', (req, res, next) => {
 | 
				
			||||||
@@ -303,6 +331,37 @@ function register(router) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        res.send(note.getContent());
 | 
					        res.send(note.getContent());
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Used for searching, require noteId so we know the subTreeRoot
 | 
				
			||||||
 | 
					    router.get('/share/api/search/:noteId', (req, res, next) => {
 | 
				
			||||||
 | 
					        shacaLoader.ensureLoad();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let note;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!(note = checkNoteAccess(req.params.noteId, req, res))) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const {query} = req.query;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!query?.trim()) {
 | 
				
			||||||
 | 
					            return res.status(400).json({ message: "'query' parameter is mandatory." });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const subRootPath = getSharedSubTreeRoot(note);
 | 
				
			||||||
 | 
					        const subRoot = subRootPath.note;
 | 
				
			||||||
 | 
					        const searchContext = new SearchContext({ancestorNoteId: subRoot.noteId});
 | 
				
			||||||
 | 
					        const searchResults = searchService.findResultsWithQuery(query, searchContext);
 | 
				
			||||||
 | 
					        const filteredResults = searchResults.map(sr => {
 | 
				
			||||||
 | 
					            const fullNote = shaca.notes[sr.noteId];
 | 
				
			||||||
 | 
					            const startIndex = sr.notePathArray.indexOf(subRoot.noteId);
 | 
				
			||||||
 | 
					            const localPathArray = sr.notePathArray.slice(startIndex + 1);
 | 
				
			||||||
 | 
					            const pathTitle = localPathArray.map(id => shaca.notes[id].title).join(" / ");
 | 
				
			||||||
 | 
					            return { id: fullNote.noteId, title: fullNote.title, score: sr.score, path: pathTitle };
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        res.json({ results: filteredResults });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user