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:
parent
e6a3bc80d7
commit
3e3dc3815b
|
@ -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"))) {
|
||||||
|
|
|
@ -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();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"}}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
23
plugin.rb
23
plugin.rb
|
@ -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]
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue