mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-10-10 16:11:38 +08:00
Merge pull request #1207 from maphew/bare2share2
Bare2share - Redirect bare domain to defined #shareRoot
This commit is contained in:
commit
46f543ad54
@ -36,6 +36,7 @@ import RibbonOptions from "./options/appearance/ribbon.js";
|
|||||||
import LocalizationOptions from "./options/appearance/i18n.js";
|
import LocalizationOptions from "./options/appearance/i18n.js";
|
||||||
import CodeBlockOptions from "./options/appearance/code_block.js";
|
import CodeBlockOptions from "./options/appearance/code_block.js";
|
||||||
import EditorOptions from "./options/text_notes/editor.js";
|
import EditorOptions from "./options/text_notes/editor.js";
|
||||||
|
import ShareSettingsOptions from "./options/other/share_settings.js";
|
||||||
import type FNote from "../../entities/fnote.js";
|
import type FNote from "../../entities/fnote.js";
|
||||||
import type NoteContextAwareWidget from "../note_context_aware_widget.js";
|
import type NoteContextAwareWidget from "../note_context_aware_widget.js";
|
||||||
|
|
||||||
@ -77,14 +78,14 @@ const CONTENT_WIDGETS: Record<string, (typeof NoteContextAwareWidget)[]> = {
|
|||||||
RevisionsSnapshotIntervalOptions,
|
RevisionsSnapshotIntervalOptions,
|
||||||
RevisionSnapshotsLimitOptions,
|
RevisionSnapshotsLimitOptions,
|
||||||
NetworkConnectionsOptions,
|
NetworkConnectionsOptions,
|
||||||
HtmlImportTagsOptions
|
HtmlImportTagsOptions,
|
||||||
|
ShareSettingsOptions
|
||||||
],
|
],
|
||||||
_optionsAdvanced: [DatabaseIntegrityCheckOptions, DatabaseAnonymizationOptions, AdvancedSyncOptions, VacuumDatabaseOptions],
|
_optionsAdvanced: [DatabaseIntegrityCheckOptions, DatabaseAnonymizationOptions, AdvancedSyncOptions, VacuumDatabaseOptions],
|
||||||
_backendLog: [BackendLogWidget]
|
_backendLog: [BackendLogWidget]
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class ContentWidgetTypeWidget extends TypeWidget {
|
export default class ContentWidgetTypeWidget extends TypeWidget {
|
||||||
|
|
||||||
private $content!: JQuery<HTMLElement>;
|
private $content!: JQuery<HTMLElement>;
|
||||||
|
|
||||||
static getType() {
|
static getType() {
|
||||||
|
@ -0,0 +1,97 @@
|
|||||||
|
import OptionsWidget from "../options_widget.js";
|
||||||
|
import options from "../../../../services/options.js";
|
||||||
|
import { t } from "../../../../services/i18n.js";
|
||||||
|
import type { OptionMap, OptionNames } from "../../../../../../services/options_interface.js";
|
||||||
|
import searchService from "../../../../services/search.js";
|
||||||
|
|
||||||
|
const TPL = `
|
||||||
|
<div class="options-section">
|
||||||
|
<h4>${t("share.title")}</h4>
|
||||||
|
|
||||||
|
<label class="tn-checkbox">
|
||||||
|
<input class="form-check-input" type="checkbox" name="redirectBareDomain" value="true">
|
||||||
|
${t("share.redirect_bare_domain")}
|
||||||
|
</label>
|
||||||
|
<p class="form-text">${t("share.redirect_bare_domain_description")}</p>
|
||||||
|
|
||||||
|
<label class="tn-checkbox">
|
||||||
|
<input class="form-check-input" type="checkbox" name="showLoginInShareTheme" value="true">
|
||||||
|
${t("share.show_login_link")}
|
||||||
|
</label>
|
||||||
|
<p class="form-text">${t("share.show_login_link_description")}</p>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
export default class ShareSettingsOptions extends OptionsWidget {
|
||||||
|
private $shareRootCheck!: JQuery<HTMLElement>;
|
||||||
|
private $shareRootStatus!: JQuery<HTMLElement>;
|
||||||
|
|
||||||
|
doRender() {
|
||||||
|
this.$widget = $(TPL);
|
||||||
|
this.contentSized();
|
||||||
|
|
||||||
|
this.$shareRootCheck = this.$widget.find('.share-root-check');
|
||||||
|
this.$shareRootStatus = this.$widget.find('.share-root-status');
|
||||||
|
|
||||||
|
// Add change handlers for both checkboxes
|
||||||
|
this.$widget.find('input[type="checkbox"]').on("change", (e: JQuery.ChangeEvent) => {
|
||||||
|
this.save();
|
||||||
|
|
||||||
|
// Show/hide share root status section based on redirectBareDomain checkbox
|
||||||
|
const target = e.target as HTMLInputElement;
|
||||||
|
if (target.name === 'redirectBareDomain') {
|
||||||
|
this.$shareRootCheck.toggle(target.checked);
|
||||||
|
if (target.checked) {
|
||||||
|
this.checkShareRoot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add click handler for check share root button
|
||||||
|
this.$widget.find('.check-share-root').on("click", () => this.checkShareRoot());
|
||||||
|
}
|
||||||
|
|
||||||
|
async optionsLoaded(options: OptionMap) {
|
||||||
|
const redirectBareDomain = options.redirectBareDomain === "true";
|
||||||
|
this.$widget.find('input[name="redirectBareDomain"]').prop("checked", redirectBareDomain);
|
||||||
|
this.$shareRootCheck.toggle(redirectBareDomain);
|
||||||
|
if (redirectBareDomain) {
|
||||||
|
await this.checkShareRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$widget.find('input[name="showLoginInShareTheme"]').prop("checked", options.showLoginInShareTheme === "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkShareRoot() {
|
||||||
|
const $button = this.$widget.find('.check-share-root');
|
||||||
|
$button.prop('disabled', true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const shareRootNotes = await searchService.searchForNotes("#shareRoot");
|
||||||
|
const sharedShareRootNote = shareRootNotes.find(note => note.isShared());
|
||||||
|
|
||||||
|
if (sharedShareRootNote) {
|
||||||
|
this.$shareRootStatus
|
||||||
|
.removeClass('text-danger')
|
||||||
|
.addClass('text-success')
|
||||||
|
.text(t("share.share_root_found", {noteTitle: sharedShareRootNote.title}));
|
||||||
|
} else {
|
||||||
|
this.$shareRootStatus
|
||||||
|
.removeClass('text-success')
|
||||||
|
.addClass('text-danger')
|
||||||
|
.text(shareRootNotes.length > 0
|
||||||
|
? t("share.share_root_not_shared", {noteTitle: shareRootNotes[0].title})
|
||||||
|
: t("share.share_root_not_found"));
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
$button.prop('disabled', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async save() {
|
||||||
|
const redirectBareDomain = this.$widget.find('input[name="redirectBareDomain"]').prop("checked");
|
||||||
|
await this.updateOption<"redirectBareDomain">("redirectBareDomain", redirectBareDomain.toString());
|
||||||
|
|
||||||
|
const showLoginInShareTheme = this.$widget.find('input[name="showLoginInShareTheme"]').prop("checked");
|
||||||
|
await this.updateOption<"showLoginInShareTheme">("showLoginInShareTheme", showLoginInShareTheme.toString());
|
||||||
|
}
|
||||||
|
}
|
@ -199,6 +199,11 @@ input::selection,
|
|||||||
color: var(--input-selection-text-color);
|
color: var(--input-selection-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-text {
|
||||||
|
color: var(--main-text-color);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
/* Input groups */
|
/* Input groups */
|
||||||
|
|
||||||
.input-group {
|
.input-group {
|
||||||
|
@ -1656,6 +1656,17 @@
|
|||||||
"hours": "Hours",
|
"hours": "Hours",
|
||||||
"days": "Days"
|
"days": "Days"
|
||||||
},
|
},
|
||||||
|
"share": {
|
||||||
|
"title": "Share Settings",
|
||||||
|
"redirect_bare_domain": "Redirect bare domain to Share page",
|
||||||
|
"redirect_bare_domain_description": "Redirect anonymous users to the Share page instead of showing Login",
|
||||||
|
"show_login_link": "Show Login link in Share theme",
|
||||||
|
"show_login_link_description": "Add a login link to the Share page footer",
|
||||||
|
"check_share_root": "Check Share Root Status",
|
||||||
|
"share_root_found": "Share root note '{{noteTitle}}' is ready",
|
||||||
|
"share_root_not_found": "No note with #shareRoot label found",
|
||||||
|
"share_root_not_shared": "Note '{{noteTitle}}' has #shareRoot label but is not Shared"
|
||||||
|
},
|
||||||
"time_selector": {
|
"time_selector": {
|
||||||
"invalid_input": "The entered time value is not a valid number.",
|
"invalid_input": "The entered time value is not a valid number.",
|
||||||
"minimum_input": "The entered time value needs to be at least {{minimumSeconds}} seconds."
|
"minimum_input": "The entered time value needs to be at least {{minimumSeconds}} seconds."
|
||||||
|
@ -75,7 +75,9 @@ const ALLOWED_OPTIONS = new Set([
|
|||||||
"textNoteEditorMultilineToolbar",
|
"textNoteEditorMultilineToolbar",
|
||||||
"layoutOrientation",
|
"layoutOrientation",
|
||||||
"backgroundEffects",
|
"backgroundEffects",
|
||||||
"allowedHtmlTags" // Allow configuring HTML import tags
|
"allowedHtmlTags",
|
||||||
|
"redirectBareDomain",
|
||||||
|
"showLoginInShareTheme"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function getOptions() {
|
function getOptions() {
|
||||||
|
@ -7,6 +7,8 @@ import { isElectron } from "./utils.js";
|
|||||||
import passwordEncryptionService from "./encryption/password_encryption.js";
|
import passwordEncryptionService from "./encryption/password_encryption.js";
|
||||||
import config from "./config.js";
|
import config from "./config.js";
|
||||||
import passwordService from "./encryption/password.js";
|
import passwordService from "./encryption/password.js";
|
||||||
|
import options from "./options.js";
|
||||||
|
import attributes from "./attributes.js";
|
||||||
import type { NextFunction, Request, Response } from "express";
|
import type { NextFunction, Request, Response } from "express";
|
||||||
|
|
||||||
const noAuthentication = config.General && config.General.noAuthentication === true;
|
const noAuthentication = config.General && config.General.noAuthentication === true;
|
||||||
@ -15,7 +17,16 @@ function checkAuth(req: Request, res: Response, next: NextFunction) {
|
|||||||
if (!sqlInit.isDbInitialized()) {
|
if (!sqlInit.isDbInitialized()) {
|
||||||
res.redirect("setup");
|
res.redirect("setup");
|
||||||
} else if (!req.session.loggedIn && !isElectron && !noAuthentication) {
|
} else if (!req.session.loggedIn && !isElectron && !noAuthentication) {
|
||||||
res.redirect("login");
|
const redirectToShare = options.getOptionBool("redirectBareDomain");
|
||||||
|
if (redirectToShare) {
|
||||||
|
// Check if any note has the #shareRoot label
|
||||||
|
const shareRootNotes = attributes.getNotesWithLabel("shareRoot");
|
||||||
|
if (shareRootNotes.length === 0) {
|
||||||
|
res.status(404).json({ message: "Share root not found. Please set up a note with #shareRoot label first." });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.redirect(redirectToShare ? "share" : "login");
|
||||||
} else {
|
} else {
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
@ -255,7 +255,11 @@ const defaultOptions: DefaultOption[] = [
|
|||||||
"tt"
|
"tt"
|
||||||
]),
|
]),
|
||||||
isSynced: true
|
isSynced: true
|
||||||
}
|
},
|
||||||
|
|
||||||
|
// Share settings
|
||||||
|
{ name: "redirectBareDomain", value: "false", isSynced: true },
|
||||||
|
{ name: "showLoginInShareTheme", value: "false", isSynced: true }
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -97,6 +97,10 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions<KeyboardActi
|
|||||||
codeBlockWordWrap: boolean;
|
codeBlockWordWrap: boolean;
|
||||||
textNoteEditorMultilineToolbar: boolean;
|
textNoteEditorMultilineToolbar: boolean;
|
||||||
backgroundEffects: boolean;
|
backgroundEffects: boolean;
|
||||||
|
// Share settings
|
||||||
|
redirectBareDomain: boolean;
|
||||||
|
showLoginInShareTheme: boolean;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OptionNames = keyof OptionDefinitions;
|
export type OptionNames = keyof OptionDefinitions;
|
||||||
|
@ -16,6 +16,7 @@ import type SNote from "./shaca/entities/snote.js";
|
|||||||
import type SBranch from "./shaca/entities/sbranch.js";
|
import type SBranch from "./shaca/entities/sbranch.js";
|
||||||
import type SAttachment from "./shaca/entities/sattachment.js";
|
import type SAttachment from "./shaca/entities/sattachment.js";
|
||||||
import utils from "../services/utils.js";
|
import utils from "../services/utils.js";
|
||||||
|
import options from "../services/options.js";
|
||||||
|
|
||||||
function getSharedSubTreeRoot(note: SNote): { note?: SNote; branch?: SBranch } {
|
function getSharedSubTreeRoot(note: SNote): { note?: SNote; branch?: SBranch } {
|
||||||
if (note.noteId === shareRoot.SHARE_ROOT_NOTE_ID) {
|
if (note.noteId === shareRoot.SHARE_ROOT_NOTE_ID) {
|
||||||
@ -151,7 +152,8 @@ function register(router: Router) {
|
|||||||
|
|
||||||
const { header, content, isEmpty } = contentRenderer.getContent(note);
|
const { header, content, isEmpty } = contentRenderer.getContent(note);
|
||||||
const subRoot = getSharedSubTreeRoot(note);
|
const subRoot = getSharedSubTreeRoot(note);
|
||||||
const opts = { note, header, content, isEmpty, subRoot, assetPath, appPath };
|
const showLoginInShareTheme = options.getOption("showLoginInShareTheme");
|
||||||
|
const opts = { note, header, content, isEmpty, subRoot, assetPath, appPath, showLoginInShareTheme };
|
||||||
let useDefaultView = true;
|
let useDefaultView = true;
|
||||||
|
|
||||||
// Check if the user has their own template
|
// Check if the user has their own template
|
||||||
|
@ -88,5 +88,10 @@
|
|||||||
</nav>
|
</nav>
|
||||||
<% } %>
|
<% } %>
|
||||||
</div>
|
</div>
|
||||||
|
<footer>
|
||||||
|
<% if (showLoginInShareTheme === 'true') { %>
|
||||||
|
<p><a href="/login" class="login-link">Login</a></p>
|
||||||
|
<% } %>
|
||||||
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user