mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-03 20:06:08 +01:00 
			
		
		
		
	include note feature
This commit is contained in:
		
							
								
								
									
										2
									
								
								libraries/ckeditor/ckeditor.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								libraries/ckeditor/ckeditor.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -33,6 +33,7 @@ import importService from './services/import.js';
 | 
				
			|||||||
import keyboardActionService from "./services/keyboard_actions.js";
 | 
					import keyboardActionService from "./services/keyboard_actions.js";
 | 
				
			||||||
import splitService from "./services/split.js";
 | 
					import splitService from "./services/split.js";
 | 
				
			||||||
import optionService from "./services/options.js";
 | 
					import optionService from "./services/options.js";
 | 
				
			||||||
 | 
					import noteContentRenderer from "./services/note_content_renderer.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.glob.isDesktop = utils.isDesktop;
 | 
					window.glob.isDesktop = utils.isDesktop;
 | 
				
			||||||
window.glob.isMobile = utils.isMobile;
 | 
					window.glob.isMobile = utils.isMobile;
 | 
				
			||||||
@@ -42,6 +43,19 @@ window.glob.getActiveNode = treeService.getActiveNode;
 | 
				
			|||||||
window.glob.getHeaders = server.getHeaders;
 | 
					window.glob.getHeaders = server.getHeaders;
 | 
				
			||||||
window.glob.showAddLinkDialog = () => import('./dialogs/add_link.js').then(d => d.showDialog());
 | 
					window.glob.showAddLinkDialog = () => import('./dialogs/add_link.js').then(d => d.showDialog());
 | 
				
			||||||
