diff --git a/plugins/automation/admin/assets/javascripts/admin/components/automation-field.gjs b/plugins/automation/admin/assets/javascripts/admin/components/automation-field.gjs index 56e8576ec5c..9aeb2db0237 100644 --- a/plugins/automation/admin/assets/javascripts/admin/components/automation-field.gjs +++ b/plugins/automation/admin/assets/javascripts/admin/components/automation-field.gjs @@ -10,6 +10,7 @@ import DaCustomFields from "./fields/da-custom-fields"; import DaDateTimeField from "./fields/da-date-time-field"; import DaEmailGroupUserField from "./fields/da-email-group-user-field"; import DaGroupField from "./fields/da-group-field"; +import DaGroupsField from "./fields/da-groups-field"; import DaKeyValueField from "./fields/da-key-value-field"; import DaMessageField from "./fields/da-message-field"; import DaPeriodField from "./fields/da-period-field"; @@ -41,6 +42,7 @@ const FIELD_COMPONENTS = { "trust-levels": DaTrustLevelsField, category: DaCategoryField, group: DaGroupField, + groups: DaGroupsField, choices: DaChoicesField, category_notification_level: DaCategoryNotificationlevelField, email_group_user: DaEmailGroupUserField, diff --git a/plugins/automation/admin/assets/javascripts/admin/components/fields/da-groups-field.gjs b/plugins/automation/admin/assets/javascripts/admin/components/fields/da-groups-field.gjs new file mode 100644 index 00000000000..5324e66bd59 --- /dev/null +++ b/plugins/automation/admin/assets/javascripts/admin/components/fields/da-groups-field.gjs @@ -0,0 +1,55 @@ +import { tracked } from "@glimmer/tracking"; +import { hash } from "@ember/helper"; +import { action } from "@ember/object"; +import Group from "discourse/models/group"; +import GroupChooser from "select-kit/components/group-chooser"; +import BaseField from "./da-base-field"; +import DAFieldDescription from "./da-field-description"; +import DAFieldLabel from "./da-field-label"; + +export default class GroupsField extends BaseField { + @tracked allGroups = []; + + constructor() { + super(...arguments); + + Group.findAll({ + ignore_automatic: this.args.field.extra.ignore_automatic ?? false, + }).then((groups) => { + if (this.isDestroying || this.isDestroyed) { + return; + } + + this.allGroups = groups; + }); + } + + get maximum() { + return this.args.field.extra.maximum ?? 10; + } + + @action + setGroupField(groupIds) { + this.mutValue(groupIds); + } + + +} diff --git a/plugins/automation/app/models/discourse_automation/field.rb b/plugins/automation/app/models/discourse_automation/field.rb index 567598fc251..4dea55a2b26 100644 --- a/plugins/automation/app/models/discourse_automation/field.rb +++ b/plugins/automation/app/models/discourse_automation/field.rb @@ -188,6 +188,12 @@ module DiscourseAutomation "type" => "integer", }, }, + "groups" => { + "value" => { + "type" => "array", + "items" => [{ type: "integer" }], + }, + }, "email_group_user" => { "value" => { "type" => "array", diff --git a/plugins/automation/config/locales/client.en.yml b/plugins/automation/config/locales/client.en.yml index 28224d80fc0..9af98e2c8c5 100644 --- a/plugins/automation/config/locales/client.en.yml +++ b/plugins/automation/config/locales/client.en.yml @@ -156,6 +156,9 @@ en: restricted_group: label: Group description: Optional, will trigger only if the post's topic is a private message in this group's inbox + restricted_groups: + label: Groups + description: Optional, will trigger only if the post's topic is a private message in one of the group inboxes ignore_group_members: label: Ignore group members description: Skip the trigger if sender is a member of the Group specified above diff --git a/plugins/automation/db/migrate/20240906142121_migrate_fields_from_group_to_groups.rb b/plugins/automation/db/migrate/20240906142121_migrate_fields_from_group_to_groups.rb new file mode 100644 index 00000000000..10c6d708d5e --- /dev/null +++ b/plugins/automation/db/migrate/20240906142121_migrate_fields_from_group_to_groups.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +class MigrateFieldsFromGroupToGroups < ActiveRecord::Migration[7.1] + def up + # correct a previous erroneous migration which was converting `{}` to `{"value": [null]}` + execute <<-SQL + UPDATE discourse_automation_fields + SET + metadata = '{}'::jsonb + WHERE + metadata = '{"value": [null]}'::jsonb + AND component = 'groups' + AND name = 'restricted_groups'; + SQL + + execute <<-SQL + UPDATE discourse_automation_fields + SET + component = 'groups', + name = 'restricted_groups', + metadata = CASE + WHEN metadata != '{}'::jsonb THEN jsonb_set(metadata, '{value}', to_jsonb(ARRAY[(metadata->>'value')::int])) + ELSE metadata + END + FROM discourse_automation_automations + WHERE discourse_automation_fields.automation_id = discourse_automation_automations.id + AND discourse_automation_automations.trigger = 'post_created_edited' + AND discourse_automation_fields.name = 'restricted_group' + AND discourse_automation_fields.component = 'group'; + SQL + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/plugins/automation/lib/discourse_automation/event_handlers.rb b/plugins/automation/lib/discourse_automation/event_handlers.rb index cd0b8d75d43..3e3133024fa 100644 --- a/plugins/automation/lib/discourse_automation/event_handlers.rb +++ b/plugins/automation/lib/discourse_automation/event_handlers.rb @@ -54,15 +54,15 @@ module DiscourseAutomation next if (restricted_tags["value"] & topic.tags.map(&:name)).empty? end - restricted_group_id = automation.trigger_field("restricted_group")["value"] - if restricted_group_id.present? + restricted_group_ids = automation.trigger_field("restricted_groups")["value"] + if restricted_group_ids.present? next if !topic.private_message? target_group_ids = topic.allowed_groups.pluck(:id) - next if restricted_group_id != target_group_ids.first + next if (restricted_group_ids & target_group_ids).empty? - ignore_group_members = automation.trigger_field("ignore_group_members") - next if ignore_group_members["value"] && post.user.in_any_groups?([restricted_group_id]) + ignore_group_members = automation.trigger_field("ignore_group_members")["value"] + next if ignore_group_members && post.user.in_any_groups?(restricted_group_ids) end ignore_automated = automation.trigger_field("ignore_automated") diff --git a/plugins/automation/lib/discourse_automation/triggers/post_created_edited.rb b/plugins/automation/lib/discourse_automation/triggers/post_created_edited.rb index 9735a55efcc..9bf62631592 100644 --- a/plugins/automation/lib/discourse_automation/triggers/post_created_edited.rb +++ b/plugins/automation/lib/discourse_automation/triggers/post_created_edited.rb @@ -14,7 +14,7 @@ DiscourseAutomation::Triggerable.add(DiscourseAutomation::Triggers::POST_CREATED } field :restricted_category, component: :category field :restricted_tags, component: :tags - field :restricted_group, component: :group + field :restricted_groups, component: :groups field :ignore_automated, component: :boolean field :ignore_group_members, component: :boolean field :valid_trust_levels, component: :"trust-levels" diff --git a/plugins/automation/spec/triggers/post_created_edited_spec.rb b/plugins/automation/spec/triggers/post_created_edited_spec.rb index 159377400b2..363171eea28 100644 --- a/plugins/automation/spec/triggers/post_created_edited_spec.rb +++ b/plugins/automation/spec/triggers/post_created_edited_spec.rb @@ -164,30 +164,50 @@ describe "PostCreatedEdited" do context "when group is restricted" do fab!(:target_group) { Fabricate(:group, messageable_level: Group::ALIAS_LEVELS[:everyone]) } + fab!(:another_group) { Fabricate(:group, messageable_level: Group::ALIAS_LEVELS[:everyone]) } before do automation.upsert_field!( - "restricted_group", - "group", - { value: target_group.id }, + "restricted_groups", + "groups", + { value: [target_group.id, another_group.id] }, target: "trigger", ) end - it "fires the trigger" do - list = - capture_contexts do - PostCreator.create( - user, - basic_topic_params.merge( - target_group_names: [target_group.name], - archetype: Archetype.private_message, - ), - ) - end + context "when PM is not sent to the group" do + it "doesnt fire the trigger" do + list = + capture_contexts do + PostCreator.create( + user, + basic_topic_params.merge( + target_group_names: [Fabricate(:group).name], + archetype: Archetype.private_message, + ), + ) + end - expect(list.length).to eq(1) - expect(list[0]["kind"]).to eq("post_created_edited") + expect(list.length).to eq(0) + end + end + + context "when PM is sent to the group" do + it "fires the trigger" do + list = + capture_contexts do + PostCreator.create( + user, + basic_topic_params.merge( + target_group_names: [target_group.name], + archetype: Archetype.private_message, + ), + ) + end + + expect(list.length).to eq(1) + expect(list[0]["kind"]).to eq("post_created_edited") + end end context "when the topic is not a PM" do diff --git a/plugins/automation/test/javascripts/integration/components/da-groups-field-test.js b/plugins/automation/test/javascripts/integration/components/da-groups-field-test.js new file mode 100644 index 00000000000..07fd87fcde3 --- /dev/null +++ b/plugins/automation/test/javascripts/integration/components/da-groups-field-test.js @@ -0,0 +1,71 @@ +import { getOwner } from "@ember/owner"; +import { render } from "@ember/test-helpers"; +import { hbs } from "ember-cli-htmlbars"; +import { module, test } from "qunit"; +import { setupRenderingTest } from "discourse/tests/helpers/component-test"; +import pretender, { response } from "discourse/tests/helpers/create-pretender"; +import selectKit from "discourse/tests/helpers/select-kit-helper"; +import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators"; + +module("Integration | Component | da-groups-field", function (hooks) { + setupRenderingTest(hooks); + + hooks.beforeEach(function () { + this.automation = new AutomationFabricators(getOwner(this)).automation(); + + pretender.get("/groups/search.json", () => { + return response([ + { + id: 1, + name: "cats", + flair_url: "fa-bars", + flair_bg_color: "CC000A", + flair_color: "FFFFFA", + }, + { + id: 2, + name: "dogs", + flair_url: "fa-bars", + flair_bg_color: "CC000A", + flair_color: "FFFFFA", + }, + ]); + }); + }); + + test("set value", async function (assert) { + this.field = new AutomationFabricators(getOwner(this)).field({ + component: "groups", + }); + + await render( + hbs`` + ); + + await selectKit().expand(); + await selectKit().selectRowByValue(1); + + assert.deepEqual(this.field.metadata.value, [1]); + }); + + test("supports a maxmimum value", async function (assert) { + this.field = new AutomationFabricators(getOwner(this)).field({ + component: "groups", + extra: { maximum: 1 }, + }); + + await render( + hbs`` + ); + + await selectKit().expand(); + await selectKit().selectRowByValue(1); + + assert.deepEqual(this.field.metadata.value, [1]); + + await selectKit().expand(); + await selectKit().selectRowByValue(2); + + assert.deepEqual(this.field.metadata.value, [2]); + }); +});