diff --git a/src/public/app/widgets/attribute_widgets/attribute_detail.js b/src/public/app/widgets/attribute_widgets/attribute_detail.js
index 388c1f8ce..3bc673ba9 100644
--- a/src/public/app/widgets/attribute_widgets/attribute_detail.js
+++ b/src/public/app/widgets/attribute_widgets/attribute_detail.js
@@ -210,7 +210,9 @@ const ATTR_HELP = {
"hoistedInbox": "default inbox location for new notes when hoisted to some ancestor of this note",
"sqlConsoleHome": "default location of SQL console notes",
"bookmarked": "note with this label will appear in bookmarks",
- "bookmarkFolder": "note with this label will appear in bookmarks as folder (allowing access to its children)"
+ "bookmarkFolder": "note with this label will appear in bookmarks as folder (allowing access to its children)",
+ "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]",
},
"relation": {
"runOnNoteCreation": "executes when note is created on backend",
@@ -221,7 +223,8 @@ const ATTR_HELP = {
"runOnAttributeChange": "executes when attribute is changed under this note",
"template": "attached note's attributes will be inherited even without parent-child relationship. See template for details.",
"renderNote": 'notes of type "render HTML note" will be rendered using a code note (HTML or script) and it is necessary to point using this relation to which note should be rendered',
- "widget": "target of this relation will be executed and rendered as a widget in the sidebar"
+ "widget": "target of this relation will be executed and rendered as a widget in the sidebar",
+ "shareCss": "CSS note which will be injected into the share page. CSS note must be in the shared sub-tree as well. Consider using 'shareHiddenFromTree' label to hide it.",
}
};
diff --git a/src/public/app/widgets/shared_info.js b/src/public/app/widgets/shared_info.js
index 3a8b19c20..646b3c4f5 100644
--- a/src/public/app/widgets/shared_info.js
+++ b/src/public/app/widgets/shared_info.js
@@ -1,5 +1,6 @@
import NoteContextAwareWidget from "./note_context_aware_widget.js";
import options from "../services/options.js";
+import attributeService from "../services/attributes.js";
const TPL = `
@@ -31,15 +32,23 @@ export default class SharedInfoWidget extends NoteContextAwareWidget {
const syncServerHost = options.get("syncServerHost");
let link;
+ const shareId = note.getOwnedLabelValue('shareAlias') || note.noteId;
+
if (syncServerHost) {
- link = syncServerHost + "/share/" + note.noteId;
+ link = syncServerHost + "/share/" + shareId;
this.$shareText.text("This note is shared publicly on");
}
else {
- link = location.protocol + '//' + location.host + location.pathname + "share/" + note.noteId;
+ link = location.protocol + '//' + location.host + location.pathname + "share/" + shareId;
this.$shareText.text("This note is shared locally on");
}
this.$shareLink.attr("href", link).text(link);
}
+
+ entitiesReloadedEvent({loadResults}) {
+ if (loadResults.getAttributes().find(attr => attr.name.startsWith("share") && attributeService.isAffecting(attr, this.note))) {
+ this.refresh();
+ }
+ }
}
diff --git a/src/services/attributes.js b/src/services/attributes.js
index 6ace1af13..be538bf65 100644
--- a/src/services/attributes.js
+++ b/src/services/attributes.js
@@ -53,6 +53,7 @@ const BUILTIN_ATTRIBUTES = [
{ type: 'label', name: 'top' },
{ type: 'label', name: 'fullContentWidth' },
{ type: 'label', name: 'shareHiddenFromTree' },
+ { type: 'label', name: 'shareAlias' },
// relation names
{ type: 'relation', name: 'runOnNoteCreation', isDangerous: true },
diff --git a/src/share/content_renderer.js b/src/share/content_renderer.js
index 8fe406d53..576b83beb 100644
--- a/src/share/content_renderer.js
+++ b/src/share/content_renderer.js
@@ -1,5 +1,6 @@
const {JSDOM} = require("jsdom");
const NO_CONTENT = '
This note has no content.
';
+const shaca = require("./shaca/shaca");
function getChildrenList(note) {
if (note.hasChildren()) {
@@ -43,7 +44,15 @@ function getContent(note) {
if (href?.startsWith("#")) {
const notePathSegments = href.split("/");
- linkEl.setAttribute("href", notePathSegments[notePathSegments.length - 1]);
+ const noteId = notePathSegments[notePathSegments.length - 1];
+ const linkedNote = shaca.getNote(noteId);
+
+ if (linkedNote) {
+ linkEl.setAttribute("href", linkedNote.shareId);
+ }
+ else {
+ linkEl.removeAttribute("href");
+ }
}
}
diff --git a/src/share/routes.js b/src/share/routes.js
index 1dcaa4c6c..71b7adae7 100644
--- a/src/share/routes.js
+++ b/src/share/routes.js
@@ -18,14 +18,14 @@ function getSubRoot(note) {
}
function register(router) {
- router.get('/share/:noteId', (req, res, next) => {
- const {noteId} = req.params;
+ router.get('/share/:shareId', (req, res, next) => {
+ const {shareId} = req.params;
shacaLoader.ensureLoad();
- if (noteId in shaca.notes) {
- const note = shaca.notes[noteId];
+ const note = shaca.aliasToNote[shareId] || shaca.notes[shareId];
+ if (note) {
const content = contentRenderer.getContent(note);
const subRoot = getSubRoot(note);
diff --git a/src/share/shaca/entities/attribute.js b/src/share/shaca/entities/attribute.js
index 8aeba46c5..cf71258a4 100644
--- a/src/share/shaca/entities/attribute.js
+++ b/src/share/shaca/entities/attribute.js
@@ -39,6 +39,10 @@ class Attribute extends AbstractEntity {
linkedChildNote.parents = linkedChildNote.parents.filter(parentNote => parentNote.noteId !== this.noteId);
}
}
+
+ if (this.type === 'label' && this.name === 'shareAlias' && this.value.trim()) {
+ this.shaca.aliasToNote[this.value.trim()] = this.note;
+ }
}
get isAffectingSubtree() {
diff --git a/src/share/shaca/entities/note.js b/src/share/shaca/entities/note.js
index 46cfdedc1..1973a499b 100644
--- a/src/share/shaca/entities/note.js
+++ b/src/share/shaca/entities/note.js
@@ -549,6 +549,12 @@ class Note extends AbstractEntity {
return notePaths.some(path => path.includes(ancestorNoteId));
}
+
+ get shareId() {
+ const sharedAlias = this.getOwnedLabelValue("shareAlias");
+
+ return sharedAlias || this.noteId;
+ }
}
module.exports = Note;
diff --git a/src/share/shaca/shaca.js b/src/share/shaca/shaca.js
index 6ffaeb936..4ae3ce166 100644
--- a/src/share/shaca/shaca.js
+++ b/src/share/shaca/shaca.js
@@ -14,6 +14,8 @@ class Shaca {
this.childParentToBranch = {};
/** @type {Object.
} */
this.attributes = {};
+ /** @type {Object.} */
+ this.aliasToNote = {};
this.loaded = false;
}
@@ -22,6 +24,10 @@ class Shaca {
return this.notes[noteId];
}
+ hasNote(noteId) {
+ return noteId in this.notes;
+ }
+
getNotes(noteIds, ignoreMissing = false) {
const filteredNotes = [];
diff --git a/src/share/shaca/shaca_loader.js b/src/share/shaca/shaca_loader.js
index c98c15b9c..f240b2524 100644
--- a/src/share/shaca/shaca_loader.js
+++ b/src/share/shaca/shaca_loader.js
@@ -48,7 +48,7 @@ function load() {
WHERE isDeleted = 0
AND noteId IN (${noteIdStr})
AND (
- (type = 'label' AND name IN ('archived', 'shareHiddenFromTree'))
+ (type = 'label' AND name IN ('archived', 'shareHiddenFromTree', 'shareAlias'))
OR (type = 'relation' AND name IN ('imageLink', 'template', 'shareCss'))
)`, []);
diff --git a/src/views/share/tree_item.ejs b/src/views/share/tree_item.ejs
index b01e0efa5..011a25ed3 100644
--- a/src/views/share/tree_item.ejs
+++ b/src/views/share/tree_item.ejs
@@ -2,7 +2,7 @@
<% if (activeNote.noteId === note.noteId) { %>
<%= note.title %>
<% } else { %>
- <%= note.title %>
+ <%= note.title %>
<% } %>