Skip to content

Commit 44d94b4

Browse files
author
x1arch
committed
fix share parents; add new attribute #shareExclude; upd readme dev section
1 parent e6810ef commit 44d94b4

File tree

7 files changed

+74
-27
lines changed

7 files changed

+74
-27
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ out-tsc
88

99
# dependencies
1010
node_modules
11+
.pnpm-store
1112

1213
# IDEs and editors
1314
/.idea
@@ -18,6 +19,7 @@ node_modules
1819
*.launch
1920
.settings/
2021
*.sublime-workspace
22+
.devcontainer
2123

2224
# misc
2325
/.sass-cache

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,21 @@ Here's the language coverage we have so far:
146146

147147
### Code
148148

149+
General (OS / docker / podman, etc.) dependencies:
150+
151+
Debian
152+
```
153+
apt update
154+
apt install -y build-essential python3 make g++ libsqlite3-dev
155+
corepack enable
156+
```
157+
158+
Alpine
159+
```
160+
apk add --no-cache build-base python3 python3-dev sqlite-dev
161+
corepack enable
162+
```
163+
149164
Download the repository, install dependencies using `pnpm` and then run the server (available at http://localhost:8080):
150165
```shell
151166
git clone https://github.com/TriliumNext/Trilium.git
@@ -154,6 +169,10 @@ pnpm install
154169
pnpm run server:start
155170
```
156171

172+
> If you faced with some problems, try to delete all `node_modules` and `.pnpm-store` folders, not only from the root, from every directory, like `apps/{app_name}/node_modules`and `/packages/{package_name}/node_modules` and then reinstall it by the `pnpm install`.
173+
174+
Share styles not compiling by default, if you see share page without styles, make `pnpm run server:build` and then run development server.
175+
157176
### Documentation
158177

159178
Download the repository, install dependencies using `pnpm` and then run the environment required to edit the documentation:

apps/server/src/share/content_renderer.ts

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,14 @@ interface Subroot {
3838
branch?: SBranch | BBranch
3939
}
4040

41-
function getSharedSubTreeRoot(note: SNote | BNote | undefined): Subroot {
41+
export function getSharedSubTreeRoot(note: SNote | BNote | undefined, parentId: string | undefined = undefined): Subroot {
4242
if (!note || note.noteId === shareRoot.SHARE_ROOT_NOTE_ID) {
4343
// share root itself is not shared
4444
return {};
4545
}
4646

47-
// every path leads to share root, but which one to choose?
48-
// for the sake of simplicity, URLs are not note paths
49-
const parentBranch = note.getParentBranches()[0];
47+
const parentBranches = note.getParentBranches()
48+
const parentBranch = (parentId ? parentBranches.find((pb: SBranch | BBranch) => pb.parentNoteId === parentId) : undefined) || parentBranches[0];
5049

5150
if (note instanceof BNote) {
5251
return {
@@ -62,7 +61,7 @@ function getSharedSubTreeRoot(note: SNote | BNote | undefined): Subroot {
6261
};
6362
}
6463

65-
return getSharedSubTreeRoot(parentBranch.getParentNote());
64+
return getSharedSubTreeRoot(parentBranch.getParentNote(), parentId);
6665
}
6766

6867
export function renderNoteForExport(note: BNote, parentBranch: BBranch, basePath: string, ancestors: string[]) {
@@ -89,9 +88,10 @@ export function renderNoteForExport(note: BNote, parentBranch: BBranch, basePath
8988
}
9089

9190
export function renderNoteContent(note: SNote) {
92-
const subRoot = getSharedSubTreeRoot(note);
91+
92+
const subRoot = getSharedSubTreeRoot(note, note.parentId);
9393

94-
const ancestors: string[] = [];
94+
const ancestors: string[] = []
9595
let notePointer = note;
9696
while (notePointer.parents[0]?.noteId !== subRoot.note?.noteId) {
9797
const pointerParent = notePointer.parents[0];
@@ -105,23 +105,23 @@ export function renderNoteContent(note: SNote) {
105105
// Determine CSS to load.
106106
const cssToLoad: string[] = [];
107107
if (!note.isLabelTruthy("shareOmitDefaultCss")) {
108-
cssToLoad.push(`assets/styles.css`);
109-
cssToLoad.push(`assets/scripts.css`);
108+
cssToLoad.push(`../assets/styles.css`);
109+
cssToLoad.push(`../assets/scripts.css`);
110110
}
111111
for (const cssRelation of note.getRelations("shareCss")) {
112-
cssToLoad.push(`api/notes/${cssRelation.value}/download`);
112+
cssToLoad.push(`../api/notes/${cssRelation.value}/download`);
113113
}
114114

115115
// Determine JS to load.
116116
const jsToLoad: string[] = [
117-
"assets/scripts.js"
117+
"../assets/scripts.js"
118118
];
119119
for (const jsRelation of note.getRelations("shareJs")) {
120-
jsToLoad.push(`api/notes/${jsRelation.value}/download`);
120+
jsToLoad.push(`../api/notes/${jsRelation.value}/download`);
121121
}
122122

123123
const customLogoId = note.getRelation("shareLogo")?.value;
124-
const logoUrl = customLogoId ? `api/images/${customLogoId}/image.png` : `../${assetUrlFragment}/images/icon-color.svg`;
124+
const logoUrl = customLogoId ? `../api/images/${customLogoId}/image.png` : `../../${assetUrlFragment}/images/icon-color.svg`;
125125

126126
return renderNoteContentInternal(note, {
127127
subRoot,
@@ -131,7 +131,7 @@ export function renderNoteContent(note: SNote) {
131131
logoUrl,
132132
ancestors,
133133
isStatic: false,
134-
faviconUrl: note.hasRelation("shareFavicon") ? `api/notes/${note.getRelationValue("shareFavicon")}/download` : `../favicon.ico`
134+
faviconUrl: note.hasRelation("shareFavicon") ? `../api/notes/${note.getRelationValue("shareFavicon")}/download` : `../../favicon.ico`
135135
});
136136
}
137137

@@ -346,7 +346,7 @@ function handleAttachmentLink(linkEl: HTMLElement, href: string, getNote: (id: s
346346
const attachment = getAttachment(attachmentId);
347347

348348
if (attachment) {
349-
linkEl.setAttribute("href", `api/attachments/${attachmentId}/download`);
349+
linkEl.setAttribute("href", `../api/attachments/${attachmentId}/download`);
350350
linkEl.classList.add(`attachment-link`);
351351
linkEl.classList.add(`role-${attachment.role}`);
352352
linkEl.childNodes.length = 0;
@@ -362,7 +362,7 @@ function handleAttachmentLink(linkEl: HTMLElement, href: string, getNote: (id: s
362362
const linkedNote = getNote(noteId);
363363
if (linkedNote) {
364364
const isExternalLink = linkedNote.hasLabel("shareExternalLink");
365-
const href = isExternalLink ? linkedNote.getLabelValue("shareExternalLink") : `./${linkedNote.shareId}`;
365+
const href = isExternalLink ? linkedNote.getLabelValue("shareExternalLink") : `../${linkedNote.shareId}`;
366366
if (href) {
367367
linkEl.setAttribute("href", href);
368368
}
@@ -397,7 +397,7 @@ function renderMermaid(result: Result, note: SNote | BNote) {
397397
}
398398

399399
result.content = `
400-
<img src="api/images/${note.noteId}/${note.encodedTitle}?${note.utcDateModified}">
400+
<img src="../api/images/${note.noteId}/${note.encodedTitle}?${note.utcDateModified}">
401401
<hr>
402402
<details>
403403
<summary>Chart source</summary>
@@ -406,14 +406,14 @@ function renderMermaid(result: Result, note: SNote | BNote) {
406406
}
407407

408408
function renderImage(result: Result, note: SNote | BNote) {
409-
result.content = `<img src="api/images/${note.noteId}/${note.encodedTitle}?${note.utcDateModified}">`;
409+
result.content = `<img src="../api/images/${note.noteId}/${note.encodedTitle}?${note.utcDateModified}">`;
410410
}
411411

412412
function renderFile(note: SNote | BNote, result: Result) {
413413
if (note.mime === "application/pdf") {
414-
result.content = `<iframe class="pdf-view" src="api/notes/${note.noteId}/view"></iframe>`;
414+
result.content = `<iframe class="pdf-view" src="../api/notes/${note.noteId}/view"></iframe>`;
415415
} else {
416-
result.content = `<button type="button" onclick="location.href='api/notes/${note.noteId}/download'">Download file</button>`;
416+
result.content = `<button type="button" onclick="location.href='../api/notes/${note.noteId}/download'">Download file</button>`;
417417
}
418418
}
419419

apps/server/src/share/routes.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import searchService from "../services/search/services/search.js";
88
import SearchContext from "../services/search/search_context.js";
99
import type SNote from "./shaca/entities/snote.js";
1010
import type SAttachment from "./shaca/entities/sattachment.js";
11-
import { getDefaultTemplatePath, renderNoteContent } from "./content_renderer.js";
11+
import { getDefaultTemplatePath, getSharedSubTreeRoot, renderNoteContent } from "./content_renderer.js";
1212
import utils from "../services/utils.js";
1313

1414
function addNoIndexHeader(note: SNote, res: Response) {
@@ -124,9 +124,14 @@ function register(router: Router) {
124124
return;
125125
}
126126

127+
if (note.isLabelTruthy("shareExclude")) {
128+
res.status(404);
129+
render404(res);
130+
return;
131+
}
132+
127133
if (!checkNoteAccess(note.noteId, req, res)) {
128134
requestCredentials(res);
129-
130135
return;
131136
}
132137

@@ -157,14 +162,29 @@ function register(router: Router) {
157162
renderNote(shaca.shareRootNote, req, res);
158163
});
159164

165+
router.get("/share/:parentShareId/:shareId", (req, res) => {
166+
shacaLoader.ensureLoad();
167+
168+
const { parentShareId, shareId } = req.params;
169+
170+
const note = shareId ? (shaca.aliasToNote[shareId] || shaca.notes[shareId]) : (shaca.aliasToNote[parentShareId] || shaca.notes[parentShareId]);
171+
if (note){
172+
note.parentId = parentShareId
173+
}
174+
175+
renderNote(note, req, res);
176+
});
177+
160178
router.get("/share/:shareId", (req, res) => {
161179
shacaLoader.ensureLoad();
162180

163181
const { shareId } = req.params;
164182

165183
const note = shaca.aliasToNote[shareId] || shaca.notes[shareId];
184+
const parent = getSharedSubTreeRoot(note)
166185

167-
renderNote(note, req, res);
186+
res.redirect(`${parent?.note?.noteId}/${shareId}`)
187+
// renderNote(note, req, res);
168188
});
169189

170190
router.get("/share/api/notes/:noteId", (req, res) => {

apps/server/src/share/shaca/entities/snote.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const isCredentials = (attr: SAttribute) => attr.type === "label" && attr.name =
1919

2020
class SNote extends AbstractShacaEntity {
2121
noteId: string;
22+
parentId?: string | undefined;
2223
title: string;
2324
type: string;
2425
mime: string;
@@ -38,6 +39,7 @@ class SNote extends AbstractShacaEntity {
3839
super();
3940

4041
this.noteId = noteId;
42+
this.parentId = undefined;
4143
this.title = isProtected ? "[protected]" : title;
4244
this.type = type;
4345
this.mime = mime;
@@ -59,6 +61,10 @@ class SNote extends AbstractShacaEntity {
5961
this.shaca.notes[this.noteId] = this;
6062
}
6163

64+
getParentId() {
65+
return this.parentId;
66+
}
67+
6268
getParentBranches() {
6369
return this.parentBranches;
6470
}
@@ -72,15 +78,15 @@ class SNote extends AbstractShacaEntity {
7278
}
7379

7480
getVisibleChildBranches() {
75-
return this.getChildBranches().filter((branch) => !branch.isHidden && !branch.getNote().isLabelTruthy("shareHiddenFromTree"));
81+
return this.getChildBranches().filter((branch) => !branch.isHidden && !branch.getNote().isLabelTruthy("shareHiddenFromTree") && !branch.getNote().isLabelTruthy("shareExclude"));
7682
}
7783

7884
getParentNotes() {
7985
return this.parents;
8086
}
8187

8288
getChildNotes() {
83-
return this.children;
89+
return this.children.filter((note) => !note.isLabelTruthy("shareExclude"));
8490
}
8591

8692
getVisibleChildNotes() {

docs/User Guide/User Guide/Advanced Usage/Sharing.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ To do so, create a shared text note and apply the `shareIndex` label. When viewe
131131

132132
## Attribute reference
133133

134-
<table class="ck-table-resized"><colgroup><col style="width:18.38%;"><col style="width:81.62%;"></colgroup><thead><tr><th>Attribute</th><th>Description</th></tr></thead><tbody><tr><td><code>#shareHiddenFromTree</code></td><td>this note is hidden from left navigation tree, but still accessible with its URL</td></tr><tr><td><code>#shareExternalLink</code></td><td>note will act as a link to an external website in the share tree</td></tr><tr><td><code>#shareAlias</code></td><td>define an alias using which the note will be available under <code>https://your_trilium_host/share/[your_alias]</code></td></tr><tr><td><code>#shareOmitDefaultCss</code></td><td>default share page CSS will be omitted. Use when you make extensive styling changes.</td></tr><tr><td><code>#shareRoot</code></td><td>marks note which is served on /share root.</td></tr><tr><td><code>#shareDescription</code></td><td>define text to be added to the HTML meta tag for description</td></tr><tr><td><code>#shareRaw</code></td><td>Note will be served in its raw format, without HTML wrapper. See also&nbsp;<a class="reference-link" href="Sharing/Serving%20directly%20the%20content%20o.md">Serving directly the content of a note</a>&nbsp;for an alternative method without setting an attribute.</td></tr><tr><td><code>#shareDisallowRobotIndexing</code></td><td><p>Indicates to web crawlers that the page should not be indexed of this note by:</p><ul><li data-list-item-id="e6baa9f60bf59d085fd31aa2cce07a0e7">Setting the <code>X-Robots-Tag: noindex</code> HTTP header.</li><li data-list-item-id="ec0d067db136ef9794e4f1033405880b7">Setting the <code>noindex, follow</code> meta tag.</li></ul></td></tr><tr><td><code>#shareCredentials</code></td><td>require credentials to access this shared note. Value is expected to be in format <code>username:password</code>. Don't forget to make this inheritable to apply to child-notes/images.</td></tr><tr><td><code>#shareIndex</code></td><td>Note with this label will list all roots of shared notes.</td></tr><tr><td><code>#shareHtmlLocation</code></td><td>defines where custom HTML injected via <code>~shareHtml</code> relation should be placed. Applied to the HTML snippet note itself. Format: <code>location:position</code> where location is <code>head</code>, <code>body</code>, or <code>content</code> and position is <code>start</code> or <code>end</code>. Defaults to <code>content:end</code>.</td></tr></tbody></table>
134+
<table class="ck-table-resized"><colgroup><col style="width:18.38%;"><col style="width:81.62%;"></colgroup><thead><tr><th>Attribute</th><th>Description</th></tr></thead><tbody><tr><td><code>#shareHiddenFromTree</code></td><td>this note is hidden from left navigation tree, but still accessible with its URL</td></tr><tr><td><code>#shareExclude</code></td><td>this note will be excluded from share, not accessible via direct URL (implemented to hide scripts from share)</td></tr><tr><td><code>#shareExternalLink</code></td><td>note will act as a link to an external website in the share tree</td></tr><tr><td><code>#shareAlias</code></td><td>define an alias using which the note will be available under <code>https://your_trilium_host/share/[your_alias]</code></td></tr><tr><td><code>#shareOmitDefaultCss</code></td><td>default share page CSS will be omitted. Use when you make extensive styling changes.</td></tr><tr><td><code>#shareRoot</code></td><td>marks note which is served on /share root.</td></tr><tr><td><code>#shareDescription</code></td><td>define text to be added to the HTML meta tag for description</td></tr><tr><td><code>#shareRaw</code></td><td>Note will be served in its raw format, without HTML wrapper. See also&nbsp;<a class="reference-link" href="Sharing/Serving%20directly%20the%20content%20o.md">Serving directly the content of a note</a>&nbsp;for an alternative method without setting an attribute.</td></tr><tr><td><code>#shareDisallowRobotIndexing</code></td><td><p>Indicates to web crawlers that the page should not be indexed of this note by:</p><ul><li data-list-item-id="e6baa9f60bf59d085fd31aa2cce07a0e7">Setting the <code>X-Robots-Tag: noindex</code> HTTP header.</li><li data-list-item-id="ec0d067db136ef9794e4f1033405880b7">Setting the <code>noindex, follow</code> meta tag.</li></ul></td></tr><tr><td><code>#shareCredentials</code></td><td>require credentials to access this shared note. Value is expected to be in format <code>username:password</code>. Don't forget to make this inheritable to apply to child-notes/images.</td></tr><tr><td><code>#shareIndex</code></td><td>Note with this label will list all roots of shared notes.</td></tr><tr><td><code>#shareHtmlLocation</code></td><td>defines where custom HTML injected via <code>~shareHtml</code> relation should be placed. Applied to the HTML snippet note itself. Format: <code>location:position</code> where location is <code>head</code>, <code>body</code>, or <code>content</code> and position is <code>start</code> or <code>end</code>. Defaults to <code>content:end</code>.</td></tr></tbody></table>
135135

136136
### Customizing logo
137137

packages/share-theme/src/templates/page.ejs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ content = content.replaceAll(headingRe, (...match) => {
152152
<p>This note has no content.</p>
153153
<% } else { %>
154154
<%
155-
content = content.replace(/<img /g, `<img alt="${t("share_theme.image_alt")}" loading="lazy" `);
155+
content = content.replace(/<img /g, `<img alt="${t("share_theme.image_alt")}" loading="lazy" `).replace(/src\=\"api\//g, `src="../api/`);
156156
%>
157157
<%- content %>
158158
<% } %>

0 commit comments

Comments
 (0)