mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-27 17:46:05 -05:00
FEATURE: Change user groups in bulk via admin
This commit is contained in:
parent
e1d5503053
commit
47e25648df
11 changed files with 143 additions and 0 deletions
|
@ -0,0 +1,34 @@
|
||||||
|
import computed from 'ember-addons/ember-computed-decorators';
|
||||||
|
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||||
|
|
||||||
|
export default Ember.Controller.extend({
|
||||||
|
users: null,
|
||||||
|
groupId: null,
|
||||||
|
saving: false,
|
||||||
|
|
||||||
|
@computed('saving', 'users', 'groupId')
|
||||||
|
buttonDisabled(saving, users, groupId) {
|
||||||
|
return saving || !groupId || !users || !users.length;
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
addToGroup() {
|
||||||
|
if (this.get('saving')) { return; }
|
||||||
|
|
||||||
|
const users = this.get('users').split("\n")
|
||||||
|
.uniq()
|
||||||
|
.reject(x => x.length === 0);
|
||||||
|
|
||||||
|
this.set('saving', true);
|
||||||
|
Discourse.ajax('/admin/groups/bulk', {
|
||||||
|
data: { users, group_id: this.get('groupId') },
|
||||||
|
method: 'PUT'
|
||||||
|
}).then(() => {
|
||||||
|
this.transitionToRoute('adminGroups.bulkComplete');
|
||||||
|
}).catch(popupAjaxError).finally(() => {
|
||||||
|
this.set('saving', false);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
13
app/assets/javascripts/admin/routes/admin-groups-bulk.js.es6
Normal file
13
app/assets/javascripts/admin/routes/admin-groups-bulk.js.es6
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import Group from 'discourse/models/group';
|
||||||
|
|
||||||
|
export default Ember.Route.extend({
|
||||||
|
model() {
|
||||||
|
return Group.findAll().then(groups => {
|
||||||
|
return groups.filter(g => !g.get('automatic'));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setupController(controller, groups) {
|
||||||
|
controller.setProperties({ groups, groupId: null, users: null });
|
||||||
|
}
|
||||||
|
});
|
|
@ -49,6 +49,8 @@ export default {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.resource('adminGroups', { path: '/groups' }, function() {
|
this.resource('adminGroups', { path: '/groups' }, function() {
|
||||||
|
this.route('bulk');
|
||||||
|
this.route('bulkComplete', { path: 'bulk-complete' });
|
||||||
this.resource('adminGroupsType', { path: '/:type' }, function() {
|
this.resource('adminGroupsType', { path: '/:type' }, function() {
|
||||||
this.resource('adminGroup', { path: '/:name' });
|
this.resource('adminGroup', { path: '/:name' });
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<p>{{i18n "admin.groups.bulk_complete"}}</p>
|
19
app/assets/javascripts/admin/templates/groups-bulk.hbs
Normal file
19
app/assets/javascripts/admin/templates/groups-bulk.hbs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<div class='groups-bulk'>
|
||||||
|
<p>{{i18n "admin.groups.bulk_paste"}}</p>
|
||||||
|
|
||||||
|
<div class='control'>
|
||||||
|
{{textarea value=users class="paste-users"}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='control'>
|
||||||
|
{{combo-box content=groups valueAttribute="id" value=groupId none="admin.groups.bulk_select"}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='control'>
|
||||||
|
{{d-button disabled=buttonDisabled
|
||||||
|
class="btn-primary"
|
||||||
|
action="addToGroup"
|
||||||
|
icon="plus"
|
||||||
|
label="admin.groups.bulk"}}
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -1,6 +1,7 @@
|
||||||
{{#admin-nav}}
|
{{#admin-nav}}
|
||||||
{{nav-item route='adminGroupsType' routeParam='custom' label='admin.groups.custom'}}
|
{{nav-item route='adminGroupsType' routeParam='custom' label='admin.groups.custom'}}
|
||||||
{{nav-item route='adminGroupsType' routeParam='automatic' label='admin.groups.automatic'}}
|
{{nav-item route='adminGroupsType' routeParam='automatic' label='admin.groups.automatic'}}
|
||||||
|
{{nav-item route='adminGroups.bulk' label='admin.groups.bulk'}}
|
||||||
{{/admin-nav}}
|
{{/admin-nav}}
|
||||||
|
|
||||||
<div class="admin-container">
|
<div class="admin-container">
|
||||||
|
|
|
@ -215,6 +215,11 @@ td.flaggers td {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.paste-users {
|
||||||
|
width: 400px;
|
||||||
|
height: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
.groups, .badges {
|
.groups, .badges {
|
||||||
.form-horizontal {
|
.form-horizontal {
|
||||||
label {
|
label {
|
||||||
|
@ -1015,6 +1020,11 @@ table.api-keys {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.groups-bulk {
|
||||||
|
.control {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.commits-widget {
|
.commits-widget {
|
||||||
border: solid 1px dark-light-diff($primary, $secondary, 90%, -60%);
|
border: solid 1px dark-light-diff($primary, $secondary, 90%, -60%);
|
||||||
|
|
|
@ -19,6 +19,42 @@ class Admin::GroupsController < Admin::AdminController
|
||||||
render nothing: true
|
render nothing: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def bulk
|
||||||
|
render nothing: true
|
||||||
|
end
|
||||||
|
|
||||||
|
def bulk_perform
|
||||||
|
group = Group.find(params[:group_id].to_i)
|
||||||
|
if group.present?
|
||||||
|
users = (params[:users] || []).map {|u| u.downcase}
|
||||||
|
user_ids = User.where("username_lower in (:users) OR email IN (:users)", users: users).pluck(:id)
|
||||||
|
|
||||||
|
if user_ids.present?
|
||||||
|
Group.exec_sql("INSERT INTO group_users
|
||||||
|
(group_id, user_id, created_at, updated_at)
|
||||||
|
SELECT #{group.id},
|
||||||
|
u.id,
|
||||||
|
CURRENT_TIMESTAMP,
|
||||||
|
CURRENT_TIMESTAMP
|
||||||
|
FROM users AS u
|
||||||
|
WHERE u.id IN (#{user_ids.join(', ')})
|
||||||
|
AND NOT EXISTS(SELECT 1 FROM group_users AS gu
|
||||||
|
WHERE gu.user_id = u.id AND
|
||||||
|
gu.group_id = #{group.id})")
|
||||||
|
|
||||||
|
if group.primary_group?
|
||||||
|
User.where(id: user_ids).update_all(primary_group_id: group.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
if group.title.present?
|
||||||
|
User.where(id: user_ids).update_all(title: group.title)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
render json: success_json
|
||||||
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
group = Group.new
|
group = Group.new
|
||||||
|
|
||||||
|
|
|
@ -1949,6 +1949,10 @@ en:
|
||||||
add: "Add"
|
add: "Add"
|
||||||
add_members: "Add members"
|
add_members: "Add members"
|
||||||
custom: "Custom"
|
custom: "Custom"
|
||||||
|
bulk_complete: "The users have been added to the group."
|
||||||
|
bulk: "Bulk Add to Group"
|
||||||
|
bulk_paste: "Paste a list of usernames or emails, one per line:"
|
||||||
|
bulk_select: "(select a group)"
|
||||||
automatic: "Automatic"
|
automatic: "Automatic"
|
||||||
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_retroactive: "Apply the same email domain rule to add existing registered users"
|
||||||
|
|
|
@ -60,6 +60,9 @@ Discourse::Application.routes.draw do
|
||||||
resources :groups, constraints: AdminConstraint.new do
|
resources :groups, constraints: AdminConstraint.new do
|
||||||
collection do
|
collection do
|
||||||
post "refresh_automatic_groups" => "groups#refresh_automatic_groups"
|
post "refresh_automatic_groups" => "groups#refresh_automatic_groups"
|
||||||
|
get 'bulk'
|
||||||
|
get 'bulk-complete' => 'groups#bulk'
|
||||||
|
put 'bulk' => 'groups#bulk_perform'
|
||||||
end
|
end
|
||||||
member do
|
member do
|
||||||
put "members" => "groups#add_members"
|
put "members" => "groups#add_members"
|
||||||
|
|
|
@ -36,6 +36,26 @@ describe Admin::GroupsController do
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context ".bulk" do
|
||||||
|
it "can assign users to a group by email or username" do
|
||||||
|
group = Fabricate(:group, name: "test", primary_group: true, title: 'WAT')
|
||||||
|
user = Fabricate(:user)
|
||||||
|
user2 = Fabricate(:user)
|
||||||
|
|
||||||
|
xhr :put, :bulk_perform, group_id: group.id, users: [user.username.upcase, user2.email, 'doesnt_exist']
|
||||||
|
|
||||||
|
expect(response).to be_success
|
||||||
|
|
||||||
|
user.reload
|
||||||
|
expect(user.primary_group).to eq(group)
|
||||||
|
expect(user.title).to eq("WAT")
|
||||||
|
|
||||||
|
user2.reload
|
||||||
|
expect(user2.primary_group).to eq(group)
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context ".create" do
|
context ".create" do
|
||||||
|
|
||||||
it "strip spaces on the group name" do
|
it "strip spaces on the group name" do
|
||||||
|
|
Loading…
Reference in a new issue