FEATURE: Reassign workflow (#231)

Build a new workflow that adds a dropdown in place of the old assign button with the ability to

Re-assign a new user / group to an assigned topic
Re-assign yourself to an assigned topic
Unassign a user / group from an assigned topic
This commit is contained in:
janzenisaac 2021-11-12 10:09:47 -06:00 committed by GitHub
parent 0e4649d507
commit 98103586b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 614 additions and 147 deletions

View File

@ -44,27 +44,11 @@ module DiscourseAssign
end
def assign
target_id = params.require(:target_id)
target_type = params.require(:target_type)
username = params.permit(:username)['username']
group_name = params.permit(:group_name)['group_name']
reassign_or_assign_target(action: "assign")
end
assign_to = username.present? ? User.find_by(username_lower: username.downcase) : Group.where("LOWER(name) = ?", group_name.downcase).first
raise Discourse::NotFound unless assign_to
raise Discourse::NotFound if !Assignment.valid_type?(target_type)
target = target_type.constantize.where(id: target_id).first
raise Discourse::NotFound unless target
# perhaps?
#Scheduler::Defer.later "assign topic" do
assign = Assigner.new(target, current_user).assign(assign_to)
if assign[:success]
render json: success_json
else
render json: translate_failure(assign[:reason], assign_to), status: 400
end
def reassign
reassign_or_assign_target(action: "reassign")
end
def assigned
@ -185,5 +169,29 @@ module DiscourseAssign
def ensure_assign_allowed
raise Discourse::InvalidAccess.new unless current_user.can_assign?
end
def reassign_or_assign_target(action:)
target_id = params.require(:target_id)
target_type = params.require(:target_type)
username = params.permit(:username)['username']
group_name = params.permit(:group_name)['group_name']
assign_to = username.present? ? User.find_by(username_lower: username.downcase) : Group.where("LOWER(name) = ?", group_name.downcase).first
raise Discourse::NotFound unless assign_to
raise Discourse::NotFound if !Assignment.valid_type?(target_type)
target = target_type.constantize.where(id: target_id).first
raise Discourse::NotFound unless target
# perhaps?
#Scheduler::Defer.later "assign topic" do
assign = Assigner.new(target, current_user).send(action, assign_to)
if assign[:success]
render json: success_json
else
render json: translate_failure(assign[:reason], assign_to), status: 400
end
end
end
end

View File

@ -5,7 +5,6 @@ import { popupAjaxError } from "discourse/lib/ajax-error";
import { not, or } from "@ember/object/computed";
import { isEmpty } from "@ember/utils";
import { action } from "@ember/object";
import discourseComputed from "discourse-common/utils/decorators";
export default Controller.extend({
topicBulkActions: controller(),
@ -39,14 +38,43 @@ export default Controller.extend({
});
},
@discourseComputed("model.targetType")
i18nSuffix(targetType) {
switch (targetType) {
case "Post":
return "_post_modal";
case "Topic":
return "_modal";
reassignOrAssignTarget(assign_action) {
if (this.isBulkAction) {
this.bulkAction(this.model.username);
return;
}
let path = "/assign/" + assign_action;
if (isEmpty(this.get("model.username"))) {
this.model.target.set("assigned_to_user", null);
}
if (isEmpty(this.get("model.group_name"))) {
this.model.target.set("assigned_to_group", null);
}
if (
isEmpty(this.get("model.username")) &&
isEmpty(this.get("model.group_name"))
) {
path = "/assign/unassign";
}
this.send("closeModal");
return ajax(path, {
type: "PUT",
data: {
username: this.get("model.username"),
group_name: this.get("model.group_name"),
target_id: this.get("model.target.id"),
target_type: this.get("model.targetType"),
},
})
.then(() => {
this.get("model.onSuccess")?.();
})
.catch(popupAjaxError);
},
@action
@ -77,48 +105,47 @@ export default Controller.extend({
@action
assign() {
this.reassignOrAssignTarget("assign");
},
@action
reassign() {
this.reassignOrAssignTarget("reassign");
},
@action
reassignUser(name) {
if (this.isBulkAction) {
this.bulkAction(this.model.username);
this.bulkAction(name);
return;
}
let path = "/assign/assign";
if (isEmpty(this.get("model.username"))) {
this.model.target.set("assigned_to_user", null);
if (this.allowedGroupsForAssignment.includes(name)) {
this.setProperties({
"model.username": null,
"model.group_name": name,
"model.allowedGroups": this.taskActions.allowedGroups,
});
} else {
this.setProperties({
"model.username": name,
"model.group_name": null,
"model.allowedGroups": this.taskActions.allowedGroups,
});
}
if (isEmpty(this.get("model.group_name"))) {
this.model.target.set("assigned_to_group", null);
if (name) {
return this.reassign();
}
if (
isEmpty(this.get("model.username")) &&
isEmpty(this.get("model.group_name"))
) {
path = "/assign/unassign";
}
this.send("closeModal");
return ajax(path, {
type: "PUT",
data: {
username: this.get("model.username"),
group_name: this.get("model.group_name"),
target_id: this.get("model.target.id"),
target_type: this.get("model.targetType"),
},
})
.then(() => {
if (this.get("model.onSuccess")) {
this.get("model.onSuccess")();
}
})
.catch(popupAjaxError);
},
@action
assignUsername(selected) {
this.assignUser(selected.firstObject);
},
@action
reassignUsername(selected) {
this.reassignUser(selected.firstObject);
},
});

View File

@ -14,9 +14,17 @@ import TopicButtonAction, {
import { inject } from "@ember/controller";
import I18n from "I18n";
import { isEmpty } from "@ember/utils";
import { registerTopicFooterDropdown } from "discourse/lib/register-topic-footer-dropdown";
const PLUGIN_ID = "discourse-assign";
const DEPENDENT_KEYS = [
"topic.assigned_to_user",
"topic.assigned_to_group",
"currentUser.can_assign",
"topic.assigned_to_user.username",
];
function titleForState(name) {
if (name) {
return I18n.t("discourse_assign.unassign.help", {
@ -27,14 +35,124 @@ function titleForState(name) {
}
}
function defaultTitle(topic) {
return titleForState(
topic.get("topic.assigned_to_user.username") ||
topic.get("topic.assigned_to_group.name")
);
}
function includeIsAssignedOnTopic(api) {
api.modifyClass("model:topic", {
pluginId: PLUGIN_ID,
isAssigned() {
return this.assigned_to_user || this.assigned_to_group;
},
});
}
function registerTopicFooterButtons(api) {
registerTopicFooterDropdown({
id: "reassign",
action(id) {
if (!this.get("currentUser.can_assign")) {
return;
}
const taskActions = getOwner(this).lookup("service:task-actions");
switch (id) {
case "unassign": {
this.set("topic.assigned_to_user", null);
this.set("topic.assigned_to_group", null);
taskActions.unassign(this.topic.id).then(() => {
this.appEvents.trigger("post-stream:refresh", {
id: this.topic.postStream.firstPostId,
});
});
break;
}
case "reassign-self": {
this.set("topic.assigned_to_user", null);
this.set("topic.assigned_to_group", null);
taskActions
.reassignUserToTopic(this.currentUser, this.topic)
.then(() => {
this.appEvents.trigger("post-stream:refresh", {
id: this.topic.postStream.firstPostId,
});
});
break;
}
case "reassign": {
taskActions.reassign(this.topic).set("model.onSuccess", () => {
this.appEvents.trigger("post-stream:refresh", {
id: this.topic.postStream.firstPostId,
});
});
break;
}
}
},
noneItem() {
const user = this.get("topic.assigned_to_user");
const group = this.get("topic.assigned_to_group");
const label = I18n.t("discourse_assign.unassign.title_w_ellipsis");
const groupLabel = I18n.t("discourse_assign.unassign.title");
if (user) {
return {
id: null,
name: htmlSafe(
`${renderAvatar(user, {
imageSize: "tiny",
ignoreTitle: true,
})}<span class="unassign-label">${label}</span>`
),
};
} else if (group) {
return {
id: null,
name: htmlSafe(
`<span class="unassign-label">${groupLabel}</span> @${group.name}...`
),
};
}
},
dependentKeys: DEPENDENT_KEYS,
classNames: ["reassign"],
content() {
const content = [
{ id: "unassign", name: I18n.t("discourse_assign.unassign.title") },
];
if (
this.topic.isAssigned() &&
this.get("topic.assigned_to_user")?.username !==
this.currentUser.username
) {
content.push({
id: "reassign-self",
name: I18n.t("discourse_assign.reassign.to_self"),
});
}
content.push({
id: "reassign",
name: I18n.t("discourse_assign.reassign.title_w_ellipsis"),
});
return content;
},
displayed() {
return !this.site.mobileView && this.topic.isAssigned();
},
});
api.registerTopicFooterButton({
id: "assign",
icon() {
const hasAssignement =
this.get("topic.assigned_to_user") ||
this.get("topic.assigned_to_group");
return hasAssignement
return this.topic.isAssigned()
? this.site.mobileView
? "user-times"
: null
@ -42,47 +160,13 @@ function registerTopicFooterButtons(api) {
},
priority: 250,
translatedTitle() {
return titleForState(
this.get("topic.assigned_to_user.username") ||
this.get("topic.assigned_to_group.name")
);
defaultTitle(this);
},
translatedAriaLabel() {
return titleForState(
this.get("topic.assigned_to_user.username") ||
this.get("topic.assigned_to_group.name")
);
defaultTitle(this);
},
translatedLabel() {
const user = this.get("topic.assigned_to_user");
const group = this.get("topic.assigned_to_group");
const label = I18n.t("discourse_assign.unassign.title");
if (user) {
if (this.site.mobileView) {
return htmlSafe(
`<span class="unassign-label"><span class="text">${label}</span><span class="username">${
user.username
}</span></span>${renderAvatar(user, {
imageSize: "small",
ignoreTitle: true,
})}`
);
} else {
return htmlSafe(
`${renderAvatar(user, {
imageSize: "tiny",
ignoreTitle: true,
})}<span class="unassign-label">${label}</span>`
);
}
} else if (group) {
return htmlSafe(
`<span class="unassign-label">${label}</span> @${group.name}`
);
} else {
return I18n.t("discourse_assign.assign.title");
}
return I18n.t("discourse_assign.assign.title");
},
action() {
if (!this.get("currentUser.can_assign")) {
@ -90,20 +174,19 @@ function registerTopicFooterButtons(api) {
}
const taskActions = getOwner(this).lookup("service:task-actions");
const topic = this.topic;
if (topic.assigned_to_user || topic.assigned_to_group) {
if (this.topic.isAssigned()) {
this.set("topic.assigned_to_user", null);
this.set("topic.assigned_to_group", null);
taskActions.unassign(topic.id, "Topic").then(() => {
taskActions.unassign(this.topic.id, "Topic").then(() => {
this.appEvents.trigger("post-stream:refresh", {
id: topic.postStream.firstPostId,
id: this.topic.postStream.firstPostId,
});
});
} else {
taskActions.assign(topic).set("model.onSuccess", () => {
taskActions.assign(this.topic).set("model.onSuccess", () => {
this.appEvents.trigger("post-stream:refresh", {
id: topic.postStream.firstPostId,
id: this.topic.postStream.firstPostId,
});
});
}
@ -112,14 +195,183 @@ function registerTopicFooterButtons(api) {
return this.site.mobileView;
},
classNames: ["assign"],
dependentKeys: [
"topic.assigned_to_user",
"topic.assigned_to_group",
"currentUser.can_assign",
"topic.assigned_to_user.username",
],
dependentKeys: DEPENDENT_KEYS,
displayed() {
return this.currentUser && this.currentUser.can_assign;
return this.currentUser?.can_assign && !this.topic.isAssigned();
},
});
api.registerTopicFooterButton({
id: "unassign-mobile-header",
translatedTitle() {
defaultTitle(this);
},
translatedAriaLabel() {
defaultTitle(this);
},
translatedLabel() {
const user = this.get("topic.assigned_to_user");
const group = this.get("topic.assigned_to_group");
const label = I18n.t("discourse_assign.assigned_to_w_ellipsis");
if (user) {
return htmlSafe(
`<span class="unassign-label"><span class="text">${label}</span><span class="username">${
user.username
}</span></span>${renderAvatar(user, {
imageSize: "small",
ignoreTitle: true,
})}`
);
} else if (group) {
return htmlSafe(
`<span class="unassign-label">${label}</span> @${group.name}`
);
}
},
dropdown() {
return this.currentUser?.can_assign && this.topic.isAssigned();
},
classNames: ["assign"],
dependentKeys: DEPENDENT_KEYS,
displayed() {
// only display the button in the mobile view
return this.site.mobileView;
},
});
api.registerTopicFooterButton({
id: "unassign-mobile",
icon() {
return "user-times";
},
translatedTitle() {
defaultTitle(this);
},
translatedAriaLabel() {
defaultTitle(this);
},
translatedLabel() {
const label = I18n.t("discourse_assign.unassign.title");
return htmlSafe(
`<span class="unassign-label"><span class="text">${label}</span></span>`
);
},
action() {
if (!this.get("currentUser.can_assign")) {
return;
}
const taskActions = getOwner(this).lookup("service:task-actions");
this.set("topic.assigned_to_user", null);
this.set("topic.assigned_to_group", null);
taskActions.unassign(this.topic.id).then(() => {
this.appEvents.trigger("post-stream:refresh", {
id: this.topic.postStream.firstPostId,
});
});
},
dropdown() {
return this.currentUser?.can_assign && this.topic.isAssigned();
},
classNames: ["assign"],
dependentKeys: DEPENDENT_KEYS,
displayed() {
// only display the button in the mobile view
return this.site.mobileView && this.topic.isAssigned();
},
});
api.registerTopicFooterButton({
id: "reassign-self-mobile",
icon() {
return "user-plus";
},
translatedTitle() {
defaultTitle(this);
},
translatedAriaLabel() {
defaultTitle(this);
},
translatedLabel() {
const label = I18n.t("discourse_assign.reassign.to_self");
return htmlSafe(
`<span class="unassign-label"><span class="text">${label}</span></span>`
);
},
action() {
if (!this.get("currentUser.can_assign")) {
return;
}
const taskActions = getOwner(this).lookup("service:task-actions");
this.set("topic.assigned_to_user", null);
this.set("topic.assigned_to_group", null);
taskActions.reassignUserToTopic(this.currentUser, this.topic).then(() => {
this.appEvents.trigger("post-stream:refresh", {
id: this.topic.postStream.firstPostId,
});
});
},
dropdown() {
return this.currentUser?.can_assign && this.topic.isAssigned();
},
classNames: ["assign"],
dependentKeys: DEPENDENT_KEYS,
displayed() {
return (
// only display the button in the mobile view
this.site.mobileView &&
this.topic.isAssigned() &&
this.get("topic.assigned_to_user")?.username !==
this.currentUser.username
);
},
});
api.registerTopicFooterButton({
id: "reassign-mobile",
icon() {
return "user-plus";
},
translatedTitle() {
defaultTitle(this);
},
translatedAriaLabel() {
defaultTitle(this);
},
translatedLabel() {
const label = I18n.t("discourse_assign.reassign.title_w_ellipsis");
return htmlSafe(
`<span class="unassign-label"><span class="text">${label}</span></span>`
);
},
action() {
if (!this.get("currentUser.can_assign")) {
return;
}
const taskActions = getOwner(this).lookup("service:task-actions");
taskActions.reassign(this.topic).set("model.onSuccess", () => {
this.appEvents.trigger("post-stream:refresh", {
id: this.topic.postStream.firstPostId,
});
});
},
dropdown() {
return this.currentUser?.can_assign && this.topic.isAssigned();
},
classNames: ["assign"],
dependentKeys: DEPENDENT_KEYS,
displayed() {
// only display the button in the mobile view
return this.site.mobileView;
},
});
}
@ -240,16 +492,19 @@ function initialize(api) {
api.addPostSmallActionIcon("unassigned_group", "group-times");
api.addPostSmallActionIcon("unassigned_from_post", "user-times");
api.addPostSmallActionIcon("unassigned_group_from_post", "group-times");
api.includePostAttributes("assigned_to_user", "assigned_to_group");
api.addPostSmallActionIcon("reassigned", "user-plus");
api.addPostSmallActionIcon("reassigned_group", "group-plus");
api.addPostTransformCallback((transformed) => {
if (
[
"assigned",
"unassigned",
"reassigned",
"assigned_group",
"unassigned_group",
"reassigned_group",
"assigned_to_post",
"assigned_group_to_post",
"unassigned_from_post",
@ -592,6 +847,7 @@ export default {
});
}
withPluginApi("0.13.0", (api) => includeIsAssignedOnTopic(api));
withPluginApi("0.11.0", (api) => initialize(api));
withPluginApi("0.8.28", (api) => registerTopicFooterButtons(api));

View File

@ -3,6 +3,15 @@ import { ajax } from "discourse/lib/ajax";
import showModal from "discourse/lib/show-modal";
export default Service.extend({
i18nSuffix(targetType) {
switch (targetType) {
case "Post":
return "_post_modal";
case "Topic":
return "_modal";
}
},
unassign(targetId, targetType = "Topic") {
return ajax("/assign/unassign", {
type: "PUT",
@ -15,22 +24,47 @@ export default Service.extend({
assign(target, targetType = "Topic") {
return showModal("assign-user", {
title: "discourse_assign.assign" + this.i18nSuffix(targetType) + ".title",
model: {
username: target.get("assigned_to_user.username"),
group_name: target.get("assigned_to_group.name"),
description:
"discourse_assign.assign" +
this.i18nSuffix(targetType) +
".description",
username: target.assigned_to_user?.username,
group_name: target.assigned_to_group?.name,
target,
targetType,
},
});
},
assignUserToTopic(user, topic) {
return ajax("/assign/assign", {
reassignUserToTopic(user, target, targetType = "Topic") {
return ajax("/assign/reassign", {
type: "PUT",
data: {
username: user.username,
target_id: topic.id,
target_type: "Topic",
target_id: target.id,
target_type: targetType,
},
});
},
reassign(target, targetType = "Topic") {
return showModal("assign-user", {
title:
"discourse_assign.assign" +
this.i18nSuffix(targetType) +
".reassign_title",
model: {
description:
"discourse_assign.reassign" +
this.i18nSuffix(targetType) +
".description",
reassign: true,
username: target.assigned_to_user?.username,
group_name: target.assigned_to_group?.name,
target,
targetType,
},
});
},

View File

@ -1,10 +1,10 @@
{{#d-modal-body title=(concat "discourse_assign.assign" i18nSuffix ".title") class="assign"}}
{{#d-modal-body class="assign"}}
<div>
<p>{{i18n (concat "discourse_assign.assign" i18nSuffix ".description")}}</p>
<p>{{i18n model.description}}</p>
{{email-group-user-chooser
autocomplete="off"
value=assigneeName
onChange=(action "assignUsername")
onChange=(action (if model.reassign "reassignUsername" "assignUsername"))
autofocus="autofocus"
options=(hash
placementStrategy="absolute"
@ -19,7 +19,7 @@
}}
<div class="assign-suggestions">
{{#each assignSuggestions as |user|}}
<a href {{action "assignUser" user.username }}>
<a href {{action (if model.reassign "reassignUser" "assignUser") user.username }}>
{{avatar user imageSize="small"}}
</a>
{{/each}}
@ -29,9 +29,9 @@
<div class="modal-footer">
{{d-button
label="discourse_assign.assign_modal.assign"
label= (if model.reassign "discourse_assign.reassign.title" "discourse_assign.assign_modal.assign")
icon=inviteIcon
action=(action "assign")
action=(action (if model.reassign "reassign" "assign"))
class="btn-primary"
disabled=disabled
}}

View File

@ -107,6 +107,12 @@
}
}
.topic-footer-dropdown {
.avatar {
margin-right: 0.25em;
}
}
// Group assigns sidebar nav
.group-assignments {

View File

@ -5,6 +5,11 @@
}
}
.select-kit-row[data-value="unassign-mobile-header"] {
background: rgba(var(--primary-low-rgb), 0.5);
pointer-events: none;
}
.select-kit-row.assign {
.name {
display: flex;

View File

@ -13,6 +13,8 @@ en:
unassigned_group: "unassigned %{who} %{when}"
unassigned_from_post: "unassigned %{who} from <a href='%{path}' data-auto-route='true'>post</a> %{when}"
unassigned_group_from_post: "unassigned %{who} from <a href='%{path}' data-auto-route='true'>post</a> %{when}"
reassigned: "Re-assigned %{who} %{when}"
reassigned_group: "Re-assigned %{who} %{when}"
discourse_assign:
add_unassigned_filter: "Add 'unassigned' filter to category"
cant_act: "You cannot act on flags that have been assigned to other users"
@ -22,10 +24,12 @@ en:
assigned: "Assigned"
group_everyone: "Everyone"
assigned_to: "Assigned to"
assigned_to_w_ellipsis: "Assigned to..."
assign_notification: "<p><span>%{username}</span> %{description}</p>"
assign_group_notification: "<p><span>%{username}</span> %{description}</p>"
unassign:
title: "Unassign"
title_w_ellipsis: "Unassign..."
help: "Unassign %{username} from Topic"
assign:
title: "Assign"
@ -37,9 +41,15 @@ en:
help: "Unassign %{username} from Post"
reassign:
title: "Re-assign"
title_w_ellipsis: "Re-assign to..."
to_self: "Re-assign to me"
help: "Re-assign Topic to a different user"
reassign_modal:
title: "Re-assign Topic"
description: "Enter the name of the user you'd like to Re-assign this topic"
assign_modal:
title: "Assign Topic"
reassign_title: "Re-assign Topic"
description: "Enter the name of the user you'd like to assign this topic"
assign: "Assign"
assign_post_modal:

View File

@ -3,6 +3,7 @@
DiscourseAssign::Engine.routes.draw do
put "/claim/:topic_id" => "assign#claim"
put "/assign" => "assign#assign"
put "/reassign" => "assign#reassign"
put "/unassign" => "assign#unassign"
get "/suggestions" => "assign#suggestions"
get "/assigned" => "assign#assigned"

View File

@ -180,7 +180,7 @@ class ::Assigner
topic.posts.where(post_number: 1).first
end
def assign(assign_to, silent: false)
def forbidden_reasons(assign_to:, type:)
forbidden_reason =
case
when assign_to.is_a?(User) && !can_assignee_see_target?(assign_to)
@ -189,19 +189,18 @@ class ::Assigner
topic.private_message? ? :forbidden_group_assignee_not_pm_participant : :forbidden_group_assignee_cant_see_topic
when !can_be_assigned?(assign_to)
assign_to.is_a?(User) ? :forbidden_assign_to : :forbidden_group_assign_to
when topic.assignment&.assigned_to_id == assign_to.id
when topic.assignment&.assigned_to_id == assign_to.id && topic.assignment&.assigned_to_type == type
assign_to.is_a?(User) ? :already_assigned : :group_already_assigned
when Assignment.where(topic: topic).count >= ASSIGNMENTS_PER_TOPIC_LIMIT
:too_many_assigns_for_topic
when !can_assign_to?(assign_to)
:too_many_assigns
end
end
return { success: false, reason: forbidden_reason } if forbidden_reason
def assign_or_reassign_target(assign_to:, type:, silent:, action_code:)
@target.assignment&.destroy!
type = assign_to.is_a?(User) ? "User" : "Group"
assignment = @target.create_assignment!(assigned_to_id: assign_to.id, assigned_to_type: type, assigned_by_user_id: @assigned_by.id, topic_id: topic.id)
first_post.publish_change_to_clients!(:revised, reload_topic: true)
@ -262,7 +261,7 @@ class ::Assigner
nil,
bump: false,
post_type: SiteSetting.assigns_public ? Post.types[:small_action] : Post.types[:whisper],
action_code: moderator_post_assign_action_code(assignment),
action_code: moderator_post_assign_action_code(assignment, action_code),
custom_fields: custom_fields
)
end
@ -296,6 +295,26 @@ class ::Assigner
{ success: true }
end
def assign(assign_to, silent: false)
type = assign_to.is_a?(User) ? "User" : "Group"
forbidden_reason = forbidden_reasons(assign_to: assign_to, type: type)
return { success: false, reason: forbidden_reason } if forbidden_reason
action_code = { user: "assigned", group: "assigned_group" }
assign_or_reassign_target(assign_to: assign_to, type: type, silent: silent, action_code: action_code)
end
def reassign(assign_to, silent: false)
type = assign_to.is_a?(User) ? "User" : "Group"
forbidden_reason = forbidden_reasons(assign_to: assign_to, type: type)
return { success: false, reason: forbidden_reason } if forbidden_reason
action_code = { user: "reassigned", group: "reassigned_group" }
assign_or_reassign_target(assign_to: assign_to, type: type, silent: silent, action_code: action_code)
end
def unassign(silent: false)
if assignment = @target.assignment
assignment.destroy!
@ -386,15 +405,15 @@ class ::Assigner
private
def moderator_post_assign_action_code(assignment)
def moderator_post_assign_action_code(assignment, action_code)
suffix =
if assignment.target.is_a?(Post)
"_to_post"
elsif assignment.target.is_a?(Topic)
""
end
return "assigned#{suffix}" if assignment.assigned_to_user?
return "assigned_group#{suffix}" if assignment.assigned_to_group?
return "#{action_code[:user]}#{suffix}" if assignment.assigned_to_user?
return "#{action_code[:group]}#{suffix}" if assignment.assigned_to_group?
end
def moderator_post_unassign_action_code(assignment)

View File

@ -8,15 +8,9 @@ import {
import { visit } from "@ember/test-helpers";
import { cloneJSON } from "discourse-common/lib/object";
import topicFixtures from "discourse/tests/fixtures/topic";
import selectKit from "discourse/tests/helpers/select-kit-helper";
acceptance("Discourse Assign | Assigned topic", function (needs) {
needs.user();
needs.settings({
assign_enabled: true,
tagging_enabled: true,
assigns_user_url_path: "/",
});
function assignCurrentUserToTopic(needs) {
needs.pretender((server, helper) => {
server.get("/t/44.json", () => {
let topic = cloneJSON(topicFixtures["/t/28830/1.json"]);
@ -42,6 +36,45 @@ acceptance("Discourse Assign | Assigned topic", function (needs) {
return helper.response(topic);
});
});
}
function assignNewUserToTopic(needs) {
needs.pretender((server, helper) => {
server.get("/t/44.json", () => {
let topic = cloneJSON(topicFixtures["/t/28830/1.json"]);
topic["assigned_to_user"] = {
username: "isaacjanzen",
name: "Isaac Janzen",
avatar_template:
"/letter_avatar/isaacjanzen/{size}/3_f9720745f5ce6dfc2b5641fca999d934.png",
};
topic["indirectly_assigned_to"] = {
2: {
name: "Developers",
},
};
return helper.response(topic);
});
server.get("/t/45.json", () => {
let topic = cloneJSON(topicFixtures["/t/28830/1.json"]);
topic["assigned_to_group"] = {
name: "Developers",
};
return helper.response(topic);
});
});
}
acceptance("Discourse Assign | Assigned topic", function (needs) {
needs.user();
needs.settings({
assign_enabled: true,
tagging_enabled: true,
assigns_user_url_path: "/",
});
assignCurrentUserToTopic(needs);
test("Shows user assignment info", async (assert) => {
updateCurrentUser({ can_assign: true });
@ -59,8 +92,8 @@ acceptance("Discourse Assign | Assigned topic", function (needs) {
);
assert.ok(exists("#post_1 .assigned-to svg.d-icon-user-plus"));
assert.ok(
exists("#topic-footer-button-assign .unassign-label"),
"shows unassign button at the bottom of the topic"
exists("#topic-footer-dropdown-reassign"),
"shows reassign dropdown at the bottom of the topic"
);
});
@ -80,8 +113,76 @@ acceptance("Discourse Assign | Assigned topic", function (needs) {
);
assert.ok(exists("#post_1 .assigned-to svg.d-icon-group-plus"));
assert.ok(
exists("#topic-footer-button-assign .unassign-label"),
"shows unassign button at the bottom of the topic"
exists("#topic-footer-dropdown-reassign"),
"shows reassign dropdown at the bottom of the topic"
);
});
});
acceptance("Discourse Assign | Re-assign topic", function (needs) {
needs.user();
needs.settings({
assign_enabled: true,
tagging_enabled: true,
assigns_user_url_path: "/",
});
assignNewUserToTopic(needs);
test("Re-assign Footer dropdown contains reassign buttons", async (assert) => {
updateCurrentUser({ can_assign: true });
const menu = selectKit("#topic-footer-dropdown-reassign");
await visit("/t/assignment-topic/44");
await menu.expand();
assert.ok(menu.rowByValue("unassign").exists());
assert.ok(menu.rowByValue("reassign").exists());
assert.ok(menu.rowByValue("reassign-self").exists());
});
});
acceptance("Discourse Assign | Re-assign topic | mobile", function (needs) {
needs.user();
needs.mobileView();
needs.settings({
assign_enabled: true,
tagging_enabled: true,
assigns_user_url_path: "/",
});
assignNewUserToTopic(needs);
test("Mobile Footer dropdown contains reassign buttons", async (assert) => {
updateCurrentUser({ can_assign: true });
const menu = selectKit(".topic-footer-mobile-dropdown");
await visit("/t/assignment-topic/44");
await menu.expand();
assert.ok(menu.rowByValue("unassign-mobile").exists());
assert.ok(menu.rowByValue("reassign-mobile").exists());
assert.ok(menu.rowByValue("reassign-self-mobile").exists());
});
});
acceptance("Discourse Assign | Re-assign topic conditionals", function (needs) {
needs.user();
needs.settings({
assign_enabled: true,
tagging_enabled: true,
assigns_user_url_path: "/",
});
assignCurrentUserToTopic(needs);
test("Reassign Footer dropdown won't display reassign-to-self button when already assigned to current user", async (assert) => {
updateCurrentUser({ can_assign: true });
const menu = selectKit("#topic-footer-dropdown-reassign");
await visit("/t/assignment-topic/44");
await menu.expand();
assert.notOk(menu.rowByValue("reassign-self").exists());
});
});