add #shareRoot label to define an "index" note, closes #2567

This commit is contained in:
zadam 2022-01-17 23:13:56 +01:00
parent 1aff3db81f
commit 52b118df7f
12 changed files with 115 additions and 84 deletions

18
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "trilium", "name": "trilium",
"version": "0.49.4", "version": "0.49.5",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "trilium", "name": "trilium",
"version": "0.49.4", "version": "0.49.5",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"dependencies": { "dependencies": {
"@electron/remote": "2.0.1", "@electron/remote": "2.0.1",
@ -62,7 +62,7 @@
"tmp": "0.2.1", "tmp": "0.2.1",
"turndown": "7.1.1", "turndown": "7.1.1",
"unescape": "1.0.1", "unescape": "1.0.1",
"ws": "8.4.0", "ws": "8.4.2",
"yauzl": "2.10.0" "yauzl": "2.10.0"
}, },
"bin": { "bin": {
@ -11045,9 +11045,9 @@
} }
}, },
"node_modules/ws": { "node_modules/ws": {
"version": "8.4.0", "version": "8.4.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.4.0.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.4.2.tgz",
"integrity": "sha512-IHVsKe2pjajSUIl4KYMQOdlyliovpEPquKkqbwswulszzI7r0SfQrxnXdWAEqOlDCLrVSJzo+O1hAwdog2sKSQ==", "integrity": "sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==",
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=10.0.0"
}, },
@ -19881,9 +19881,9 @@
} }
}, },
"ws": { "ws": {
"version": "8.4.0", "version": "8.4.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.4.0.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.4.2.tgz",
"integrity": "sha512-IHVsKe2pjajSUIl4KYMQOdlyliovpEPquKkqbwswulszzI7r0SfQrxnXdWAEqOlDCLrVSJzo+O1hAwdog2sKSQ==", "integrity": "sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==",
"requires": {} "requires": {}
}, },
"xdg-basedir": { "xdg-basedir": {

View File

@ -77,7 +77,7 @@
"tmp": "0.2.1", "tmp": "0.2.1",
"turndown": "7.1.1", "turndown": "7.1.1",
"unescape": "1.0.1", "unescape": "1.0.1",
"ws": "8.4.1", "ws": "8.4.2",
"yauzl": "2.10.0" "yauzl": "2.10.0"
}, },
"devDependencies": { "devDependencies": {

View File

@ -40,7 +40,7 @@ async function createNote(parentNotePath, options = {}) {
C-->D;` C-->D;`
} }
const {note, branch} = await server.post(`notes/${parentNoteId}/children?target=${options.target}&targetBranchId=${options.targetBranchId}`, { const {note, branch} = await server.post(`notes/${parentNoteId}/children?target=${options.target}&targetBranchId=${options.targetBranchId || ""}`, {
title: newNoteName, title: newNoteName,
content: options.content || "", content: options.content || "",
isProtected: options.isProtected, isProtected: options.isProtected,

View File

@ -214,6 +214,7 @@ const ATTR_HELP = {
"shareHiddenFromTree": "this note is hidden from left navigation tree, but still accessible with its URL", "shareHiddenFromTree": "this note is hidden from left navigation tree, but still accessible with its URL",
"shareAlias": "define an alias using which the note will be available under https://your_trilium_host/share/[your_alias]", "shareAlias": "define an alias using which the note will be available under https://your_trilium_host/share/[your_alias]",
"shareOmitDefaultCss": "default share page CSS will be omitted. Use when you make extensive styling changes.", "shareOmitDefaultCss": "default share page CSS will be omitted. Use when you make extensive styling changes.",
"shareRoot": "marks note which is served on /share root.",
}, },
"relation": { "relation": {
"runOnNoteCreation": "executes when note is created on backend", "runOnNoteCreation": "executes when note is created on backend",

View File

@ -617,6 +617,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
/** /**
* @param {Branch} branch * @param {Branch} branch
* @param {boolean} forceLazy
*/ */
prepareNode(branch, forceLazy = false) { prepareNode(branch, forceLazy = false) {
const note = branch.getNoteFromCache(); const note = branch.getNoteFromCache();

View File

@ -32,7 +32,7 @@ export default class SharedInfoWidget extends NoteContextAwareWidget {
const syncServerHost = options.get("syncServerHost"); const syncServerHost = options.get("syncServerHost");
let link; let link;
const shareId = note.getOwnedLabelValue('shareAlias') || note.noteId; const shareId = this.getShareId(note);
if (syncServerHost) { if (syncServerHost) {
link = syncServerHost + "/share/" + shareId; link = syncServerHost + "/share/" + shareId;
@ -46,6 +46,14 @@ export default class SharedInfoWidget extends NoteContextAwareWidget {
this.$sharedLink.attr("href", link).text(link); this.$sharedLink.attr("href", link).text(link);
} }
getShareId(note) {
if (note.hasOwnedLabel('shareRoot')) {
return '';
}
return note.getOwnedLabelValue('shareAlias') || note.noteId;
}
entitiesReloadedEvent({loadResults}) { entitiesReloadedEvent({loadResults}) {
if (loadResults.getAttributes().find(attr => attr.name.startsWith("share") && attributeService.isAffecting(attr, this.note))) { if (loadResults.getAttributes().find(attr => attr.name.startsWith("share") && attributeService.isAffecting(attr, this.note))) {
this.refresh(); this.refresh();

View File

@ -5,72 +5,10 @@ const sql = require('./sql');
const becca = require('../becca/becca'); const becca = require('../becca/becca');
const Attribute = require('../becca/entities/attribute'); const Attribute = require('../becca/entities/attribute');
const {formatAttrForSearch} = require("./attribute_formatter"); const {formatAttrForSearch} = require("./attribute_formatter");
const BUILTIN_ATTRIBUTES = require("./builtin_attributes");
const ATTRIBUTE_TYPES = [ 'label', 'relation' ]; const ATTRIBUTE_TYPES = [ 'label', 'relation' ];
const BUILTIN_ATTRIBUTES = [
// label names
{ type: 'label', name: 'inbox' },
{ type: 'label', name: 'disableVersioning' },
{ type: 'label', name: 'calendarRoot' },
{ type: 'label', name: 'archived' },
{ type: 'label', name: 'excludeFromExport' },
{ type: 'label', name: 'disableInclusion' },
{ type: 'label', name: 'appCss' },
{ type: 'label', name: 'appTheme' },
{ type: 'label', name: 'hidePromotedAttributes' },
{ type: 'label', name: 'readOnly' },
{ type: 'label', name: 'autoReadOnlyDisabled' },
{ type: 'label', name: 'hoistedCssClass' },
{ type: 'label', name: 'cssClass' },
{ type: 'label', name: 'iconClass' },
{ type: 'label', name: 'keyboardShortcut' },
{ type: 'label', name: 'run', isDangerous: true },
{ type: 'label', name: 'runOnInstance', isDangerous: false },
{ type: 'label', name: 'runAtHour', isDangerous: false },
{ type: 'label', name: 'customRequestHandler', isDangerous: true },
{ type: 'label', name: 'customResourceProvider', isDangerous: true },
{ type: 'label', name: 'widget', isDangerous: true },
{ type: 'label', name: 'noteInfoWidgetDisabled' },
{ type: 'label', name: 'linkMapWidgetDisabled' },
{ type: 'label', name: 'noteRevisionsWidgetDisabled' },
{ type: 'label', name: 'whatLinksHereWidgetDisabled' },
{ type: 'label', name: 'similarNotesWidgetDisabled' },
{ type: 'label', name: 'workspace' },
{ type: 'label', name: 'workspaceIconClass' },
{ type: 'label', name: 'workspaceTabBackgroundColor' },
{ type: 'label', name: 'searchHome' },
{ type: 'label', name: 'hoistedInbox' },
{ type: 'label', name: 'hoistedSearchHome' },
{ type: 'label', name: 'sqlConsoleHome' },
{ type: 'label', name: 'datePattern' },
{ type: 'label', name: 'pageSize' },
{ type: 'label', name: 'viewType' },
{ type: 'label', name: 'mapRootNoteId' },
{ type: 'label', name: 'bookmarked' },
{ type: 'label', name: 'bookmarkFolder' },
{ type: 'label', name: 'sorted' },
{ type: 'label', name: 'top' },
{ type: 'label', name: 'fullContentWidth' },
{ type: 'label', name: 'shareHiddenFromTree' },
{ type: 'label', name: 'shareAlias' },
{ type: 'label', name: 'shareOmitDefaultCss' },
// relation names
{ type: 'relation', name: 'runOnNoteCreation', isDangerous: true },
{ type: 'relation', name: 'runOnNoteTitleChange', isDangerous: true },
{ type: 'relation', name: 'runOnNoteChange', isDangerous: true },
{ type: 'relation', name: 'runOnChildNoteCreation', isDangerous: true },
{ type: 'relation', name: 'runOnAttributeCreation', isDangerous: true },
{ type: 'relation', name: 'runOnAttributeChange', isDangerous: true },
{ type: 'relation', name: 'template' },
{ type: 'relation', name: 'widget', isDangerous: true },
{ type: 'relation', name: 'renderNote', isDangerous: true },
{ type: 'relation', name: 'shareCss', isDangerous: false },
{ type: 'relation', name: 'shareJs', isDangerous: false },
{ type: 'relation', name: 'shareFavicon', isDangerous: false },
];
/** @returns {Note[]} */ /** @returns {Note[]} */
function getNotesWithLabel(name, value) { function getNotesWithLabel(name, value) {
const query = formatAttrForSearch({type: 'label', name, value}, true); const query = formatAttrForSearch({type: 'label', name, value}, true);

View File

@ -0,0 +1,63 @@
module.exports = [
// label names
{ type: 'label', name: 'inbox' },
{ type: 'label', name: 'disableVersioning' },
{ type: 'label', name: 'calendarRoot' },
{ type: 'label', name: 'archived' },
{ type: 'label', name: 'excludeFromExport' },
{ type: 'label', name: 'disableInclusion' },
{ type: 'label', name: 'appCss' },
{ type: 'label', name: 'appTheme' },
{ type: 'label', name: 'hidePromotedAttributes' },
{ type: 'label', name: 'readOnly' },
{ type: 'label', name: 'autoReadOnlyDisabled' },
{ type: 'label', name: 'hoistedCssClass' },
{ type: 'label', name: 'cssClass' },
{ type: 'label', name: 'iconClass' },
{ type: 'label', name: 'keyboardShortcut' },
{ type: 'label', name: 'run', isDangerous: true },
{ type: 'label', name: 'runOnInstance', isDangerous: false },
{ type: 'label', name: 'runAtHour', isDangerous: false },
{ type: 'label', name: 'customRequestHandler', isDangerous: true },
{ type: 'label', name: 'customResourceProvider', isDangerous: true },
{ type: 'label', name: 'widget', isDangerous: true },
{ type: 'label', name: 'noteInfoWidgetDisabled' },
{ type: 'label', name: 'linkMapWidgetDisabled' },
{ type: 'label', name: 'noteRevisionsWidgetDisabled' },
{ type: 'label', name: 'whatLinksHereWidgetDisabled' },
{ type: 'label', name: 'similarNotesWidgetDisabled' },
{ type: 'label', name: 'workspace' },
{ type: 'label', name: 'workspaceIconClass' },
{ type: 'label', name: 'workspaceTabBackgroundColor' },
{ type: 'label', name: 'searchHome' },
{ type: 'label', name: 'hoistedInbox' },
{ type: 'label', name: 'hoistedSearchHome' },
{ type: 'label', name: 'sqlConsoleHome' },
{ type: 'label', name: 'datePattern' },
{ type: 'label', name: 'pageSize' },
{ type: 'label', name: 'viewType' },
{ type: 'label', name: 'mapRootNoteId' },
{ type: 'label', name: 'bookmarked' },
{ type: 'label', name: 'bookmarkFolder' },
{ type: 'label', name: 'sorted' },
{ type: 'label', name: 'top' },
{ type: 'label', name: 'fullContentWidth' },
{ type: 'label', name: 'shareHiddenFromTree' },
{ type: 'label', name: 'shareAlias' },
{ type: 'label', name: 'shareOmitDefaultCss' },
{ type: 'label', name: 'shareRoot' },
// relation names
{ type: 'relation', name: 'runOnNoteCreation', isDangerous: true },
{ type: 'relation', name: 'runOnNoteTitleChange', isDangerous: true },
{ type: 'relation', name: 'runOnNoteChange', isDangerous: true },
{ type: 'relation', name: 'runOnChildNoteCreation', isDangerous: true },
{ type: 'relation', name: 'runOnAttributeCreation', isDangerous: true },
{ type: 'relation', name: 'runOnAttributeChange', isDangerous: true },
{ type: 'relation', name: 'template' },
{ type: 'relation', name: 'widget', isDangerous: true },
{ type: 'relation', name: 'renderNote', isDangerous: true },
{ type: 'relation', name: 'shareCss', isDangerous: false },
{ type: 'relation', name: 'shareJs', isDangerous: false },
{ type: 'relation', name: 'shareFavicon', isDangerous: false },
];

View File

@ -21,13 +21,7 @@ function getSharedSubTreeRoot(note) {
} }
function register(router) { function register(router) {
router.get('/share/:shareId', (req, res, next) => { function renderNote(note, res) {
const {shareId} = req.params;
shacaLoader.ensureLoad();
const note = shaca.aliasToNote[shareId] || shaca.notes[shareId];
if (note) { if (note) {
const {header, content, isEmpty} = contentRenderer.getContent(note); const {header, content, isEmpty} = contentRenderer.getContent(note);
@ -40,10 +34,25 @@ function register(router) {
isEmpty, isEmpty,
subRoot subRoot
}); });
} } else {
else {
res.status(404).render("share/404"); res.status(404).render("share/404");
} }
}
router.get(['/share', '/share/'], (req, res, next) => {
shacaLoader.ensureLoad();
renderNote(shaca.shareRootNote, res);
});
router.get('/share/:shareId', (req, res, next) => {
const {shareId} = req.params;
shacaLoader.ensureLoad();
const note = shaca.aliasToNote[shareId] || shaca.notes[shareId];
renderNote(note, res);
}); });
router.get('/share/api/notes/:noteId', (req, res, next) => { router.get('/share/api/notes/:noteId', (req, res, next) => {

View File

@ -43,6 +43,10 @@ class Attribute extends AbstractEntity {
if (this.type === 'label' && this.name === 'shareAlias' && this.value.trim()) { if (this.type === 'label' && this.name === 'shareAlias' && this.value.trim()) {
this.shaca.aliasToNote[this.value.trim()] = this.note; this.shaca.aliasToNote[this.value.trim()] = this.note;
} }
if (this.type === 'label' && this.name === 'shareRoot') {
this.shaca.shareRootNote = this.note;
}
} }
get isAffectingSubtree() { get isAffectingSubtree() {

View File

@ -406,6 +406,10 @@ class Note extends AbstractEntity {
} }
get shareId() { get shareId() {
if (this.hasOwnedLabel('shareRoot')) {
return "";
}
const sharedAlias = this.getOwnedLabelValue("shareAlias"); const sharedAlias = this.getOwnedLabelValue("shareAlias");
return sharedAlias || this.noteId; return sharedAlias || this.noteId;

View File

@ -17,6 +17,9 @@ class Shaca {
/** @type {Object.<String, String>} */ /** @type {Object.<String, String>} */
this.aliasToNote = {}; this.aliasToNote = {};
/** @type {Note|null} */
this.shareRootNote = null;
this.loaded = false; this.loaded = false;
} }