FEATURE: add user status to user preferences (#18532)
This commit is contained in:
parent
231dc10bbd
commit
0fe111e492
|
@ -11,6 +11,7 @@ import getURL from "discourse-common/lib/get-url";
|
|||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { next } from "@ember/runloop";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
|
||||
export default Controller.extend(CanCheckEmails, {
|
||||
dialog: service(),
|
||||
|
@ -22,16 +23,19 @@ export default Controller.extend(CanCheckEmails, {
|
|||
"title",
|
||||
"primary_group_id",
|
||||
"flair_group_id",
|
||||
"status",
|
||||
];
|
||||
this.set("revoking", {});
|
||||
},
|
||||
|
||||
canEditName: setting("enable_names"),
|
||||
canSelectUserStatus: setting("enable_user_status"),
|
||||
canSaveUser: true,
|
||||
|
||||
newNameInput: null,
|
||||
newTitleInput: null,
|
||||
newPrimaryGroupInput: null,
|
||||
newStatus: null,
|
||||
|
||||
revoking: null,
|
||||
|
||||
|
@ -146,6 +150,19 @@ export default Controller.extend(CanCheckEmails, {
|
|||
});
|
||||
},
|
||||
|
||||
@action
|
||||
showUserStatusModal(status) {
|
||||
showModal("user-status", {
|
||||
title: "user_status.set_custom_status",
|
||||
modalClass: "user-status",
|
||||
model: {
|
||||
status,
|
||||
saveAction: async (s) => this.set("newStatus", s),
|
||||
deleteAction: async () => this.set("newStatus", null),
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
save() {
|
||||
this.set("saved", false);
|
||||
|
@ -155,6 +172,7 @@ export default Controller.extend(CanCheckEmails, {
|
|||
title: this.newTitleInput,
|
||||
primary_group_id: this.newPrimaryGroupInput,
|
||||
flair_group_id: this.newFlairGroupId,
|
||||
status: this.newStatus,
|
||||
});
|
||||
|
||||
return this.model
|
||||
|
|
|
@ -69,6 +69,7 @@ let userFields = [
|
|||
"user_notification_schedule",
|
||||
"sidebar_category_ids",
|
||||
"sidebar_tag_names",
|
||||
"status",
|
||||
];
|
||||
|
||||
export function addSaveableUserField(fieldName) {
|
||||
|
|
|
@ -31,6 +31,7 @@ export default RestrictedUserRoute.extend({
|
|||
newTitleInput: user.get("title"),
|
||||
newPrimaryGroupInput: user.get("primary_group_id"),
|
||||
newFlairGroupId: user.get("flair_group_id"),
|
||||
newStatus: user.status,
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -173,6 +173,24 @@
|
|||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.canSelectUserStatus}}
|
||||
<div class="control-group pref-user-status">
|
||||
<label class="control-label">{{i18n "user.status.title"}}</label>
|
||||
<div class="controls">
|
||||
{{#if this.newStatus}}
|
||||
<UserStatusMessage @status={{this.newStatus}} @showDescription={{true}} />
|
||||
{{else}}
|
||||
<span class="static">{{i18n "user.status.not_set"}}</span>
|
||||
{{/if}}
|
||||
<DButton
|
||||
@action={{action "showUserStatusModal"}}
|
||||
@actionParam={{this.newStatus}}
|
||||
@class="btn-default btn-small pad-left"
|
||||
@icon="pencil-alt"/>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.canSelectPrimaryGroup}}
|
||||
<div class="control-group pref-primary-group">
|
||||
<label class="control-label">{{i18n "user.primary_group.title"}}</label>
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
import {
|
||||
acceptance,
|
||||
exists,
|
||||
query,
|
||||
updateCurrentUser,
|
||||
} from "discourse/tests/helpers/qunit-helpers";
|
||||
import { click, fillIn, visit } from "@ember/test-helpers";
|
||||
import { test } from "qunit";
|
||||
|
||||
async function openUserStatusModal() {
|
||||
await click(".pref-user-status .btn-default");
|
||||
}
|
||||
|
||||
async function pickEmoji(emoji) {
|
||||
await click(".btn-emoji");
|
||||
await fillIn(".emoji-picker-content .filter", emoji);
|
||||
await click(".results .emoji");
|
||||
}
|
||||
|
||||
async function setStatus(status) {
|
||||
await openUserStatusModal();
|
||||
await pickEmoji(status.emoji);
|
||||
await fillIn(".user-status-description", status.description);
|
||||
await click(".modal-footer .btn-primary"); // save and close modal
|
||||
}
|
||||
|
||||
acceptance("User Profile - Account - User Status", function (needs) {
|
||||
const username = "eviltrout";
|
||||
const status = {
|
||||
emoji: "tooth",
|
||||
description: "off to dentist",
|
||||
};
|
||||
|
||||
needs.user({ username, status });
|
||||
|
||||
test("doesn't render status block if status is disabled in site settings", async function (assert) {
|
||||
this.siteSettings.enable_user_status = false;
|
||||
await visit(`/u/${username}/preferences/account`);
|
||||
assert.notOk(exists(".pref-user-status"));
|
||||
});
|
||||
|
||||
test("renders status block if status is enabled in site settings", async function (assert) {
|
||||
this.siteSettings.enable_user_status = true;
|
||||
|
||||
await visit(`/u/${username}/preferences/account`);
|
||||
|
||||
assert.ok(
|
||||
exists(".pref-user-status .user-status-message"),
|
||||
"status is shown"
|
||||
);
|
||||
assert.ok(
|
||||
exists(`.pref-user-status .emoji[alt='${status.emoji}']`),
|
||||
"status emoji is correct"
|
||||
);
|
||||
assert.equal(
|
||||
query(
|
||||
`.pref-user-status .user-status-message-description`
|
||||
).innerText.trim(),
|
||||
status.description,
|
||||
"status description is correct"
|
||||
);
|
||||
});
|
||||
|
||||
test("the status modal sets status", async function (assert) {
|
||||
this.siteSettings.enable_user_status = true;
|
||||
updateCurrentUser({ status: null });
|
||||
|
||||
await visit(`/u/${username}/preferences/account`);
|
||||
assert.notOk(
|
||||
exists(".pref-user-status .user-status-message"),
|
||||
"status isn't shown"
|
||||
);
|
||||
|
||||
await setStatus(status);
|
||||
|
||||
assert.ok(
|
||||
exists(".pref-user-status .user-status-message"),
|
||||
"status is shown"
|
||||
);
|
||||
assert.ok(
|
||||
exists(`.pref-user-status .emoji[alt='${status.emoji}']`),
|
||||
"status emoji is correct"
|
||||
);
|
||||
assert.equal(
|
||||
query(
|
||||
`.pref-user-status .user-status-message-description`
|
||||
).innerText.trim(),
|
||||
status.description,
|
||||
"status description is correct"
|
||||
);
|
||||
});
|
||||
|
||||
test("the status modal updates status", async function (assert) {
|
||||
this.siteSettings.enable_user_status = true;
|
||||
|
||||
await visit(`/u/${username}/preferences/account`);
|
||||
const newStatus = { emoji: "surfing_man", description: "surfing" };
|
||||
await setStatus(newStatus);
|
||||
|
||||
assert.ok(
|
||||
exists(".pref-user-status .user-status-message"),
|
||||
"status is shown"
|
||||
);
|
||||
assert.ok(
|
||||
exists(`.pref-user-status .emoji[alt='${newStatus.emoji}']`),
|
||||
"status emoji is correct"
|
||||
);
|
||||
assert.equal(
|
||||
query(
|
||||
`.pref-user-status .user-status-message-description`
|
||||
).innerText.trim(),
|
||||
newStatus.description,
|
||||
"status description is correct"
|
||||
);
|
||||
});
|
||||
|
||||
test("the status modal clears status", async function (assert) {
|
||||
this.siteSettings.enable_user_status = true;
|
||||
|
||||
await visit(`/u/${username}/preferences/account`);
|
||||
await openUserStatusModal();
|
||||
await click(".btn.delete-status");
|
||||
|
||||
assert.notOk(
|
||||
exists(".pref-user-status .user-status-message"),
|
||||
"status isn't shown"
|
||||
);
|
||||
});
|
||||
});
|
|
@ -1965,6 +1965,10 @@ class UsersController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
if SiteSetting.enable_user_status
|
||||
permitted << { status: [:emoji, :description, :ends_at] }
|
||||
end
|
||||
|
||||
result = params
|
||||
.permit(permitted, theme_ids: [], seen_popups: [])
|
||||
.reverse_merge(
|
||||
|
|
|
@ -218,9 +218,17 @@ class UserUpdater
|
|||
update_sidebar_tag_section_links(attributes[:sidebar_tag_names])
|
||||
end
|
||||
|
||||
if SiteSetting.enable_user_status?
|
||||
update_user_status(attributes[:status])
|
||||
end
|
||||
|
||||
name_changed = user.name_changed?
|
||||
if (saved = (!save_options || user.user_option.save) && (user_notification_schedule.nil? || user_notification_schedule.save) && user_profile.save && user.save) &&
|
||||
(name_changed && old_user_name.casecmp(attributes.fetch(:name)) != 0)
|
||||
saved = (!save_options || user.user_option.save) &&
|
||||
(user_notification_schedule.nil? || user_notification_schedule.save) &&
|
||||
user_profile.save &&
|
||||
user.save
|
||||
|
||||
if saved && (name_changed && old_user_name.casecmp(attributes.fetch(:name)) != 0)
|
||||
|
||||
StaffActionLogger.new(@actor).log_name_change(
|
||||
user.id,
|
||||
|
@ -341,6 +349,14 @@ class UserUpdater
|
|||
end
|
||||
end
|
||||
|
||||
def update_user_status(status)
|
||||
if status.blank?
|
||||
@user.clear_status!
|
||||
else
|
||||
@user.set_status!(status[:description], status[:emoji], status[:ends_at])
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :user, :guardian
|
||||
|
||||
def format_url(website)
|
||||
|
|
|
@ -1793,6 +1793,9 @@ en:
|
|||
title: "Flair"
|
||||
none: "(none)"
|
||||
instructions: "icon displayed next to your profile picture"
|
||||
status:
|
||||
title: "Custom Status"
|
||||
not_set: "Not set"
|
||||
primary_group:
|
||||
title: "Primary Group"
|
||||
none: "(none)"
|
||||
|
|
|
@ -2472,6 +2472,224 @@ RSpec.describe UsersController do
|
|||
expect(response.status).to eq(400)
|
||||
end
|
||||
end
|
||||
|
||||
context "with user status" do
|
||||
context "as a regular user" do
|
||||
before do
|
||||
SiteSetting.enable_user_status = true
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it "sets user status" do
|
||||
status = {
|
||||
emoji: "tooth",
|
||||
description: "off to dentist",
|
||||
}
|
||||
|
||||
put "/u/#{user.username}.json", params: {
|
||||
status: status
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
user.reload
|
||||
expect(user.user_status).not_to be_nil
|
||||
expect(user.user_status.emoji).to eq(status[:emoji])
|
||||
expect(user.user_status.description).to eq(status[:description])
|
||||
end
|
||||
|
||||
it "updates user status" do
|
||||
user.set_status!("off to dentist", "tooth")
|
||||
user.reload
|
||||
|
||||
new_status = {
|
||||
emoji: "surfing_man",
|
||||
description: "surfing",
|
||||
}
|
||||
put "/u/#{user.username}.json", params: {
|
||||
status: new_status
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
user.reload
|
||||
expect(user.user_status).not_to be_nil
|
||||
expect(user.user_status.emoji).to eq(new_status[:emoji])
|
||||
expect(user.user_status.description).to eq(new_status[:description])
|
||||
end
|
||||
|
||||
it "clears user status" do
|
||||
user.set_status!("off to dentist", "tooth")
|
||||
user.reload
|
||||
|
||||
put "/u/#{user.username}.json", params: {
|
||||
status: nil
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
user.reload
|
||||
expect(user.user_status).to be_nil
|
||||
end
|
||||
|
||||
it "can't set status of another user" do
|
||||
put "/u/#{user1.username}.json", params: {
|
||||
status: {
|
||||
emoji: "tooth",
|
||||
description: "off to dentist",
|
||||
}
|
||||
}
|
||||
expect(response.status).to eq(403)
|
||||
|
||||
user1.reload
|
||||
expect(user1.user_status).to be_nil
|
||||
end
|
||||
|
||||
it "can't update status of another user" do
|
||||
old_status = {
|
||||
emoji: "tooth",
|
||||
description: "off to dentist",
|
||||
}
|
||||
user1.set_status!(old_status[:description], old_status[:emoji])
|
||||
user1.reload
|
||||
|
||||
new_status = {
|
||||
emoji: "surfing_man",
|
||||
description: "surfing",
|
||||
}
|
||||
put "/u/#{user1.username}.json", params: {
|
||||
status: new_status
|
||||
}
|
||||
expect(response.status).to eq(403)
|
||||
|
||||
user1.reload
|
||||
expect(user1.user_status).not_to be_nil
|
||||
expect(user1.user_status.emoji).to eq(old_status[:emoji])
|
||||
expect(user1.user_status.description).to eq(old_status[:description])
|
||||
end
|
||||
|
||||
it "can't clear status of another user" do
|
||||
user1.set_status!("off to dentist", "tooth")
|
||||
user1.reload
|
||||
|
||||
put "/u/#{user1.username}.json", params: {
|
||||
status: nil
|
||||
}
|
||||
expect(response.status).to eq(403)
|
||||
|
||||
user1.reload
|
||||
expect(user1.user_status).not_to be_nil
|
||||
end
|
||||
|
||||
context 'when user status is disabled' do
|
||||
before do
|
||||
SiteSetting.enable_user_status = false
|
||||
end
|
||||
|
||||
it "doesn't set user status" do
|
||||
put "/u/#{user.username}.json", params: {
|
||||
status: {
|
||||
emoji: "tooth",
|
||||
description: "off to dentist",
|
||||
}
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
user.reload
|
||||
expect(user.user_status).to be_nil
|
||||
end
|
||||
|
||||
it "doesn't update user status" do
|
||||
old_status = {
|
||||
emoji: "tooth",
|
||||
description: "off to dentist",
|
||||
}
|
||||
user.set_status!(old_status[:description], old_status[:emoji])
|
||||
user.reload
|
||||
|
||||
new_status = {
|
||||
emoji: "surfing_man",
|
||||
description: "surfing",
|
||||
}
|
||||
put "/u/#{user.username}.json", params: {
|
||||
status: new_status
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
user.reload
|
||||
expect(user.user_status).not_to be_nil
|
||||
expect(user.user_status.emoji).to eq(old_status[:emoji])
|
||||
expect(user.user_status.description).to eq(old_status[:description])
|
||||
end
|
||||
|
||||
it "doesn't clear user status" do
|
||||
user.set_status!("off to dentist", "tooth")
|
||||
user.reload
|
||||
|
||||
put "/u/#{user.username}.json", params: {
|
||||
status: nil
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
user.reload
|
||||
expect(user.user_status).not_to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'as a staff user' do
|
||||
before do
|
||||
SiteSetting.enable_user_status = true
|
||||
sign_in(moderator)
|
||||
end
|
||||
|
||||
it "sets another user's status" do
|
||||
status = {
|
||||
emoji: "tooth",
|
||||
description: "off to dentist",
|
||||
}
|
||||
|
||||
put "/u/#{user.username}.json", params: {
|
||||
status: status
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
user.reload
|
||||
expect(user.user_status).not_to be_nil
|
||||
expect(user.user_status.emoji).to eq(status[:emoji])
|
||||
expect(user.user_status.description).to eq(status[:description])
|
||||
end
|
||||
|
||||
it "updates another user's status" do
|
||||
user.set_status!("off to dentist", "tooth")
|
||||
user.reload
|
||||
|
||||
new_status = {
|
||||
emoji: "surfing_man",
|
||||
description: "surfing",
|
||||
}
|
||||
put "/u/#{user.username}.json", params: {
|
||||
status: new_status
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
user.reload
|
||||
expect(user.user_status).not_to be_nil
|
||||
expect(user.user_status.emoji).to eq(new_status[:emoji])
|
||||
expect(user.user_status.description).to eq(new_status[:description])
|
||||
end
|
||||
|
||||
it "clears another user's status" do
|
||||
user.set_status!("off to dentist", "tooth")
|
||||
user.reload
|
||||
|
||||
put "/u/#{user.username}.json", params: {
|
||||
status: nil
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
user.reload
|
||||
expect(user.user_status).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#badge_title' do
|
||||
|
|
Loading…
Reference in New Issue