From 7af4e527660761f65605ab5d0d65879d434e78e0 Mon Sep 17 00:00:00 2001 From: matt wilkie Date: Sun, 16 Feb 2025 21:29:38 -0700 Subject: [PATCH 01/12] brought over changes from 879035d The last known good state before I got sidetracked into docker changes --- src/public/app/widgets/type_widgets/content_widget.ts | 4 +++- src/services/auth.ts | 2 +- src/services/options_init.ts | 6 +++++- src/share/routes.ts | 4 +++- src/views/share/page.ejs | 5 +++++ 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/public/app/widgets/type_widgets/content_widget.ts b/src/public/app/widgets/type_widgets/content_widget.ts index 1f19e2b81..d2ea5a971 100644 --- a/src/public/app/widgets/type_widgets/content_widget.ts +++ b/src/public/app/widgets/type_widgets/content_widget.ts @@ -35,6 +35,7 @@ import RibbonOptions from "./options/appearance/ribbon.js"; import LocalizationOptions from "./options/appearance/i18n.js"; import CodeBlockOptions from "./options/appearance/code_block.js"; import EditorOptions from "./options/text_notes/editor.js"; +import ShareSettingsOptions from "./options/other/share_settings.js"; // added import statement import type FNote from "../../entities/fnote.js"; import type NoteContextAwareWidget from "../note_context_aware_widget.js"; @@ -76,7 +77,8 @@ const CONTENT_WIDGETS: Record = { RevisionsSnapshotIntervalOptions, RevisionSnapshotsLimitOptions, NetworkConnectionsOptions, - HtmlImportTagsOptions + HtmlImportTagsOptions, + ShareSettingsOptions // moved to the end of the array ], _optionsAdvanced: [DatabaseIntegrityCheckOptions, DatabaseAnonymizationOptions, AdvancedSyncOptions, VacuumDatabaseOptions], _backendLog: [BackendLogWidget] diff --git a/src/services/auth.ts b/src/services/auth.ts index 3e8957100..d89fad633 100644 --- a/src/services/auth.ts +++ b/src/services/auth.ts @@ -15,7 +15,7 @@ function checkAuth(req: Request, res: Response, next: NextFunction) { if (!sqlInit.isDbInitialized()) { res.redirect("setup"); } else if (!req.session.loggedIn && !isElectron && !noAuthentication) { - res.redirect("login"); + res.redirect("share"); } else { next(); } diff --git a/src/services/options_init.ts b/src/services/options_init.ts index bb962835d..0c738a759 100644 --- a/src/services/options_init.ts +++ b/src/services/options_init.ts @@ -252,7 +252,11 @@ const defaultOptions: DefaultOption[] = [ "tt" ]), isSynced: true - } + }, + + // Share settings + { name: 'redirectBareDomain', value: 'false', isSynced: true }, + { name: 'showLoginInShareTheme', value: 'false', isSynced: true } ]; /** diff --git a/src/share/routes.ts b/src/share/routes.ts index c77a41fc0..a1ea2ecf2 100644 --- a/src/share/routes.ts +++ b/src/share/routes.ts @@ -16,6 +16,7 @@ import type SNote from "./shaca/entities/snote.js"; import type SBranch from "./shaca/entities/sbranch.js"; import type SAttachment from "./shaca/entities/sattachment.js"; import utils from "../services/utils.js"; +import optionService from '../services/option_service.js'; function getSharedSubTreeRoot(note: SNote): { note?: SNote; branch?: SBranch } { if (note.noteId === shareRoot.SHARE_ROOT_NOTE_ID) { @@ -151,7 +152,8 @@ function register(router: Router) { const { header, content, isEmpty } = contentRenderer.getContent(note); const subRoot = getSharedSubTreeRoot(note); - const opts = { note, header, content, isEmpty, subRoot, assetPath, appPath }; + const showLoginInShareTheme = optionService.getOption('showLoginInShareTheme'); + const opts = { note, header, content, isEmpty, subRoot, assetPath, appPath, showLoginInShareTheme }; let useDefaultView = true; // Check if the user has their own template diff --git a/src/views/share/page.ejs b/src/views/share/page.ejs index 0ff04174d..35d01cd2c 100644 --- a/src/views/share/page.ejs +++ b/src/views/share/page.ejs @@ -88,5 +88,10 @@ <% } %> +
+ <% if (showLoginInShareTheme === 'true') { %> +

+ <% } %> +
From bc66e985331c00b1ccea3d6307a0f54ff714a252 Mon Sep 17 00:00:00 2001 From: matt wilkie Date: Sun, 16 Feb 2025 22:17:59 -0700 Subject: [PATCH 02/12] okay, we can start npm server now, but new db redirects to share --- .../widgets/type_widgets/content_widget.ts | 2 +- .../options/other/share_settings.ts | 47 +++++++++++++++++++ src/services/options_interface.ts | 3 ++ src/share/routes.ts | 4 +- 4 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 src/public/app/widgets/type_widgets/options/other/share_settings.ts diff --git a/src/public/app/widgets/type_widgets/content_widget.ts b/src/public/app/widgets/type_widgets/content_widget.ts index d2ea5a971..e7a0533ff 100644 --- a/src/public/app/widgets/type_widgets/content_widget.ts +++ b/src/public/app/widgets/type_widgets/content_widget.ts @@ -35,7 +35,7 @@ import RibbonOptions from "./options/appearance/ribbon.js"; import LocalizationOptions from "./options/appearance/i18n.js"; import CodeBlockOptions from "./options/appearance/code_block.js"; import EditorOptions from "./options/text_notes/editor.js"; -import ShareSettingsOptions from "./options/other/share_settings.js"; // added import statement +import ShareSettingsOptions from "./options/other/share_settings.js"; import type FNote from "../../entities/fnote.js"; import type NoteContextAwareWidget from "../note_context_aware_widget.js"; diff --git a/src/public/app/widgets/type_widgets/options/other/share_settings.ts b/src/public/app/widgets/type_widgets/options/other/share_settings.ts new file mode 100644 index 000000000..d90840948 --- /dev/null +++ b/src/public/app/widgets/type_widgets/options/other/share_settings.ts @@ -0,0 +1,47 @@ +import OptionsWidget from "../options_widget.js"; +import options from "../../../../services/options.js"; +import { t } from "../../../../services/i18n.js"; + +const TPL = ` +
+

