mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
include note feature
This commit is contained in:
parent
2f711a12f8
commit
0fe91d0184
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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user