FEATURE: Assign bulk actions for topics lists (#110)

- Allows unassigning and reassigning topics using bulk actions
- Adds bulk actions UI to the group assigned page
This commit is contained in:
Ahmed Gagan 2020-09-11 16:19:25 +05:30 committed by GitHub
parent e6a3bc80d7
commit 3e3dc3815b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 177 additions and 2 deletions

View File

@ -1,7 +1,9 @@
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import { inject as controller } from "@ember/controller";
export default Ember.Controller.extend({ export default Ember.Controller.extend({
topicBulkActions: controller(),
assignSuggestions: null, assignSuggestions: null,
allowedGroups: null, allowedGroups: null,
taskActions: Ember.inject.service(), taskActions: Ember.inject.service(),
@ -22,8 +24,19 @@ export default Ember.Controller.extend({
} }
}, },
bulkAction(username) {
this.topicBulkActions.performAndRefresh({
type: "assign",
username,
});
},
actions: { actions: {
assignUser(user) { assignUser(user) {
if (this.isBulkAction) {
this.bulkAction(user.username);
return;
}
this.setProperties({ this.setProperties({
"model.username": user.username, "model.username": user.username,
"model.allowedGroups": this.taskActions.allowedGroups, "model.allowedGroups": this.taskActions.allowedGroups,
@ -32,6 +45,10 @@ export default Ember.Controller.extend({
}, },
assign() { assign() {
if (this.isBulkAction) {
this.bulkAction(this.model.username);
return;
}
let path = "/assign/assign"; let path = "/assign/assign";
if (Ember.isEmpty(this.get("model.username"))) { if (Ember.isEmpty(this.get("model.username"))) {

View File

@ -1,4 +1,5 @@
import UserTopicsList from "discourse/controllers/user-topics-list"; import UserTopicsList from "discourse/controllers/user-topics-list";
import { alias } from "@ember/object/computed";
import { debounce } from "@ember/runloop"; import { debounce } from "@ember/runloop";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import { INPUT_DELAY } from "discourse-common/config/environment"; import { INPUT_DELAY } from "discourse-common/config/environment";
@ -9,6 +10,9 @@ export default UserTopicsList.extend({
order: null, order: null,
ascending: false, ascending: false,
q: "", q: "",
bulkSelectEnabled: false,
selected: [],
canBulkSelect: alias("currentUser.staff"),
queryParams: ["order", "ascending", "q"], queryParams: ["order", "ascending", "q"],
@ -61,5 +65,11 @@ export default UserTopicsList.extend({
onChangeFilter(value) { onChangeFilter(value) {
debounce(this, this._setSearchTerm, value, INPUT_DELAY * 2); debounce(this, this._setSearchTerm, value, INPUT_DELAY * 2);
}, },
toggleBulkSelect() {
this.toggleProperty("bulkSelectEnabled");
},
refresh() {
this.refreshModel();
},
}, },
}); });

View File

@ -7,6 +7,9 @@ import { queryRegistry } from "discourse/widgets/widget";
import { getOwner } from "discourse-common/lib/get-owner"; import { getOwner } from "discourse-common/lib/get-owner";
import { htmlSafe } from "@ember/template"; import { htmlSafe } from "@ember/template";
import getURL from "discourse-common/lib/get-url"; import getURL from "discourse-common/lib/get-url";
import { addBulkButton } from "discourse/controllers/topic-bulk-actions";
import TopicButtonAction from "discourse/controllers/topic-bulk-actions";
import { inject } from "@ember/controller";
import I18n from "I18n"; import I18n from "I18n";
function titleForState(user) { function titleForState(user) {
@ -320,6 +323,30 @@ export default {
return; return;
} }
const currentUser = container.lookup("current-user:main");
if (currentUser.can_assign) {
TopicButtonAction.reopen({
assignUser: inject("assign-user"),
actions: {
showReAssign() {
this.set("assignUser.isBulkAction", true);
this.set("assignUser.model", { username: "" });
this.send("changeBulkTemplate", "modal/assign-user");
},
unassignTopics() {
this.performAndRefresh({ type: "unassign" });
},
},
});
addBulkButton("showReAssign", "assign", {
icon: "user-plus",
class: "btn-default",
});
addBulkButton("unassignTopics", "unassign", {
icon: "user-times",
class: "btn-default",
});
}
withPluginApi("0.8.11", (api) => initialize(api, container)); withPluginApi("0.8.11", (api) => initialize(api, container));
withPluginApi("0.8.28", (api) => withPluginApi("0.8.28", (api) =>
registerTopicFooterButtons(api, container) registerTopicFooterButtons(api, container)

View File

@ -5,8 +5,13 @@
This causes the topic-post-badge to be considered the same word as "text" This causes the topic-post-badge to be considered the same word as "text"
at the end of the link, preventing it from line wrapping onto its own line. at the end of the link, preventing it from line wrapping onto its own line.
--}} --}}
<td class="main-link clearfix" colspan="1"> {{#if bulkSelectEnabled}}
<span class="link-top-line"> <td class="bulk-select">
<input type="checkbox" class="bulk-select">
</td>
{{/if}}
<td class='main-link clearfix' colspan="1">
<span class='link-top-line'>
{{~raw "topic-status" topic=topic}} {{~raw "topic-status" topic=topic}}
{{~#if isPrivateMessage}} {{~#if isPrivateMessage}}
{{~d-icon "envelope" class="private-message-icon"}} {{~d-icon "envelope" class="private-message-icon"}}

View File

@ -1,6 +1,7 @@
{{#unless skipHeader}} {{#unless skipHeader}}
<thead> <thead>
{{raw "topic-list-header" {{raw "topic-list-header"
canBulkSelect=canBulkSelect
toggleInTitle=toggleInTitle toggleInTitle=toggleInTitle
hideCategory=hideCategory hideCategory=hideCategory
showPosters=showPosters showPosters=showPosters
@ -17,6 +18,7 @@
<tbody> <tbody>
{{#each filteredTopics as |topic|}} {{#each filteredTopics as |topic|}}
{{assigned-topic-list-item topic=topic {{assigned-topic-list-item topic=topic
bulkSelectEnabled=bulkSelectEnabled
showTopicPostBadges=showTopicPostBadges showTopicPostBadges=showTopicPostBadges
hideCategory=hideCategory hideCategory=hideCategory
showPosters=showPosters showPosters=showPosters

View File

@ -14,10 +14,13 @@
hideCategory=hideCategory hideCategory=hideCategory
topics=topics topics=topics
expandExcerpts=expandExcerpts expandExcerpts=expandExcerpts
bulkSelectEnabled=bulkSelectEnabled
canBulkSelect=canBulkSelect
selected=selected selected=selected
skipHeader=skipHeader skipHeader=skipHeader
tagsForUser=tagsForUser tagsForUser=tagsForUser
changeSort=changeSort changeSort=changeSort
toggleBulkSelect=toggleBulkSelect
unassign=unassign unassign=unassign
reassign=reassign reassign=reassign
onScroll=onScroll onScroll=onScroll

View File

@ -8,17 +8,23 @@
</div> </div>
</div> </div>
{{#load-more class="paginated-topics-list" selector=".paginated-topics-list .topic-list tr" action=(action "loadMore")}} {{#load-more class="paginated-topics-list" selector=".paginated-topics-list .topic-list tr" action=(action "loadMore")}}
{{bulk-select-button
selected=selected
action=(action "refresh")
}}
{{basic-assigned-topic-list {{basic-assigned-topic-list
topicList=model topicList=model
hideCategory=hideCategory hideCategory=hideCategory
showPosters=showPosters showPosters=showPosters
bulkSelectEnabled=bulkSelectEnabled bulkSelectEnabled=bulkSelectEnabled
canBulkSelect=canBulkSelect
selected=selected selected=selected
hasIncoming=hasIncoming hasIncoming=hasIncoming
incomingCount=incomingCount incomingCount=incomingCount
showInserted=(action "showInserted") showInserted=(action "showInserted")
tagsForUser=tagsForUser tagsForUser=tagsForUser
changeSort=(action "changeSort") changeSort=(action "changeSort")
toggleBulkSelect=(action "toggleBulkSelect")
unassign=(action "unassign") unassign=(action "unassign")
reassign=(action "reassign") reassign=(action "reassign")
onScroll=saveScrollPosition onScroll=saveScrollPosition

View File

@ -53,3 +53,7 @@ en:
assign_event: assign_event:
name: "Assign Event" name: "Assign Event"
details: "When a user assigns or unassigns a topic." details: "When a user assigns or unassigns a topic."
topics:
bulk:
unassign: "Unassign Topics"
assign: "Assign Topics"

View File

@ -398,6 +398,29 @@ after_initialize do
end end
end end
TopicsBulkAction.register_operation("assign") do
if @user.can_assign?
assign_user = User.find_by_username(@operation[:username])
topics.each do |t|
TopicAssigner.new(t, @user).assign(assign_user)
end
end
end
TopicsBulkAction.register_operation("unassign") do
if @user.can_assign?
topics.each do |t|
if guardian.can_assign?
TopicAssigner.new(t, @user).unassign
end
end
end
end
if defined? register_permitted_bulk_action_parameter
register_permitted_bulk_action_parameter :username
end
if defined? UserBookmarkSerializer if defined? UserBookmarkSerializer
add_to_class(:user_bookmark_serializer, :assigned_to_user_id) do add_to_class(:user_bookmark_serializer, :assigned_to_user_id) do
id = topic.custom_fields[TopicAssigner::ASSIGNED_TO_ID] id = topic.custom_fields[TopicAssigner::ASSIGNED_TO_ID]

View File

@ -0,0 +1,78 @@
# frozen_string_literal: true
require 'rails_helper'
require_relative '../support/assign_allowed_group'
describe TopicsBulkAction do
fab!(:post) { Fabricate(:post) }
fab!(:post1) { Fabricate(:post) }
fab!(:post2) { Fabricate(:post) }
before do
SiteSetting.assign_enabled = true
end
let(:user) { Fabricate(:user) }
let(:user2) { Fabricate(:user) }
include_context 'A group that is allowed to assign'
before do
add_to_assign_allowed_group(user)
end
describe "assign_topics" do
it "assigns multiple topics to user" do
TopicsBulkAction.new(user, [post.topic.id, post1.topic.id], { type: 'assign', username: user.username }).perform!
assigned_topics = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics
expect(assigned_topics.length).to eq(2)
expect(assigned_topics).to contain_exactly(post.topic, post1.topic)
end
it "doesn't allows to assign to user not in assign_allowed_group" do
TopicsBulkAction.new(user, [post.topic.id, post1.topic.id], { type: 'assign', username: user2.username }).perform!
assigned_topics = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user2).topics
expect(assigned_topics.length).to eq(0)
end
it "user who is not in assign_allowed_group can't assign topics" do
TopicsBulkAction.new(user2, [post.topic.id, post1.topic.id, post2.topic.id], { type: 'assign', username: user.username }).perform!
assigned_topics = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics
expect(assigned_topics.length).to eq(0)
end
end
describe "unassign_topics" do
it "unassigns multiple topics assigned to user" do
TopicsBulkAction.new(user, [post.topic.id, post1.topic.id, post2.topic.id], { type: 'assign', username: user.username }).perform!
TopicsBulkAction.new(user, [post.topic.id, post1.topic.id], type: 'unassign').perform!
assigned_topics = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics
expect(assigned_topics.length).to eq(1)
expect(assigned_topics).to contain_exactly(post2.topic)
end
it "user who is not in assign_allowed_group can't unassign topics" do
TopicsBulkAction.new(user, [post.topic.id, post1.topic.id, post2.topic.id], { type: 'assign', username: user.username }).perform!
TopicsBulkAction.new(user2, [post.topic.id, post1.topic.id], type: 'unassign').perform!
assigned_topics = TopicQuery.new(user, { page: 0 }).list_messages_assigned(user).topics
expect(assigned_topics.length).to eq(3)
expect(assigned_topics).to contain_exactly(post.topic, post1.topic, post2.topic)
end
end
end