${t('Share Settings')}

+ +
+ +

${t('When enabled, accessing the root URL will redirect to the Share page instead of Login')}

+
+ +
+ +

${t('Add a login link to the Share page footer')}

+
+
`; + +export default class ShareSettingsOptions extends OptionsWidget { + doRender() { + this.$widget = $(TPL); + this.contentSized(); + } + + async optionsLoaded(options: Record) { + this.$widget.find('input[name="redirectBareDomain"]').prop('checked', + options.redirectBareDomain === 'true'); + + this.$widget.find('input[name="showLoginInShareTheme"]').prop('checked', + options.showLoginInShareTheme === 'true'); + } + + async save() { + const redirectBareDomain = this.$widget.find('input[name="redirectBareDomain"]').prop('checked'); + await this.updateOption('redirectBareDomain', redirectBareDomain.toString()); + + const showLoginInShareTheme = this.$widget.find('input[name="showLoginInShareTheme"]').prop('checked'); + await this.updateOption('showLoginInShareTheme', showLoginInShareTheme.toString()); + } +} diff --git a/src/services/options_interface.ts b/src/services/options_interface.ts index f3a92383b..ef55af6c5 100644 --- a/src/services/options_interface.ts +++ b/src/services/options_interface.ts @@ -29,6 +29,9 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions Date: Sun, 16 Feb 2025 22:47:50 -0700 Subject: [PATCH 03/12] feature complete; tested and working on local linux machine --- .../type_widgets/options/other/share_settings.ts | 10 +++++++--- src/routes/api/options.ts | 4 +++- src/services/auth.ts | 4 +++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/public/app/widgets/type_widgets/options/other/share_settings.ts b/src/public/app/widgets/type_widgets/options/other/share_settings.ts index d90840948..d7743c63c 100644 --- a/src/public/app/widgets/type_widgets/options/other/share_settings.ts +++ b/src/public/app/widgets/type_widgets/options/other/share_settings.ts @@ -1,6 +1,7 @@ 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"; const TPL = `
@@ -27,9 +28,12 @@ export default class ShareSettingsOptions extends OptionsWidget { doRender() { this.$widget = $(TPL); this.contentSized(); + + // Add change handlers for both checkboxes + this.$widget.find('input[type="checkbox"]').on('change', () => this.save()); } - async optionsLoaded(options: Record) { + async optionsLoaded(options: OptionMap) { this.$widget.find('input[name="redirectBareDomain"]').prop('checked', options.redirectBareDomain === 'true'); @@ -39,9 +43,9 @@ export default class ShareSettingsOptions extends OptionsWidget { async save() { const redirectBareDomain = this.$widget.find('input[name="redirectBareDomain"]').prop('checked'); - await this.updateOption('redirectBareDomain', redirectBareDomain.toString()); + await this.updateOption<'redirectBareDomain'>('redirectBareDomain', redirectBareDomain.toString()); const showLoginInShareTheme = this.$widget.find('input[name="showLoginInShareTheme"]').prop('checked'); - await this.updateOption('showLoginInShareTheme', showLoginInShareTheme.toString()); + await this.updateOption<'showLoginInShareTheme'>('showLoginInShareTheme', showLoginInShareTheme.toString()); } } diff --git a/src/routes/api/options.ts b/src/routes/api/options.ts index 14cb7ec42..85272ea62 100644 --- a/src/routes/api/options.ts +++ b/src/routes/api/options.ts @@ -72,7 +72,9 @@ const ALLOWED_OPTIONS = new Set([ "textNoteEditorMultilineToolbar", "layoutOrientation", "backgroundEffects", - "allowedHtmlTags" // Allow configuring HTML import tags + "allowedHtmlTags", + "redirectBareDomain", + "showLoginInShareTheme" ]); function getOptions() { diff --git a/src/services/auth.ts b/src/services/auth.ts index d89fad633..5ba56e426 100644 --- a/src/services/auth.ts +++ b/src/services/auth.ts @@ -7,6 +7,7 @@ import { isElectron } from "./utils.js"; import passwordEncryptionService from "./encryption/password_encryption.js"; import config from "./config.js"; import passwordService from "./encryption/password.js"; +import options from "./options.js"; import type { NextFunction, Request, Response } from "express"; const noAuthentication = config.General && config.General.noAuthentication === true; @@ -15,7 +16,8 @@ function checkAuth(req: Request, res: Response, next: NextFunction) { if (!sqlInit.isDbInitialized()) { res.redirect("setup"); } else if (!req.session.loggedIn && !isElectron && !noAuthentication) { - res.redirect("share"); + const redirectToShare = options.getOption('redirectBareDomain') === 'true'; + res.redirect(redirectToShare ? "share" : "login"); } else { next(); } From 2ec2d784ec8f6f6f4fda00d9aef32b7b264885d8 Mon Sep 17 00:00:00 2001 From: Matt Wilkie Date: Mon, 17 Feb 2025 13:19:55 -0700 Subject: [PATCH 04/12] results of `npx prettier` --- .../widgets/type_widgets/content_widget.ts | 1 - .../options/other/share_settings.ts | 28 +++++++++---------- src/services/auth.ts | 2 +- src/services/options_init.ts | 4 +-- src/share/routes.ts | 4 +-- 5 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/public/app/widgets/type_widgets/content_widget.ts b/src/public/app/widgets/type_widgets/content_widget.ts index e7a0533ff..5bd577fd2 100644 --- a/src/public/app/widgets/type_widgets/content_widget.ts +++ b/src/public/app/widgets/type_widgets/content_widget.ts @@ -85,7 +85,6 @@ const CONTENT_WIDGETS: Record = { }; export default class ContentWidgetTypeWidget extends TypeWidget { - private $content!: JQuery; static getType() { diff --git a/src/public/app/widgets/type_widgets/options/other/share_settings.ts b/src/public/app/widgets/type_widgets/options/other/share_settings.ts index d7743c63c..9c7cd1b2d 100644 --- a/src/public/app/widgets/type_widgets/options/other/share_settings.ts +++ b/src/public/app/widgets/type_widgets/options/other/share_settings.ts @@ -5,22 +5,22 @@ import type { OptionMap, OptionNames } from "../../../../../../services/options_ const TPL = `
-

${t('Share Settings')}

+

${t("Share Settings")}

-

${t('When enabled, accessing the root URL will redirect to the Share page instead of Login')}

+

${t("When enabled, accessing the root URL will redirect to the Share page instead of Login")}

-

${t('Add a login link to the Share page footer')}

+

${t("Add a login link to the Share page footer")}

`; @@ -30,22 +30,20 @@ export default class ShareSettingsOptions extends OptionsWidget { this.contentSized(); // Add change handlers for both checkboxes - this.$widget.find('input[type="checkbox"]').on('change', () => this.save()); + this.$widget.find('input[type="checkbox"]').on("change", () => this.save()); } async optionsLoaded(options: OptionMap) { - this.$widget.find('input[name="redirectBareDomain"]').prop('checked', - options.redirectBareDomain === 'true'); - - this.$widget.find('input[name="showLoginInShareTheme"]').prop('checked', - options.showLoginInShareTheme === 'true'); + this.$widget.find('input[name="redirectBareDomain"]').prop("checked", options.redirectBareDomain === "true"); + + this.$widget.find('input[name="showLoginInShareTheme"]').prop("checked", options.showLoginInShareTheme === "true"); } async save() { - const redirectBareDomain = this.$widget.find('input[name="redirectBareDomain"]').prop('checked'); - await this.updateOption<'redirectBareDomain'>('redirectBareDomain', redirectBareDomain.toString()); + 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()); + const showLoginInShareTheme = this.$widget.find('input[name="showLoginInShareTheme"]').prop("checked"); + await this.updateOption<"showLoginInShareTheme">("showLoginInShareTheme", showLoginInShareTheme.toString()); } } diff --git a/src/services/auth.ts b/src/services/auth.ts index 5ba56e426..68c497b91 100644 --- a/src/services/auth.ts +++ b/src/services/auth.ts @@ -16,7 +16,7 @@ function checkAuth(req: Request, res: Response, next: NextFunction) { if (!sqlInit.isDbInitialized()) { res.redirect("setup"); } else if (!req.session.loggedIn && !isElectron && !noAuthentication) { - const redirectToShare = options.getOption('redirectBareDomain') === 'true'; + const redirectToShare = options.getOption("redirectBareDomain") === "true"; res.redirect(redirectToShare ? "share" : "login"); } else { next(); diff --git a/src/services/options_init.ts b/src/services/options_init.ts index 0c738a759..da81abc90 100644 --- a/src/services/options_init.ts +++ b/src/services/options_init.ts @@ -255,8 +255,8 @@ const defaultOptions: DefaultOption[] = [ }, // Share settings - { name: 'redirectBareDomain', value: 'false', isSynced: true }, - { name: 'showLoginInShareTheme', value: 'false', isSynced: true } + { name: "redirectBareDomain", value: "false", isSynced: true }, + { name: "showLoginInShareTheme", value: "false", isSynced: true } ]; /** diff --git a/src/share/routes.ts b/src/share/routes.ts index ae2b5d414..3b6258872 100644 --- a/src/share/routes.ts +++ b/src/share/routes.ts @@ -16,7 +16,7 @@ import type SNote from "./shaca/entities/snote.js"; import type SBranch from "./shaca/entities/sbranch.js"; import type SAttachment from "./shaca/entities/sattachment.js"; import utils from "../services/utils.js"; -import options from '../services/options.js'; +import options from "../services/options.js"; function getSharedSubTreeRoot(note: SNote): { note?: SNote; branch?: SBranch } { if (note.noteId === shareRoot.SHARE_ROOT_NOTE_ID) { @@ -152,7 +152,7 @@ function register(router: Router) { const { header, content, isEmpty } = contentRenderer.getContent(note); const subRoot = getSharedSubTreeRoot(note); - const showLoginInShareTheme = options.getOption('showLoginInShareTheme'); + const showLoginInShareTheme = options.getOption("showLoginInShareTheme"); const opts = { note, header, content, isEmpty, subRoot, assetPath, appPath, showLoginInShareTheme }; let useDefaultView = true; From 657638ee54f93c5e278e2a65b68b98230de102a0 Mon Sep 17 00:00:00 2001 From: Matt Wilkie Date: Mon, 17 Feb 2025 13:46:03 -0700 Subject: [PATCH 05/12] responding to code review (thanks pano!) --- .../widgets/type_widgets/content_widget.ts | 2 +- .../options/other/share_settings.ts | 13 ++++++----- src/public/translations/en/translation.json | 22 +++++++++++++++++++ src/services/options_interface.ts | 7 +++--- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/public/app/widgets/type_widgets/content_widget.ts b/src/public/app/widgets/type_widgets/content_widget.ts index 5bd577fd2..e4cb81c10 100644 --- a/src/public/app/widgets/type_widgets/content_widget.ts +++ b/src/public/app/widgets/type_widgets/content_widget.ts @@ -78,7 +78,7 @@ const CONTENT_WIDGETS: Record = { RevisionSnapshotsLimitOptions, NetworkConnectionsOptions, HtmlImportTagsOptions, - ShareSettingsOptions // moved to the end of the array + ShareSettingsOptions ], _optionsAdvanced: [DatabaseIntegrityCheckOptions, DatabaseAnonymizationOptions, AdvancedSyncOptions, VacuumDatabaseOptions], _backendLog: [BackendLogWidget] diff --git a/src/public/app/widgets/type_widgets/options/other/share_settings.ts b/src/public/app/widgets/type_widgets/options/other/share_settings.ts index 9c7cd1b2d..c4c9d9421 100644 --- a/src/public/app/widgets/type_widgets/options/other/share_settings.ts +++ b/src/public/app/widgets/type_widgets/options/other/share_settings.ts @@ -5,22 +5,23 @@ import type { OptionMap, OptionNames } from "../../../../../../services/options_ const TPL = `
-

${t("Share Settings")}

- +

${t("share.title")}

+
-

${t("When enabled, accessing the root URL will redirect to the Share page instead of Login")}

+

${t("share.redirect_bare_domain_description")}

-

${t("Add a login link to the Share page footer")}

+

${t("share.show_login_link_description")}

+

 

`; diff --git a/src/public/translations/en/translation.json b/src/public/translations/en/translation.json index f8fdbc51a..7fed97206 100644 --- a/src/public/translations/en/translation.json +++ b/src/public/translations/en/translation.json @@ -1656,5 +1656,27 @@ "minutes": "Minutes", "hours": "Hours", "days": "Days" + }, + "share": { + "title": "Share Settings", + "redirect_bare_domain": "Redirect bare domain to Share page", + "redirect_bare_domain_description": "When enabled, accessing the root URL will redirect to the Share page instead of Login", + "show_login_link": "Show Login link in Share theme", + "show_login_link_description": "Add a login link to the Share page footer" + }, + "sync_2": { + "config_title": "Sync Configuration", + "server_address": "Server instance address", + "timeout": "Sync timeout (milliseconds)", + "proxy_label": "Sync proxy server (optional)", + "note": "Note", + "note_description": "If you leave the proxy setting blank, the system proxy will be used (applies to desktop/electron build only).", + "special_value_description": "Another special value is noproxy which forces ignoring even the system proxy and respects NODE_TLS_REJECT_UNAUTHORIZED.", + "save": "Save", + "help": "Help", + "test_title": "Sync Test", + "test_description": "This will test the connection and handshake to the sync server. If the sync server isn't initialized, this will set it up to sync with the local document.", + "test_button": "Test sync", + "handshake_failed": "Sync server handshake failed, error: {{message}}" } } diff --git a/src/services/options_interface.ts b/src/services/options_interface.ts index ef55af6c5..9a89c9ac7 100644 --- a/src/services/options_interface.ts +++ b/src/services/options_interface.ts @@ -29,9 +29,6 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions Date: Mon, 17 Feb 2025 13:55:55 -0700 Subject: [PATCH 06/12] fix indent --- src/public/translations/en/translation.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/public/translations/en/translation.json b/src/public/translations/en/translation.json index 027c2f269..8cbe3aa9a 100644 --- a/src/public/translations/en/translation.json +++ b/src/public/translations/en/translation.json @@ -1677,7 +1677,7 @@ "test_button": "Test sync", "handshake_failed": "Sync server handshake failed, error: {{message}}" }, - "time_selector": { + "time_selector": { "invalid_input": "The entered time value is not a valid number." - } + } } From aab35955bf6af74595a49e4737278709d8b907b8 Mon Sep 17 00:00:00 2001 From: Matt Wilkie Date: Mon, 17 Feb 2025 16:45:47 -0700 Subject: [PATCH 07/12] remove duplicated sync_2 --- src/public/translations/en/translation.json | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/public/translations/en/translation.json b/src/public/translations/en/translation.json index 8cbe3aa9a..f2978237d 100644 --- a/src/public/translations/en/translation.json +++ b/src/public/translations/en/translation.json @@ -1662,21 +1662,6 @@ "show_login_link": "Show Login link in Share theme", "show_login_link_description": "Add a login link to the Share page footer" }, - "sync_2": { - "config_title": "Sync Configuration", - "server_address": "Server instance address", - "timeout": "Sync timeout (milliseconds)", - "proxy_label": "Sync proxy server (optional)", - "note": "Note", - "note_description": "If you leave the proxy setting blank, the system proxy will be used (applies to desktop/electron build only).", - "special_value_description": "Another special value is noproxy which forces ignoring even the system proxy and respects NODE_TLS_REJECT_UNAUTHORIZED.", - "save": "Save", - "help": "Help", - "test_title": "Sync Test", - "test_description": "This will test the connection and handshake to the sync server. If the sync server isn't initialized, this will set it up to sync with the local document.", - "test_button": "Test sync", - "handshake_failed": "Sync server handshake failed, error: {{message}}" - }, "time_selector": { "invalid_input": "The entered time value is not a valid number." } From 5a6c3ae4263e289af0f241c9ca06df8eeb3a6a1d Mon Sep 17 00:00:00 2001 From: Matt Wilkie Date: Wed, 19 Feb 2025 08:33:03 -0700 Subject: [PATCH 08/12] use the standard classes per @pano9000 advice, https://github.com/TriliumNext/Notes/pull/1207#issuecomment-2667896424 --- .../options/other/share_settings.ts | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/public/app/widgets/type_widgets/options/other/share_settings.ts b/src/public/app/widgets/type_widgets/options/other/share_settings.ts index c4c9d9421..199605082 100644 --- a/src/public/app/widgets/type_widgets/options/other/share_settings.ts +++ b/src/public/app/widgets/type_widgets/options/other/share_settings.ts @@ -4,25 +4,20 @@ import { t } from "../../../../services/i18n.js"; import type { OptionMap, OptionNames } from "../../../../../../services/options_interface.js"; const TPL = ` -
+

${t("share.title")}

-
- -

${t("share.redirect_bare_domain_description")}

-
+ +

${t("share.redirect_bare_domain_description")}

-
- -

${t("share.show_login_link_description")}

-

 

-
+ +

${t("share.show_login_link_description")}

`; export default class ShareSettingsOptions extends OptionsWidget { From 2734e230ab07fcc802ca988ff92094ff4abf4f69 Mon Sep 17 00:00:00 2001 From: matt wilkie Date: Thu, 20 Feb 2025 08:12:51 -0700 Subject: [PATCH 09/12] WIP: 1st step at verifying shareRoot is set --- .../options/other/share_settings.ts | 71 +++++++++++++++---- src/public/translations/en/translation.json | 5 +- src/services/auth.ts | 11 ++- 3 files changed, 73 insertions(+), 14 deletions(-) diff --git a/src/public/app/widgets/type_widgets/options/other/share_settings.ts b/src/public/app/widgets/type_widgets/options/other/share_settings.ts index 199605082..abc540b49 100644 --- a/src/public/app/widgets/type_widgets/options/other/share_settings.ts +++ b/src/public/app/widgets/type_widgets/options/other/share_settings.ts @@ -2,44 +2,91 @@ 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 = `
-

${t("share.title")}

-

${t("share.redirect_bare_domain_description")}

+ + -

${t("share.show_login_link_description")}

`; export default class ShareSettingsOptions extends OptionsWidget { + private $shareRootCheck!: JQuery; + private $shareRootStatus!: JQuery; + 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", () => this.save()); + 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) { - this.$widget.find('input[name="redirectBareDomain"]').prop("checked", options.redirectBareDomain === "true"); + 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"); + this.$widget.find('input[name="shareSubtree"]').prop("checked", options.shareSubtree === "true"); + } + + async checkShareRoot() { + const shareRootNotes = await searchService.searchNotes("#shareRoot", { + includeArchivedNotes: true, + ignoreHoistedNote: true + }); + + if (shareRootNotes.length > 0) { + this.$shareRootStatus + .removeClass('text-danger') + .addClass('text-success') + .text(t("share.share_root_found", {noteTitle: shareRootNotes[0].title})); + } else { + this.$shareRootStatus + .removeClass('text-success') + .addClass('text-danger') + .text(t("share.share_root_not_found")); + } } 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()); + const showLoginInShareTheme = this.$widget.find('input[name="shareSubtree"]').prop("checked"); + await this.updateOption<"shareSubtree">("shareSubtree", showLoginInShareTheme.toString()); } } diff --git a/src/public/translations/en/translation.json b/src/public/translations/en/translation.json index f2978237d..c0ee99e4b 100644 --- a/src/public/translations/en/translation.json +++ b/src/public/translations/en/translation.json @@ -1660,7 +1660,10 @@ "redirect_bare_domain": "Redirect bare domain to Share page", "redirect_bare_domain_description": "When enabled, accessing the root URL will redirect to the Share page instead of Login", "show_login_link": "Show Login link in Share theme", - "show_login_link_description": "Add a login link to the Share page footer" + "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 found: {{noteTitle}}", + "share_root_not_found": "No note with #shareRoot label found. Set up a note with #shareRoot label first." }, "time_selector": { "invalid_input": "The entered time value is not a valid number." diff --git a/src/services/auth.ts b/src/services/auth.ts index 68c497b91..03f40e6e7 100644 --- a/src/services/auth.ts +++ b/src/services/auth.ts @@ -8,6 +8,7 @@ import passwordEncryptionService from "./encryption/password_encryption.js"; import config from "./config.js"; import passwordService from "./encryption/password.js"; import options from "./options.js"; +import attributes from "./attributes.js"; import type { NextFunction, Request, Response } from "express"; const noAuthentication = config.General && config.General.noAuthentication === true; @@ -16,7 +17,15 @@ function checkAuth(req: Request, res: Response, next: NextFunction) { if (!sqlInit.isDbInitialized()) { res.redirect("setup"); } else if (!req.session.loggedIn && !isElectron && !noAuthentication) { - const redirectToShare = options.getOption("redirectBareDomain") === "true"; + 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 { next(); From bdd6395a762f29b46e3fecadb5c7783af57e132a Mon Sep 17 00:00:00 2001 From: matt wilkie Date: Thu, 20 Feb 2025 09:08:24 -0700 Subject: [PATCH 10/12] works! verify shareRoot is set and note is shared --- .../options/other/share_settings.ts | 47 +++++++++++-------- src/public/translations/en/translation.json | 7 +-- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/public/app/widgets/type_widgets/options/other/share_settings.ts b/src/public/app/widgets/type_widgets/options/other/share_settings.ts index abc540b49..d2d77bc6d 100644 --- a/src/public/app/widgets/type_widgets/options/other/share_settings.ts +++ b/src/public/app/widgets/type_widgets/options/other/share_settings.ts @@ -6,11 +6,11 @@ import searchService from "../../../../services/search.js"; const TPL = `
+

${t("share.redirect_bare_domain_description")}

-

${t("share.redirect_bare_domain_description")}

`; @@ -60,25 +60,32 @@ export default class ShareSettingsOptions extends OptionsWidget { await this.checkShareRoot(); } - this.$widget.find('input[name="shareSubtree"]').prop("checked", options.shareSubtree === "true"); + this.$widget.find('input[name="showLoginInShareTheme"]').prop("checked", options.showLoginInShareTheme === "true"); } async checkShareRoot() { - const shareRootNotes = await searchService.searchNotes("#shareRoot", { - includeArchivedNotes: true, - ignoreHoistedNote: true - }); + 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 (shareRootNotes.length > 0) { - this.$shareRootStatus - .removeClass('text-danger') - .addClass('text-success') - .text(t("share.share_root_found", {noteTitle: shareRootNotes[0].title})); - } else { - this.$shareRootStatus - .removeClass('text-success') - .addClass('text-danger') - .text(t("share.share_root_not_found")); + 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); } } @@ -86,7 +93,7 @@ export default class ShareSettingsOptions extends OptionsWidget { const redirectBareDomain = this.$widget.find('input[name="redirectBareDomain"]').prop("checked"); await this.updateOption<"redirectBareDomain">("redirectBareDomain", redirectBareDomain.toString()); - const showLoginInShareTheme = this.$widget.find('input[name="shareSubtree"]').prop("checked"); - await this.updateOption<"shareSubtree">("shareSubtree", showLoginInShareTheme.toString()); + const showLoginInShareTheme = this.$widget.find('input[name="showLoginInShareTheme"]').prop("checked"); + await this.updateOption<"showLoginInShareTheme">("showLoginInShareTheme", showLoginInShareTheme.toString()); } } diff --git a/src/public/translations/en/translation.json b/src/public/translations/en/translation.json index c0ee99e4b..506952623 100644 --- a/src/public/translations/en/translation.json +++ b/src/public/translations/en/translation.json @@ -1658,12 +1658,13 @@ "share": { "title": "Share Settings", "redirect_bare_domain": "Redirect bare domain to Share page", - "redirect_bare_domain_description": "When enabled, accessing the root URL will redirect to the Share page instead of Login", + "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 found: {{noteTitle}}", - "share_root_not_found": "No note with #shareRoot label found. Set up a note with #shareRoot label first." + "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": { "invalid_input": "The entered time value is not a valid number." From 16b16927efda18a67e218431ab6fe6e2060000c4 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 20 Feb 2025 22:08:04 +0200 Subject: [PATCH 11/12] feat(settings/share): add title to section --- .../widgets/type_widgets/options/other/share_settings.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/public/app/widgets/type_widgets/options/other/share_settings.ts b/src/public/app/widgets/type_widgets/options/other/share_settings.ts index d2d77bc6d..52d41552c 100644 --- a/src/public/app/widgets/type_widgets/options/other/share_settings.ts +++ b/src/public/app/widgets/type_widgets/options/other/share_settings.ts @@ -6,6 +6,8 @@ import searchService from "../../../../services/search.js"; const TPL = `
+

${t("share.title")}

+

${t("share.redirect_bare_domain_description")}