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

View File

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

View File

@ -40,7 +40,7 @@ async function createNote(parentNotePath, options = {}) {
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,
content: options.content || "",
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",
"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.",
"shareRoot": "marks note which is served on /share root.",
},
"relation": {
"runOnNoteCreation": "executes when note is created on backend",

View File

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

View File

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

View File

@ -5,72 +5,10 @@ const sql = require('./sql');
const becca = require('../becca/becca');
const Attribute = require('../becca/entities/attribute');
const {formatAttrForSearch} = require("./attribute_formatter");
const BUILTIN_ATTRIBUTES = require("./builtin_attributes");
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[]} */
function getNotesWithLabel(name, value) {
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) {
router.get('/share/:shareId', (req, res, next) => {
const {shareId} = req.params;
shacaLoader.ensureLoad();
const note = shaca.aliasToNote[shareId] || shaca.notes[shareId];
function renderNote(note, res) {
if (note) {
const {header, content, isEmpty} = contentRenderer.getContent(note);
@ -40,10 +34,25 @@ function register(router) {
isEmpty,
subRoot
});
}
else {
} else {
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) => {

View File

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

View File

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

View File

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