From 85b5c899e890dc5c5b1dccf38e20761b6b5c98ee Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 17 Jun 2026 11:01:45 -0400 Subject: [PATCH 1/7] Migrate category lockdown settings connector to .gjs and Form API --- .../lockdown-settings.gjs | 94 +++++++++++++++++++ .../lockdown-settings.hbs | 33 ------- .../lockdown-settings.js | 35 ------- 3 files changed, 94 insertions(+), 68 deletions(-) create mode 100644 assets/javascripts/discourse/connectors/category-custom-security/lockdown-settings.gjs delete mode 100644 assets/javascripts/discourse/connectors/category-custom-security/lockdown-settings.hbs delete mode 100644 assets/javascripts/discourse/connectors/category-custom-security/lockdown-settings.js diff --git a/assets/javascripts/discourse/connectors/category-custom-security/lockdown-settings.gjs b/assets/javascripts/discourse/connectors/category-custom-security/lockdown-settings.gjs new file mode 100644 index 0000000..80c7776 --- /dev/null +++ b/assets/javascripts/discourse/connectors/category-custom-security/lockdown-settings.gjs @@ -0,0 +1,94 @@ +import Component from "@glimmer/component"; +import { tracked } from "@glimmer/tracking"; +import { fn } from "@ember/helper"; +import { action } from "@ember/object"; +import { service } from "@ember/service"; +import { i18n } from "discourse-i18n"; +import GroupChooser from "select-kit/components/group-chooser"; + +export default class LockdownSettings extends Component { + @service site; + @tracked lockdownEnabled; + + constructor() { + super(...arguments); + this.lockdownEnabled = ["true", "t", true].includes( + this.args.outletArgs.category?.custom_fields?.lockdown_enabled + ); + } + + get availableGroups() { + return (this.site.groups || []) + .map((g) => { + // prevents group "everyone" to be listed + return g.id === 0 ? null : g.name; + }) + .filter(Boolean); + } + + selectedGroups(value) { + return (value || "").split(",").filter(Boolean); + } + + @action + async onToggleEnabled(value, { set, name }) { + this.lockdownEnabled = value; + // store "" (which deletes the custom field) when disabled, so the + // checkbox state stays consistent after a reload + await set(name, value || ""); + } + + @action + onChangeGroups(field, values) { + field.set(values.join(",")); + } + + +} diff --git a/assets/javascripts/discourse/connectors/category-custom-security/lockdown-settings.hbs b/assets/javascripts/discourse/connectors/category-custom-security/lockdown-settings.hbs deleted file mode 100644 index b8f9cd9..0000000 --- a/assets/javascripts/discourse/connectors/category-custom-security/lockdown-settings.hbs +++ /dev/null @@ -1,33 +0,0 @@ -

{{i18n "lockdown.category_setting_heading"}}

-
- -
-{{#if @outletArgs.category.custom_fields.lockdown_enabled}} -
- - -
-
- - -
-{{/if}} \ No newline at end of file diff --git a/assets/javascripts/discourse/connectors/category-custom-security/lockdown-settings.js b/assets/javascripts/discourse/connectors/category-custom-security/lockdown-settings.js deleted file mode 100644 index 3f9803d..0000000 --- a/assets/javascripts/discourse/connectors/category-custom-security/lockdown-settings.js +++ /dev/null @@ -1,35 +0,0 @@ -import Component from "@glimmer/component"; -import { tracked } from "@glimmer/tracking"; -import { action, computed } from "@ember/object"; -import { service } from "@ember/service"; - -export default class LockdownSettings extends Component { - @service site; - @tracked selectedGroups = null; - - constructor() { - super(...arguments); - this.selectedGroups = ( - this.args.outletArgs.category.custom_fields.lockdown_allowed_groups || "" - ) - .split(",") - .filter(Boolean); - } - - @computed("site.groups.[]") - get availableGroups() { - return (this.site.groups || []) - .map((g) => { - // prevents group "everyone" to be listed - return g.id === 0 ? null : g.name; - }) - .filter(Boolean); - } - - @action - onChangeGroups(values) { - this.selectedGroups = values; - this.args.outletArgs.category.custom_fields.lockdown_allowed_groups = - values.join(","); - } -} From 5ad7c8e819f6b028433ba25c3195bc73f137b436 Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 17 Jun 2026 11:01:51 -0400 Subject: [PATCH 2/7] Add integration tests for lockdown settings connector --- .../components/lockdown-settings-test.gjs | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 test/javascripts/integration/components/lockdown-settings-test.gjs diff --git a/test/javascripts/integration/components/lockdown-settings-test.gjs b/test/javascripts/integration/components/lockdown-settings-test.gjs new file mode 100644 index 0000000..4f2c9e5 --- /dev/null +++ b/test/javascripts/integration/components/lockdown-settings-test.gjs @@ -0,0 +1,91 @@ +import { hash } from "@ember/helper"; +import { click, render } from "@ember/test-helpers"; +import { module, test } from "qunit"; +import Form from "discourse/components/form"; +import { setupRenderingTest } from "discourse/tests/helpers/component-test"; +import LockdownSettings from "discourse/plugins/discourse-category-lockdown/discourse/connectors/category-custom-security/lockdown-settings"; + +module( + "Discourse Category Lockdown | Integration | Component | lockdown-settings", + function (hooks) { + setupRenderingTest(hooks); + + test("hides the redirect settings when lockdown is disabled", async function (assert) { + const data = { custom_fields: { lockdown_enabled: false } }; + const category = { custom_fields: data.custom_fields }; + + await render(); + + assert.dom(".lockdown-settings").includesText("Category Lockdown"); + assert.dom("input[type=checkbox]").isNotChecked(); + assert + .dom(".lockdown-settings input[type=text]") + .doesNotExist("hides the redirect url field"); + assert + .dom(".group-chooser") + .doesNotExist("hides the allowed groups chooser"); + }); + + test("reveals the redirect settings when lockdown is enabled", async function (assert) { + const data = { + custom_fields: { + lockdown_enabled: true, + redirect_url: "https://example.com", + lockdown_allowed_groups: "", + }, + }; + const category = { custom_fields: data.custom_fields }; + + await render(); + + assert.dom("input[type=checkbox]").isChecked(); + assert + .dom(".lockdown-settings input[type=text]") + .hasValue("https://example.com", "shows the stored redirect url"); + assert.dom(".group-chooser").exists("renders the allowed groups chooser"); + }); + + test("toggling the checkbox reveals the fields and writes through the form", async function (assert) { + const data = { custom_fields: {} }; + const category = { custom_fields: data.custom_fields }; + let submitted = null; + + const onSubmit = (formData) => { + submitted = formData; + }; + + await render(); + + assert + .dom(".group-chooser") + .doesNotExist("fields are hidden before enabling"); + + await click("input[type=checkbox]"); + + assert + .dom(".group-chooser") + .exists("enabling the checkbox reveals the fields"); + + await click(".form-kit__button[type='submit']"); + + assert.strictEqual( + submitted?.custom_fields?.lockdown_enabled, + true, + "the lockdown_enabled change is routed through the form" + ); + }); + } +); From 096aa53c5106cacee2a968493ae173ecfd206471 Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 17 Jun 2026 11:01:57 -0400 Subject: [PATCH 3/7] Pin pre-Form-API plugin version for older Discourse installs --- .discourse-compatibility | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.discourse-compatibility b/.discourse-compatibility index c277e98..c1f40a2 100644 --- a/.discourse-compatibility +++ b/.discourse-compatibility @@ -1 +1,2 @@ -< 3.3.0.beta3-dev: 5288581b1d232a9a8a1d3803951f3a6b30ed5468 \ No newline at end of file +< 3.4.0.beta3-dev: 2db5bfe10525888e8e34929b293ae9407a77aee5 +< 3.3.0.beta3-dev: 5288581b1d232a9a8a1d3803951f3a6b30ed5468 From 9a64c32f6f0fdcfd3b7118edc8de35f2efec96fc Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 17 Jun 2026 11:02:05 -0400 Subject: [PATCH 4/7] Sort imports and remove trailing space in lockdown icon connector --- .../connectors/topic-link__before/category-lockdown-icon.gjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/javascripts/discourse/connectors/topic-link__before/category-lockdown-icon.gjs b/assets/javascripts/discourse/connectors/topic-link__before/category-lockdown-icon.gjs index 82dc2b9..8073137 100644 --- a/assets/javascripts/discourse/connectors/topic-link__before/category-lockdown-icon.gjs +++ b/assets/javascripts/discourse/connectors/topic-link__before/category-lockdown-icon.gjs @@ -1,11 +1,11 @@ import Component from "@glimmer/component"; import { service } from "@ember/service"; -import i18n from "discourse-common/helpers/i18n"; import icon from "discourse-common/helpers/d-icon"; +import i18n from "discourse-common/helpers/i18n"; export default class CategoryLockdownIcon extends Component { @service siteSettings; - + +} From 89ba4564dc577dfe016d468c8003c6c7a69412b6 Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 17 Jun 2026 11:58:43 -0400 Subject: [PATCH 7/7] bump version --- plugin.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.rb b/plugin.rb index 911e74b..14bf3a6 100644 --- a/plugin.rb +++ b/plugin.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # name: discourse-category-lockdown # about: Set all topics in a category to redirect, unless part of a specified group -# version: 1.2.1 +# version: 1.2.2 # authors: Pavilion # meta_topic_id: 70649 # url: https://github.com/paviliondev/discourse-category-lockdown