mirror of
https://github.com/discourse/discourse.git
synced 2024-11-25 18:03:43 +08:00
FIX: supports groups field in post_created_edited (#28783)
⚠️ This commit is a revert of a revert due to a migration which was causing `{}` metadata to be transformed into `{"value": [null]}`. The new migration shouldn't cause this and will also clean the existing errors, there shouldn't be any data loss given the affected fields where not containing actual data. We might want to stop storing these empty fields in the future.
To achieve it, this commit does the following:
- create a new `groups field`, ideally we would have reused the existing group field, but many automations now have the expectation that this field will return a group id and not an array of group ids, which makes it a dangerous change
- alter the code in `post_created_edited` to use this new groups field and change the logic to use an array
- migrate the existing group fields post_created_edited automations to change name from `restricted_group` to `restricted_groups`, the component from `group` to `groups` and the metadata from `{"value": integer}` to `{"value": [integer]}`
<!-- NOTE: All pull requests should have tests (rspec in Ruby, qunit in JavaScript). If your code does not include test coverage, please include an explanation of why it was omitted. -->
This commit is contained in:
parent
2a5e1aa8e6
commit
ad7d5426d8
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
<template>
|
||||
<section class="field group-field">
|
||||
<div class="control-group">
|
||||
<DAFieldLabel @label={{@label}} @field={{@field}} />
|
||||
|
||||
<div class="controls">
|
||||
<GroupChooser
|
||||
@content={{this.allGroups}}
|
||||
@value={{@field.metadata.value}}
|
||||
@labelProperty="name"
|
||||
@onChange={{this.setGroupField}}
|
||||
@options={{hash maximum=this.maximum disabled=@field.isDisabled}}
|
||||
/>
|
||||
|
||||
<DAFieldDescription @description={{@description}} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
}
|
|
@ -188,6 +188,12 @@ module DiscourseAutomation
|
|||
"type" => "integer",
|
||||
},
|
||||
},
|
||||
"groups" => {
|
||||
"value" => {
|
||||
"type" => "array",
|
||||
"items" => [{ type: "integer" }],
|
||||
},
|
||||
},
|
||||
"email_group_user" => {
|
||||
"value" => {
|
||||
"type" => "array",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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")
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`<AutomationField @automation={{this.automation}} @field={{this.field}} />`
|
||||
);
|
||||
|
||||
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`<AutomationField @automation={{this.automation}} @field={{this.field}} />`
|
||||
);
|
||||
|
||||
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]);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user