window.glob.showIncludeNoteDialog = cb => import('./dialogs/include_note.js').then(d => d.showDialog(cb));
 | 
					window.glob.showIncludeNoteDialog = cb => import('./dialogs/include_note.js').then(d => d.showDialog(cb));
 | 
				
			||||||
 | 
					window.glob.loadIncludedNote = async (noteId, el) => {
 | 
				
			||||||
 | 
					    const note = await treeCache.getNote(noteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (note) {
 | 
				
			||||||
 | 
					        $(el).empty().append($("<h3>").append(await linkService.createNoteLink(note.noteId, {
 | 
				
			||||||
 | 
					            showTooltip: false
 | 
				
			||||||
 | 
					        })));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const {renderedContent} = await noteContentRenderer.getRenderedContent(note);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $(el).append(renderedContent);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
// this is required by CKEditor when uploading images
 | 
					// this is required by CKEditor when uploading images
 | 
				
			||||||
window.glob.noteChanged = noteDetailService.noteChanged;
 | 
					window.glob.noteChanged = noteDetailService.noteChanged;
 | 
				
			||||||
window.glob.refreshTree = treeService.reload;
 | 
					window.glob.refreshTree = treeService.reload;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										107
									
								
								src/public/javascripts/services/note_content_renderer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								src/public/javascripts/services/note_content_renderer.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
				
			|||||||
 | 
					import server from "./server.js";
 | 
				
			||||||
 | 
					import utils from "./utils.js";
 | 
				
			||||||
 | 
					import renderService from "./render.js";
 | 
				
			||||||
 | 
					import protectedSessionService from "./protected_session.js";
 | 
				
			||||||
 | 
					import protectedSessionHolder from "./protected_session_holder.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function getRenderedContent(note) {
 | 
				
			||||||
 | 
					    const type = getRenderingType(note);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let rendered;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (type === 'text') {
 | 
				
			||||||
 | 
					        const fullNote = await server.get('notes/' + note.noteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const $content = $("<div>").html(fullNote.content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (utils.isHtmlEmpty(fullNote.content)) {
 | 
				
			||||||
 | 
					            rendered = "";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            rendered = $content;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (type === 'code') {
 | 
				
			||||||
 | 
					        const fullNote = await server.get('notes/' + note.noteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (fullNote.content.trim() === "") {
 | 
				
			||||||
 | 
					            rendered = "";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        rendered = $("<pre>").text(fullNote.content);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (type === 'image') {
 | 
				
			||||||
 | 
					        rendered = $("<img>").attr("src", `api/images/${note.noteId}/${note.title}`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (type === 'file') {
 | 
				
			||||||
 | 
					        function getFileUrl() {
 | 
				
			||||||
 | 
					            return utils.getUrlForDownload("api/notes/" + note.noteId + "/download");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const $downloadButton = $('<button class="file-download btn btn-primary" type="button">Download</button>');
 | 
				
			||||||
 | 
					        const $openButton = $('<button class="file-open btn btn-primary" type="button">Open</button>');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $downloadButton.on('click', () => utils.download(getFileUrl()));
 | 
				
			||||||
 | 
					        $openButton.on('click', () => {
 | 
				
			||||||
 | 
					            if (utils.isElectron()) {
 | 
				
			||||||
 | 
					                const open = require("open");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                open(getFileUrl(), {url: true});
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                window.location.href = getFileUrl();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // open doesn't work for protected notes since it works through browser which isn't in protected session
 | 
				
			||||||
 | 
					        $openButton.toggle(!note.isProtected);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        rendered = $('<div>')
 | 
				
			||||||
 | 
					            .append($downloadButton)
 | 
				
			||||||
 | 
					            .append('   ')
 | 
				
			||||||
 | 
					            .append($openButton);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (type === 'render') {
 | 
				
			||||||
 | 
					        const $el = $('<div>');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await renderService.render(note, $el, this.ctx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        rendered = $el;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (type === 'protected-session') {
 | 
				
			||||||
 | 
					        const $button = $(`<button class="btn btn-sm"><span class="bx bx-log-in"></span> Enter protected session</button>`)
 | 
				
			||||||
 | 
					            .on('click', protectedSessionService.enterProtectedSession);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        rendered = $("<div>")
 | 
				
			||||||
 | 
					            .append("<div>This note is protected and to access it you need to enter password.</div>")
 | 
				
			||||||
 | 
					            .append("<br/>")
 | 
				
			||||||
 | 
					            .append($button);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					        rendered = "<em>Content of this note cannot be displayed in the book format</em>";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					        renderedContent: rendered,
 | 
				
			||||||
 | 
					        type
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getRenderingType(note) {
 | 
				
			||||||
 | 
					    let type = note.type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (note.isProtected) {
 | 
				
			||||||
 | 
					        if (protectedSessionHolder.isProtectedSessionAvailable()) {
 | 
				
			||||||
 | 
					            protectedSessionHolder.touchProtectedSession();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            type = 'protected-session';
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return type;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					    getRenderedContent
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -1,10 +1,6 @@
 | 
				
			|||||||
import server from "./server.js";
 | 
					 | 
				
			||||||
import linkService from "./link.js";
 | 
					import linkService from "./link.js";
 | 
				
			||||||
import utils from "./utils.js";
 | 
					 | 
				
			||||||
import treeCache from "./tree_cache.js";
 | 
					import treeCache from "./tree_cache.js";
 | 
				
			||||||
import renderService from "./render.js";
 | 
					import noteContentRenderer from "./note_content_renderer.js";
 | 
				
			||||||
import protectedSessionHolder from "./protected_session_holder.js";
 | 
					 | 
				
			||||||
import protectedSessionService from "./protected_session.js";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const MIN_ZOOM_LEVEL = 1;
 | 
					const MIN_ZOOM_LEVEL = 1;
 | 
				
			||||||
const MAX_ZOOM_LEVEL = 6;
 | 
					const MAX_ZOOM_LEVEL = 6;
 | 
				
			||||||
@@ -129,10 +125,10 @@ class NoteDetailBook {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    async renderIntoElement(note, $container) {
 | 
					    async renderIntoElement(note, $container) {
 | 
				
			||||||
        for (const childNote of await note.getChildNotes()) {
 | 
					        for (const childNote of await note.getChildNotes()) {
 | 
				
			||||||
            const type = this.getRenderingType(childNote);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            const childNotePath = this.ctx.notePath + '/' + childNote.noteId;
 | 
					            const childNotePath = this.ctx.notePath + '/' + childNote.noteId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const {type, renderedContent} = await noteContentRenderer.getRenderedContent(childNote);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const $card = $('<div class="note-book-card">')
 | 
					            const $card = $('<div class="note-book-card">')
 | 
				
			||||||
                .attr('data-note-id', childNote.noteId)
 | 
					                .attr('data-note-id', childNote.noteId)
 | 
				
			||||||
                .css("flex-basis", ZOOMS[this.zoomLevel].width)
 | 
					                .css("flex-basis", ZOOMS[this.zoomLevel].width)
 | 
				
			||||||
@@ -140,7 +136,7 @@ class NoteDetailBook {
 | 
				
			|||||||
                .append($('<h5 class="note-book-title">').append(await linkService.createNoteLink(childNotePath,  {showTooltip: false})))
 | 
					                .append($('<h5 class="note-book-title">').append(await linkService.createNoteLink(childNotePath,  {showTooltip: false})))
 | 
				
			||||||
                .append($('<div class="note-book-content">')
 | 
					                .append($('<div class="note-book-content">')
 | 
				
			||||||
                    .css("max-height", ZOOMS[this.zoomLevel].height)
 | 
					                    .css("max-height", ZOOMS[this.zoomLevel].height)
 | 
				
			||||||
                    .append(await this.getNoteContent(type, childNote)));
 | 
					                    .append(renderedContent));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const childCount = childNote.getChildNoteIds().length;
 | 
					            const childCount = childNote.getChildNoteIds().length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -158,80 +154,6 @@ class NoteDetailBook {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async getNoteContent(type, note) {
 | 
					 | 
				
			||||||
        if (type === 'text') {
 | 
					 | 
				
			||||||
            const fullNote = await server.get('notes/' + note.noteId);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            const $content = $("<div>").html(fullNote.content);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (utils.isHtmlEmpty(fullNote.content)) {
 | 
					 | 
				
			||||||
                return "";
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else {
 | 
					 | 
				
			||||||
                return $content;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (type === 'code') {
 | 
					 | 
				
			||||||
            const fullNote = await server.get('notes/' + note.noteId);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (fullNote.content.trim() === "") {
 | 
					 | 
				
			||||||
                return "";
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return $("<pre>").text(fullNote.content);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (type === 'image') {
 | 
					 | 
				
			||||||
            return $("<img>").attr("src", `api/images/${note.noteId}/${note.title}`);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (type === 'file') {
 | 
					 | 
				
			||||||
            function getFileUrl() {
 | 
					 | 
				
			||||||
                return utils.getUrlForDownload("api/notes/" + note.noteId + "/download");
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            const $downloadButton = $('<button class="file-download btn btn-primary" type="button">Download</button>');
 | 
					 | 
				
			||||||
            const $openButton = $('<button class="file-open btn btn-primary" type="button">Open</button>');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            $downloadButton.on('click', () => utils.download(getFileUrl()));
 | 
					 | 
				
			||||||
            $openButton.on('click', () => {
 | 
					 | 
				
			||||||
                if (utils.isElectron()) {
 | 
					 | 
				
			||||||
                    const open = require("open");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    open(getFileUrl(), {url: true});
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else {
 | 
					 | 
				
			||||||
                    window.location.href = getFileUrl();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // open doesn't work for protected notes since it works through browser which isn't in protected session
 | 
					 | 
				
			||||||
            $openButton.toggle(!note.isProtected);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return $('<div>')
 | 
					 | 
				
			||||||
                .append($downloadButton)
 | 
					 | 
				
			||||||
                .append('   ')
 | 
					 | 
				
			||||||
                .append($openButton);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (type === 'render') {
 | 
					 | 
				
			||||||
            const $el = $('<div>');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            await renderService.render(note, $el, this.ctx);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return $el;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (type === 'protected-session') {
 | 
					 | 
				
			||||||
            const $button = $(`<button class="btn btn-sm"><span class="bx bx-log-in"></span> Enter protected session</button>`)
 | 
					 | 
				
			||||||
                .on('click', protectedSessionService.enterProtectedSession);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return $("<div>")
 | 
					 | 
				
			||||||
                .append("<div>This note is protected and to access it you need to enter password.</div>")
 | 
					 | 
				
			||||||
                .append("<br/>")
 | 
					 | 
				
			||||||
                .append($button);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else {
 | 
					 | 
				
			||||||
            return "<em>Content of this note cannot be displayed in the book format</em>";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /** @return {boolean} true if this is "auto book" activated (empty text note) and not explicit book note */
 | 
					    /** @return {boolean} true if this is "auto book" activated (empty text note) and not explicit book note */
 | 
				
			||||||
    isAutoBook() {
 | 
					    isAutoBook() {
 | 
				
			||||||
        return this.ctx.note.type !== 'book';
 | 
					        return this.ctx.note.type !== 'book';
 | 
				
			||||||
@@ -256,21 +178,6 @@ class NoteDetailBook {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getRenderingType(childNote) {
 | 
					 | 
				
			||||||
        let type = childNote.type;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (childNote.isProtected) {
 | 
					 | 
				
			||||||
            if (protectedSessionHolder.isProtectedSessionAvailable()) {
 | 
					 | 
				
			||||||
                protectedSessionHolder.touchProtectedSession();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else {
 | 
					 | 
				
			||||||
                type = 'protected-session';
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return type;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    getContent() {
 | 
					    getContent() {
 | 
				
			||||||
        // for auto-book cases when renaming title there should be content
 | 
					        // for auto-book cases when renaming title there should be content
 | 
				
			||||||
        return "";
 | 
					        return "";
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ import libraryLoader from "./library_loader.js";
 | 
				
			|||||||
import treeService from './tree.js';
 | 
					import treeService from './tree.js';
 | 
				
			||||||
import noteAutocompleteService from './note_autocomplete.js';
 | 
					import noteAutocompleteService from './note_autocomplete.js';
 | 
				
			||||||
import mimeTypesService from './mime_types.js';
 | 
					import mimeTypesService from './mime_types.js';
 | 
				
			||||||
 | 
					import treeCache from "./tree_cache.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mentionSetup = {
 | 
					const mentionSetup = {
 | 
				
			||||||
    feeds: [
 | 
					    feeds: [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -960,4 +960,11 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
 | 
				
			|||||||
.ck-link-actions .ck-tooltip {
 | 
					.ck-link-actions .ck-tooltip {
 | 
				
			||||||
    /* force hide the tooltip since it shows misleading "open link in new tab */
 | 
					    /* force hide the tooltip since it shows misleading "open link in new tab */
 | 
				
			||||||
    display: none !important;
 | 
					    display: none !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.include-note {
 | 
				
			||||||
 | 
					    margin: 20px;
 | 
				
			||||||
 | 
					    padding: 20px;
 | 
				
			||||||
 | 
					    border-radius: 10px;
 | 
				
			||||||
 | 
					    background-color: var(--accented-background-color);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -63,7 +63,8 @@ async function getNotes(noteIds) {
 | 
				
			|||||||
        SELECT 
 | 
					        SELECT 
 | 
				
			||||||
          noteId,
 | 
					          noteId,
 | 
				
			||||||
          title,
 | 
					          title,
 | 
				
			||||||
          isProtected, 
 | 
					          contentLength,
 | 
				
			||||||
 | 
					          isProtected,
 | 
				
			||||||
          type,
 | 
					          type,
 | 
				
			||||||
          mime,
 | 
					          mime,
 | 
				
			||||||
          isDeleted
 | 
					          isDeleted
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user