UX: drop the `automatic_membership_retroactive` column from groups model. (#9430)

This commit is contained in:
Vinoth Kannan 2020-04-22 22:07:39 +05:30 committed by GitHub
parent bf0c055a9a
commit df0c386f8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 144 additions and 82 deletions

View File

@ -1,6 +1,7 @@
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import Component from "@ember/component"; import Component from "@ember/component";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import { popupAutomaticMembershipAlert } from "discourse/controllers/groups-new";
export default Component.extend({ export default Component.extend({
saving: null, saving: null,
@ -14,8 +15,14 @@ export default Component.extend({
actions: { actions: {
save() { save() {
this.set("saving", true); this.set("saving", true);
const group = this.model;
return this.model popupAutomaticMembershipAlert(
group.id,
group.automatic_membership_email_domains
);
return group
.save() .save()
.then(() => { .then(() => {
this.set("saved", true); this.set("saved", true);

View File

@ -1,18 +1,54 @@
import { action } from "@ember/object"; import { action } from "@ember/object";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
export function popupAutomaticMembershipAlert(group_id, email_domains) {
if (!email_domains) {
return;
}
const data = {};
data.automatic_membership_email_domains = email_domains;
if (group_id) {
data.id = group_id;
}
ajax(`/admin/groups/automatic_membership_count.json`, {
type: "PUT",
data
}).then(result => {
const count = result.user_count;
if (count > 0) {
bootbox.alert(
I18n.t(
"admin.groups.manage.membership.automatic_membership_user_count",
{ count }
)
);
}
});
}
export default Controller.extend({ export default Controller.extend({
saving: null, saving: null,
@action @action
save() { save() {
this.set("saving", true); this.set("saving", true);
const group = this.model;
this.model popupAutomaticMembershipAlert(
group.id,
group.automatic_membership_email_domains
);
group
.create() .create()
.then(() => { .then(() => {
this.transitionToRoute("group.members", this.model.name); this.transitionToRoute("group.members", group.name);
}) })
.catch(popupAjaxError) .catch(popupAjaxError)
.finally(() => this.set("saving", false)); .finally(() => this.set("saving", false));

View File

@ -184,7 +184,6 @@ const Group = RestModel.extend({
visibility_level: this.visibility_level, visibility_level: this.visibility_level,
members_visibility_level: this.members_visibility_level, members_visibility_level: this.members_visibility_level,
automatic_membership_email_domains: this.emailDomains, automatic_membership_email_domains: this.emailDomains,
automatic_membership_retroactive: !!this.automatic_membership_retroactive,
title: this.title, title: this.title,
primary_group: !!this.primary_group, primary_group: !!this.primary_group,
grant_trust_level: this.grant_trust_level, grant_trust_level: this.grant_trust_level,

View File

@ -17,14 +17,6 @@
onChange=(action "onChangeEmailDomainsSetting") onChange=(action "onChangeEmailDomainsSetting")
options=(hash allowAny=true) options=(hash allowAny=true)
}} }}
<label>
{{input type="checkbox"
checked=model.automatic_membership_retroactive
class="groups-form-automatic-membership-retroactive"}}
{{i18n "admin.groups.manage.membership.automatic_membership_retroactive"}}
</label>
</div> </div>
{{plugin-outlet name="groups-form-membership-below-automatic" {{plugin-outlet name="groups-form-membership-below-automatic"

View File

@ -121,6 +121,28 @@ class Admin::GroupsController < Admin::AdminController
render json: success_json render json: success_json
end end
def automatic_membership_count
domains = Group.get_valid_email_domains(params.require(:automatic_membership_email_domains))
group_id = params[:id]
user_count = 0
if domains.present?
if group_id.present?
group = Group.find_by(id: group_id)
raise Discourse::NotFound unless group
return can_not_modify_automatic if group.automatic
existing_domains = group.automatic_membership_email_domains.split("|")
domains -= existing_domains
end
user_count = Group.automatic_membership_users(domains.join("|")).count
end
render json: { user_count: user_count }
end
protected protected
def can_not_modify_automatic def can_not_modify_automatic
@ -137,7 +159,6 @@ class Admin::GroupsController < Admin::AdminController
:visibility_level, :visibility_level,
:members_visibility_level, :members_visibility_level,
:automatic_membership_email_domains, :automatic_membership_email_domains,
:automatic_membership_retroactive,
:title, :title,
:primary_group, :primary_group,
:grant_trust_level, :grant_trust_level,

View File

@ -537,7 +537,6 @@ class GroupsController < ApplicationController
:name, :name,
:grant_trust_level, :grant_trust_level,
:automatic_membership_email_domains, :automatic_membership_email_domains,
:automatic_membership_retroactive,
:publish_read_state :publish_read_state
]) ])

View File

@ -11,16 +11,10 @@ module Jobs
group = Group.find_by(id: group_id) group = Group.find_by(id: group_id)
raise Discourse::InvalidParameters.new(:group_id) if group.nil? raise Discourse::InvalidParameters.new(:group_id) if group.nil?
return unless group.automatic_membership_retroactive domains = group.automatic_membership_email_domains
return if domains.blank?
domains = group.automatic_membership_email_domains.gsub('.', '\.') Group.automatic_membership_users(domains).find_each do |user|
User.joins(:user_emails)
.where("user_emails.email ~* '@(#{domains})$'")
.where("users.id NOT IN (SELECT user_id FROM group_users WHERE group_users.group_id = ?)", group_id)
.activated
.where(staged: false)
.find_each do |user|
next unless user.email_confirmed? next unless user.email_confirmed?
group.add(user, automatic: true) group.add(user, automatic: true)
GroupActionLogger.new(Discourse.system_user, group).log_add_user_to_group(user) GroupActionLogger.new(Discourse.system_user, group).log_add_user_to_group(user)

View File

@ -770,12 +770,10 @@ class Group < ActiveRecord::Base
def automatic_membership_email_domains_format_validator def automatic_membership_email_domains_format_validator
return if self.automatic_membership_email_domains.blank? return if self.automatic_membership_email_domains.blank?
domains = self.automatic_membership_email_domains.split("|") domains = Group.get_valid_email_domains(self.automatic_membership_email_domains) do |domain|
domains.each do |domain|
domain.sub!(/^https?:\/\//, '')
domain.sub!(/\/.*$/, '')
self.errors.add :base, (I18n.t('groups.errors.invalid_domain', domain: domain)) unless domain =~ /\A[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,24}(:[0-9]{1,5})?(\/.*)?\Z/i self.errors.add :base, (I18n.t('groups.errors.invalid_domain', domain: domain)) unless domain =~ /\A[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,24}(:[0-9]{1,5})?(\/.*)?\Z/i
end end
self.automatic_membership_email_domains = domains.join("|") self.automatic_membership_email_domains = domains.join("|")
end end
@ -791,7 +789,7 @@ class Group < ActiveRecord::Base
end end
def automatic_group_membership def automatic_group_membership
if self.automatic_membership_retroactive if self.automatic_membership_email_domains.present?
Jobs.enqueue(:automatic_group_membership, group_id: self.id) Jobs.enqueue(:automatic_group_membership, group_id: self.id)
end end
end end
@ -842,6 +840,32 @@ class Group < ActiveRecord::Base
end end
end end
def self.automatic_membership_users(domains, group_id = nil)
pattern = "@(#{domains.gsub('.', '\.')})$"
users = User.joins(:user_emails).where("user_emails.email ~* ?", pattern).activated.where(staged: false)
users = users.where("users.id NOT IN (SELECT user_id FROM group_users WHERE group_users.group_id = ?)", group_id) if group_id.present?
users
end
def self.get_valid_email_domains(value)
valid_domains = []
value.split("|").each do |domain|
domain.sub!(/^https?:\/\//, '')
domain.sub!(/\/.*$/, '')
if domain =~ /\A[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,24}(:[0-9]{1,5})?(\/.*)?\Z/i
valid_domains << domain
else
yield domain
end
end
valid_domains
end
private private
def validate_grant_trust_level def validate_grant_trust_level
@ -887,7 +911,6 @@ end
# automatic :boolean default(FALSE), not null # automatic :boolean default(FALSE), not null
# user_count :integer default(0), not null # user_count :integer default(0), not null
# automatic_membership_email_domains :text # automatic_membership_email_domains :text
# automatic_membership_retroactive :boolean default(FALSE)
# primary_group :boolean default(FALSE), not null # primary_group :boolean default(FALSE), not null
# title :string # title :string
# grant_trust_level :integer # grant_trust_level :integer

View File

@ -10,7 +10,6 @@ class BasicGroupSerializer < ApplicationSerializer
:messageable_level, :messageable_level,
:visibility_level, :visibility_level,
:automatic_membership_email_domains, :automatic_membership_email_domains,
:automatic_membership_retroactive,
:primary_group, :primary_group,
:title, :title,
:grant_trust_level, :grant_trust_level,
@ -56,10 +55,6 @@ class BasicGroupSerializer < ApplicationSerializer
scope.is_admin? scope.is_admin?
end end
def include_automatic_membership_retroactive?
scope.is_admin?
end
def include_has_messages? def include_has_messages?
staff? || scope.can_see_group_messages?(object) staff? || scope.can_see_group_messages?(object)
end end

View File

@ -3460,7 +3460,7 @@ en:
effects: Effects effects: Effects
trust_levels_none: "None" trust_levels_none: "None"
automatic_membership_email_domains: "Users who register with an email domain that exactly matches one in this list will be automatically added to this group:" automatic_membership_email_domains: "Users who register with an email domain that exactly matches one in this list will be automatically added to this group:"
automatic_membership_retroactive: "Apply the same email domain rule to add existing registered users" automatic_membership_user_count: "%{count} users have the new email domains and will be added to the group."
primary_group: "Automatically set as primary group" primary_group: "Automatically set as primary group"
name_placeholder: "Group name, no spaces, same as username rule" name_placeholder: "Group name, no spaces, same as username rule"
primary: "Primary Group" primary: "Primary Group"

View File

@ -94,6 +94,7 @@ Discourse::Application.routes.draw do
get 'bulk' get 'bulk'
get 'bulk-complete' => 'groups#bulk' get 'bulk-complete' => 'groups#bulk'
put 'bulk' => 'groups#bulk_perform' put 'bulk' => 'groups#bulk_perform'
put "automatic_membership_count" => "groups#automatic_membership_count"
end end
member do member do
put "owners" => "groups#add_owners" put "owners" => "groups#add_owners"

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
class DropAutomaticMembershipRetroactiveFromGroup < ActiveRecord::Migration[6.0]
DROPPED_COLUMNS ||= {
groups: %i{
automatic_membership_retroactive
}
}
def up
DROPPED_COLUMNS.each do |table, columns|
Migration::ColumnDropper.execute_drop(table, columns)
end
end
def down
raise ActiveRecord::IrreversibleMigration
end
end

View File

@ -20,7 +20,7 @@ describe Jobs::AutomaticGroupMembership do
user6 = Fabricate(:user, email: "sso2@wat.com") user6 = Fabricate(:user, email: "sso2@wat.com")
user6.create_single_sign_on_record(external_id: 456, external_email: "sso2@wat.com", last_payload: "") user6.create_single_sign_on_record(external_id: 456, external_email: "sso2@wat.com", last_payload: "")
group = Fabricate(:group, automatic_membership_email_domains: "wat.com", automatic_membership_retroactive: true) group = Fabricate(:group, automatic_membership_email_domains: "wat.com")
begin begin
automatic = nil automatic = nil

View File

@ -942,27 +942,25 @@ describe Group do
end end
describe '#automatic_group_membership' do describe '#automatic_group_membership' do
describe 'for a automatic_membership_retroactive group' do let(:group) { Fabricate(:group, automatic_membership_email_domains: "example.com") }
let(:group) { Fabricate(:group, automatic_membership_retroactive: true) }
it "should be triggered on create and update" do it "should be triggered on create and update" do
expect { group } expect { group }
.to change { Jobs::AutomaticGroupMembership.jobs.size }.by(1) .to change { Jobs::AutomaticGroupMembership.jobs.size }.by(1)
job = Jobs::AutomaticGroupMembership.jobs.last job = Jobs::AutomaticGroupMembership.jobs.last
expect(job["args"].first["group_id"]).to eq(group.id) expect(job["args"].first["group_id"]).to eq(group.id)
Jobs::AutomaticGroupMembership.jobs.clear Jobs::AutomaticGroupMembership.jobs.clear
expect do expect do
group.update!(name: 'asdiaksjdias') group.update!(name: 'asdiaksjdias')
end.to change { Jobs::AutomaticGroupMembership.jobs.size }.by(1) end.to change { Jobs::AutomaticGroupMembership.jobs.size }.by(1)
job = Jobs::AutomaticGroupMembership.jobs.last job = Jobs::AutomaticGroupMembership.jobs.last
expect(job["args"].first["group_id"]).to eq(group.id) expect(job["args"].first["group_id"]).to eq(group.id)
end
end end
end end

View File

@ -657,8 +657,7 @@ describe GroupsController do
mentionable_level: 2, mentionable_level: 2,
messageable_level: 2, messageable_level: 2,
default_notification_level: 0, default_notification_level: 0,
grant_trust_level: 0, grant_trust_level: 0
automatic_membership_retroactive: false
) )
expect do expect do
@ -668,7 +667,6 @@ describe GroupsController do
messageable_level: 1, messageable_level: 1,
visibility_level: 1, visibility_level: 1,
automatic_membership_email_domains: 'test.org', automatic_membership_email_domains: 'test.org',
automatic_membership_retroactive: true,
title: 'haha', title: 'haha',
primary_group: true, primary_group: true,
grant_trust_level: 1, grant_trust_level: 1,
@ -707,7 +705,6 @@ describe GroupsController do
expect(group.messageable_level).to eq(1) expect(group.messageable_level).to eq(1)
expect(group.default_notification_level).to eq(1) expect(group.default_notification_level).to eq(1)
expect(group.automatic_membership_email_domains).to eq(nil) expect(group.automatic_membership_email_domains).to eq(nil)
expect(group.automatic_membership_retroactive).to eq(false)
expect(group.title).to eq('haha') expect(group.title).to eq('haha')
expect(group.primary_group).to eq(false) expect(group.primary_group).to eq(false)
expect(group.incoming_email).to eq(nil) expect(group.incoming_email).to eq(nil)
@ -737,7 +734,6 @@ describe GroupsController do
group.update!( group.update!(
visibility_level: 2, visibility_level: 2,
members_visibility_level: 2, members_visibility_level: 2,
automatic_membership_retroactive: false,
grant_trust_level: 0 grant_trust_level: 0
) )
@ -748,7 +744,6 @@ describe GroupsController do
incoming_email: 'test@mail.org', incoming_email: 'test@mail.org',
primary_group: true, primary_group: true,
automatic_membership_email_domains: 'test.org', automatic_membership_email_domains: 'test.org',
automatic_membership_retroactive: true,
grant_trust_level: 2, grant_trust_level: 2,
visibility_level: 1, visibility_level: 1,
members_visibility_level: 3 members_visibility_level: 3
@ -765,7 +760,6 @@ describe GroupsController do
expect(group.visibility_level).to eq(1) expect(group.visibility_level).to eq(1)
expect(group.members_visibility_level).to eq(3) expect(group.members_visibility_level).to eq(3)
expect(group.automatic_membership_email_domains).to eq('test.org') expect(group.automatic_membership_email_domains).to eq('test.org')
expect(group.automatic_membership_retroactive).to eq(true)
expect(group.grant_trust_level).to eq(2) expect(group.grant_trust_level).to eq(2)
expect(Jobs::AutomaticGroupMembership.jobs.first["args"].first["group_id"]) expect(Jobs::AutomaticGroupMembership.jobs.first["args"].first["group_id"])

View File

@ -41,19 +41,17 @@ describe BasicGroupSerializer do
end end
describe '#automatic_membership_email_domains' do describe '#automatic_membership_email_domains' do
fab!(:group) { Fabricate(:group, automatic_membership_email_domains: 'ilovediscourse.com', automatic_membership_retroactive: true) } fab!(:group) { Fabricate(:group, automatic_membership_email_domains: 'ilovediscourse.com') }
let(:admin_guardian) { Guardian.new(Fabricate(:admin)) } let(:admin_guardian) { Guardian.new(Fabricate(:admin)) }
it 'should include email domains for admin' do it 'should include email domains for admin' do
subject = described_class.new(group, scope: admin_guardian, root: false, owner_group_ids: [group.id]) subject = described_class.new(group, scope: admin_guardian, root: false, owner_group_ids: [group.id])
expect(subject.as_json[:automatic_membership_email_domains]).to eq('ilovediscourse.com') expect(subject.as_json[:automatic_membership_email_domains]).to eq('ilovediscourse.com')
expect(subject.as_json[:automatic_membership_retroactive]).to eq(true)
end end
it 'should not include email domains for other users' do it 'should not include email domains for other users' do
subject = described_class.new(group, scope: guardian, root: false, owner_group_ids: [group.id]) subject = described_class.new(group, scope: guardian, root: false, owner_group_ids: [group.id])
expect(subject.as_json[:automatic_membership_email_domains]).to eq(nil) expect(subject.as_json[:automatic_membership_email_domains]).to eq(nil)
expect(subject.as_json[:automatic_membership_retroactive]).to eq(nil)
end end
end end

View File

@ -15,7 +15,6 @@ acceptance("Admin - User Index", {
alias_level: 99, alias_level: 99,
visible: true, visible: true,
automatic_membership_email_domains: "", automatic_membership_email_domains: "",
automatic_membership_retroactive: false,
primary_group: false, primary_group: false,
title: null, title: null,
grant_trust_level: null, grant_trust_level: null,
@ -38,7 +37,9 @@ acceptance("Admin - User Index", {
QUnit.test("can edit username", async assert => { QUnit.test("can edit username", async assert => {
pretender.put("/users/sam/preferences/username", () => [ pretender.put("/users/sam/preferences/username", () => [
200, 200,
{ "Content-Type": "application/json" }, {
"Content-Type": "application/json"
},
{ id: 2, username: "new-sam" } { id: 2, username: "new-sam" }
]); ]);

View File

@ -13,7 +13,6 @@ acceptance("Group logs", {
alias_level: 0, alias_level: 0,
visible: true, visible: true,
automatic_membership_email_domains: "", automatic_membership_email_domains: "",
automatic_membership_retroactive: false,
primary_group: true, primary_group: true,
title: "Team Snorlax", title: "Team Snorlax",
grant_trust_level: null, grant_trust_level: null,

View File

@ -13,11 +13,6 @@ QUnit.test("As an admin", async assert => {
"it should display automatic membership label" "it should display automatic membership label"
); );
assert.ok(
find(".groups-form-automatic-membership-retroactive").length === 1,
"it should display automatic membership retroactive checkbox"
);
assert.ok( assert.ok(
find(".groups-form-primary-group").length === 1, find(".groups-form-primary-group").length === 1,
"it should display set as primary group checkbox" "it should display set as primary group checkbox"

View File

@ -17,7 +17,6 @@ acceptance("Group Requests", {
messageable_level: 0, messageable_level: 0,
visibility_level: 0, visibility_level: 0,
automatic_membership_email_domains: "", automatic_membership_email_domains: "",
automatic_membership_retroactive: false,
primary_group: false, primary_group: false,
title: "Macdonald", title: "Macdonald",
grant_trust_level: null, grant_trust_level: null,

View File

@ -30,7 +30,6 @@ acceptance("Search - Full Page", {
alias_level: 0, alias_level: 0,
visible: true, visible: true,
automatic_membership_email_domains: null, automatic_membership_email_domains: null,
automatic_membership_retroactive: false,
primary_group: false, primary_group: false,
title: null, title: null,
grant_trust_level: null, grant_trust_level: null,

View File

@ -9,7 +9,6 @@ export default {
messageable_level: 99, messageable_level: 99,
visibility_level: 0, visibility_level: 0,
automatic_membership_email_domains: null, automatic_membership_email_domains: null,
automatic_membership_retroactive: false,
primary_group: false, primary_group: false,
title: null, title: null,
grant_trust_level: null, grant_trust_level: null,

View File

@ -9,7 +9,6 @@ export default {
alias_level: 0, alias_level: 0,
visible: true, visible: true,
automatic_membership_email_domains: "", automatic_membership_email_domains: "",
automatic_membership_retroactive: false,
primary_group: false, primary_group: false,
title: null, title: null,
grant_trust_level: null, grant_trust_level: null,
@ -31,7 +30,6 @@ export default {
alias_level: 99, alias_level: 99,
visible: true, visible: true,
automatic_membership_email_domains: "", automatic_membership_email_domains: "",
automatic_membership_retroactive: false,
primary_group: false, primary_group: false,
title: null, title: null,
grant_trust_level: null, grant_trust_level: null,
@ -61,7 +59,6 @@ export default {
alias_level: 0, alias_level: 0,
visible: true, visible: true,
automatic_membership_email_domains: "", automatic_membership_email_domains: "",
automatic_membership_retroactive: false,
primary_group: false, primary_group: false,
title: null, title: null,
grant_trust_level: null, grant_trust_level: null,

View File

@ -4060,7 +4060,6 @@ export default {
alias_level: 99, alias_level: 99,
visible: true, visible: true,
automatic_membership_email_domains: "", automatic_membership_email_domains: "",
automatic_membership_retroactive: false,
primary_group: false, primary_group: false,
title: null, title: null,
grant_trust_level: null, grant_trust_level: null,

View File

@ -204,7 +204,6 @@ export default {
alias_level: 0, alias_level: 0,
visible: true, visible: true,
automatic_membership_email_domains: null, automatic_membership_email_domains: null,
automatic_membership_retroactive: false,
primary_group: false, primary_group: false,
title: null title: null
}, },
@ -216,7 +215,6 @@ export default {
alias_level: 0, alias_level: 0,
visible: true, visible: true,
automatic_membership_email_domains: null, automatic_membership_email_domains: null,
automatic_membership_retroactive: false,
primary_group: false, primary_group: false,
title: null title: null
} }
@ -2250,7 +2248,11 @@ export default {
{ extras: null, description: "Most Posts", user_id: 5460 }, { extras: null, description: "Most Posts", user_id: 5460 },
{ extras: null, description: "Frequent Poster", user_id: 402 }, { extras: null, description: "Frequent Poster", user_id: 402 },
{ extras: null, description: "Frequent Poster", user_id: 5707 }, { extras: null, description: "Frequent Poster", user_id: 5707 },
{ extras: "latest", description: "Most Recent Poster", user_id: 32 } {
extras: "latest",
description: "Most Recent Poster",
user_id: 32
}
] ]
}, },
{ {
@ -2539,7 +2541,6 @@ export default {
messageable_level: 0, messageable_level: 0,
visibility_level: 0, visibility_level: 0,
automatic_membership_email_domains: null, automatic_membership_email_domains: null,
automatic_membership_retroactive: false,
primary_group: false, primary_group: false,
title: null, title: null,
grant_trust_level: null, grant_trust_level: null,
@ -2567,7 +2568,6 @@ export default {
messageable_level: 0, messageable_level: 0,
visibility_level: 0, visibility_level: 0,
automatic_membership_email_domains: null, automatic_membership_email_domains: null,
automatic_membership_retroactive: false,
primary_group: false, primary_group: false,
title: null, title: null,
grant_trust_level: null, grant_trust_level: null,
@ -2595,7 +2595,6 @@ export default {
messageable_level: 0, messageable_level: 0,
visibility_level: 0, visibility_level: 0,
automatic_membership_email_domains: null, automatic_membership_email_domains: null,
automatic_membership_retroactive: false,
primary_group: false, primary_group: false,
title: null, title: null,
grant_trust_level: null, grant_trust_level: null,
@ -2623,7 +2622,6 @@ export default {
messageable_level: 0, messageable_level: 0,
visibility_level: 0, visibility_level: 0,
automatic_membership_email_domains: null, automatic_membership_email_domains: null,
automatic_membership_retroactive: false,
primary_group: false, primary_group: false,
title: null, title: null,
grant_trust_level: null, grant_trust_level: null,