Add "Unassign All" button to user's assigned topics view

This commit is contained in:
Robin Ward 2018-05-04 13:40:48 -04:00
parent 7458b38f71
commit 754209ce58
11 changed files with 136 additions and 8 deletions

View File

@ -70,5 +70,11 @@ module DiscourseAssign
render json: success_json
end
def unassign_all
user = User.find_by(id: params[:user_id])
TopicAssigner.unassign_all(user, current_user)
render json: success_json
end
end
end

View File

@ -0,0 +1,28 @@
import { ajax } from 'discourse/lib/ajax';
import computed from 'ember-addons/ember-computed-decorators';
export default Ember.Controller.extend({
user: Ember.inject.controller(),
@computed('model.topics')
canUnassignAll(topics) {
return topics && topics.length && this.currentUser.get('staff');
},
actions: {
unassignAll() {
let user = this.get('user.model');
bootbox.confirm(
I18n.t("discourse_assign.unassign_all.confirm", { username: user.get('username') }),
value => {
if (value) {
ajax("/assign/unassign-all", {
type: 'PUT',
data: { user_id: user.get('id') }
}).then(() => this.send('unassignedAll'));
}
}
);
}
}
});

View File

@ -3,7 +3,30 @@ import UserTopicListRoute from "discourse/routes/user-topic-list";
export default UserTopicListRoute.extend({
userActionType: 16,
noContentHelpKey: "discourse_assigns.no_assigns",
model: function() {
return this.store.findFiltered('topicList', {filter: 'latest', params: {assigned: this.modelFor('user').get('username_lower') }});
model() {
return this.store.findFiltered(
'topicList',
{
filter: 'latest',
params: { assigned: this.modelFor('user').get('username_lower') }
}
);
},
renderTemplate() {
this.render('user-activity-assigned');
this.render('user-topics-list', { into: 'user-activity-assigned' });
},
setupController(controller, model) {
this._super(controller, model);
controller.set('model', model);
},
actions: {
unassignedAll() {
this.refresh();
}
}
});

View File

@ -0,0 +1,11 @@
{{#if canUnassignAll}}
<div class='assign-controls'>
{{d-button
action=(action "unassignAll")
icon="times"
label="discourse_assign.unassign_all.title"
class="btn-danger"}}
</div>
{{/if}}
{{outlet}}

View File

@ -1,3 +1,11 @@
.user-right {
.assign-controls {
display: flex;
justify-content: flex-end;
margin-bottom: 0.5em;
}
}
.assigned-to {
.d-icon, i.fa {
margin-right: 0.5em;

View File

@ -9,6 +9,10 @@ en:
assigned: "Assigned"
assigned_to: "Assigned to"
assign_notification: "<i title='assigned' class='fa fa-user-plus'></i><p><span>{{username}}</span> {{description}}</p>"
unassign_all:
title: "Unassign All Topics"
confirm: "Are you sure you want to unassign all topics from {{username}}?"
unassign:
title: "Unassign"
help: "Unassign Topic"

View File

@ -2,5 +2,6 @@ DiscourseAssign::Engine.routes.draw do
put "/claim/:topic_id" => "assign#claim"
put "/assign" => "assign#assign"
put "/unassign" => "assign#unassign"
put "/unassign-all" => "assign#unassign_all"
get "/suggestions" => "assign#suggestions"
end

10
jobs/unassign_bulk.rb Normal file
View File

@ -0,0 +1,10 @@
module Jobs
class UnassignBulk < Jobs::Base
def execute(args)
assigned_by = User.find(args[:assigned_by_id])
Topic.where(id: args[:topic_ids]).each do |t|
TopicAssigner.new(t, assigned_by).unassign
end
end
end
end

View File

@ -1,6 +1,25 @@
require_dependency 'email/sender'
class ::TopicAssigner
def self.unassign_all(user, assigned_by)
topic_ids = TopicCustomField.where(name: 'assigned_to_id', value: user.id).pluck(:topic_id)
# Fast path: by doing this we can instantly refresh for the user showing no assigned topics
# while doing the "full" removal asynchronously.
TopicCustomField.where(
name: ['assigned_to_id', 'assigned_by_id'],
topic_id: topic_ids
).delete_all
Jobs.enqueue(
:unassign_bulk,
user_id: user.id,
assigned_by_id: assigned_by.id,
topic_ids: topic_ids
)
end
def self.backfill_auto_assign
staff_mention = User.where('moderator OR admin')
.pluck('username')
@ -95,9 +114,7 @@ SQL
end
def assign(assign_to, silent: false)
@topic.custom_fields["assigned_to_id"] = assign_to.id
@topic.custom_fields["assigned_by_id"] = @assigned_by.id
@topic.save!
@topic.upsert_custom_fields(assigned_to_id: assign_to.id, assigned_by_id: @assigned_by.id)
first_post = @topic.posts.find_by(post_number: 1)
first_post.publish_change_to_clients!(:revised, reload_topic: true)
@ -162,9 +179,7 @@ SQL
def unassign(silent: false)
if assigned_to_id = @topic.custom_fields["assigned_to_id"]
@topic.custom_fields["assigned_to_id"] = nil
@topic.custom_fields["assigned_by_id"] = nil
@topic.save!
@topic.upsert_custom_fields(assigned_to_id: nil, assigned_by_id: nil)
post = @topic.posts.where(post_number: 1).first
return unless post.present?

View File

@ -15,6 +15,7 @@ Discourse::Application.routes.append do
end
after_initialize do
require File.expand_path('../jobs/unassign_bulk', __FILE__)
require 'topic_assigner'
# Raise an invalid access error if a user tries to act on something

View File

@ -22,4 +22,25 @@ RSpec.describe TopicAssigner do
assert_publish_topic_state(pm, user) { assigner.unassign }
end
end
context "assigning and unassigning" do
let(:post) { Fabricate(:post) }
let(:topic) { post.topic }
let(:moderator) { Fabricate(:moderator) }
let(:assigner) { TopicAssigner.new(topic, moderator) }
it "can assign and unassign correctly" do
assigner.assign(moderator)
expect(TopicQuery.new(moderator, assigned: moderator.username).list_latest.topics).to be_present
assigner.unassign
expect(TopicQuery.new(moderator, assigned: moderator.username).list_latest.topics).to be_blank
end
it "can unassign all a user's topics at once" do
assigner.assign(moderator)
TopicAssigner.unassign_all(moderator, moderator)
expect(TopicQuery.new(moderator, assigned: moderator.username).list_latest.topics).to be_blank
end
end
end