Allow admins to change reputation modifier (#20766)

* Allow admins to change reputation modifier

* locale + rubocop
This commit is contained in:
Philip How 2024-03-15 14:46:19 +00:00 committed by GitHub
parent 05d33720db
commit 57c0078bd6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 139 additions and 1 deletions

View file

@ -9,6 +9,7 @@ module Admin
add_org_credits remove_org_credits
organization_id identity_id
credit_action credit_amount
reputation_modifier
].freeze
EMAIL_ALLOWED_PARAMS = %i[
@ -78,6 +79,30 @@ module Admin
redirect_to admin_user_path(params[:id])
end
def reputation_modifier
@user = User.find(params[:id])
reputation_modifier_value = user_params[:reputation_modifier]
note_content = if user_params[:new_note].present?
"Changed user's reputation modifier to #{reputation_modifier_value}. " \
"Reason: #{user_params[:new_note]}"
else
"Changed user's reputation modifier to #{reputation_modifier_value}."
end
if @user.update(reputation_modifier: reputation_modifier_value)
Note.create(
author_id: current_user.id,
noteable_id: @user.id,
noteable_type: "User",
reason: "reputation_modifier_change",
content: note_content,
)
flash[:success] = I18n.t("views.admin.users.reputation.success", reputation_modifier: reputation_modifier_value)
else
flash[:error] = I18n.t("views.admin.users.reputation.error")
end
redirect_to admin_user_path(@user)
end
def destroy
role = Role.find(params[:role_id])
authorize(role, :remove_role?)

View file

@ -145,6 +145,8 @@ class User < ApplicationRecord
validates :spent_credits_count, presence: true
validates :subscribed_to_user_subscriptions_count, presence: true
validates :unspent_credits_count, presence: true
validates :reputation_modifier, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 5 },
presence: true
# add validators for provider related usernames
Authentication::Providers.username_fields.each do |username_field|

View file

@ -29,6 +29,7 @@
<% if @user.identities.any? %>
<li><button type="button" class="c-btn w-100 align-left" data-modal-title="<%= t("views.admin.users.social.heading") %>" data-modal-size="medium" data-modal-content-selector="#remove-social-accounts"><%= t("views.admin.users.profile.options.remove_social") %></button></li>
<% end %>
<li><button type="button" class="c-btn w-100 align-left" data-modal-title="<%= t("views.admin.users.reputation.heading") %>" data-modal-size="medium" data-modal-content-selector="#change-reputation"><%= t("views.admin.users.reputation.change") %></button></li>
<li>
<button type="button"
class="c-btn c-btn--destructive w-100 align-left"
@ -38,7 +39,7 @@
data-form-action="<%= banish_admin_user_path(@user) %>"
data-user-name="<%= @user.name %>"
data-banishable-user="<%= @banishable_user %>"><%= t("views.admin.users.profile.options.banish") %></button>
</li>
</li>
<li><button type="button" class="c-btn c-btn--destructive w-100 align-left" data-modal-title="<%= t("views.admin.users.delete.heading", user: @user.name) %>" data-modal-size="small" data-modal-content-selector="#delete-user"><%= t("views.admin.users.profile.options.delete") %></button></li>
</ul>
</div>
@ -51,6 +52,7 @@
<%= render "admin/users/show/profile/actions/merge" %>
<%= render "admin/users/modals/unpublish_modal" %>
<%= render "admin/users/show/profile/actions/social_accounts" %>
<%= render "admin/users/show/profile/actions/change_reputation" %>
<%= render "admin/users/modals/banish_modal" %>
<%= render "admin/users/show/profile/actions/delete" %>
</div>

View file

@ -0,0 +1,17 @@
<div id="change-reputation">
<%= form_for(@user, url: reputation_modifier_admin_user_path(@user),
html: { class: "flex flex-col gap-4", method: :patch }) do |f| %>
<p><%= t("views.admin.users.reputation.desc_html", reputation_modifier: @user.reputation_modifier) %></p>
<div class="crayons-field mb-4">
<%= f.label :reputation_modifier, t("views.admin.users.reputation.reputation_modifier"), class: "crayons-field__label" %>
<%= f.text_field :reputation_modifier, placeholder: @user.reputation_modifier, class: "crayons-textfield", type: "number", inputmode: "numeric", step: "0.01", min: "0", max: 5, aria: { describedby: "reputation_modifier" } %>
</div>
<div class="crayons-field mb-4">
<%= f.label :new_note, t("views.admin.users.reputation.change_note"), class: "crayons-field__label" %>
<%= f.text_area :new_note, size: 50, required: true, class: "crayons-textfield", id: "new_note" %>
</div>
<div>
<%= f.button t("views.admin.users.reputation.submit"), class: "c-btn c-btn--primary", type: "submit" %>
</div>
<% end %>
</div>

View file

@ -365,6 +365,15 @@ en:
Open: Open
Resolved: Resolved
Invalid: Invalid
reputation:
change: Change reputation modifier
change_note: Reason for change
desc_html: This changes the points awarded to reactions given by the user, increasing or decreasing their impact on article and comment score. The user's current's reputation modifier is <strong>%{reputation_modifier}</strong>.
heading: Change reputation modifier
reputation_modifier: New reputation modifier
submit: Change reputation modifier
success: Successfully changed user's reputation modifier to %{reputation_modifier}.
error: Failed to change user's reptuation modifier (select between 0 and 5)
unpublish_logs:
subtitle_html: Unpublished by %{user} on %{time}
posts: "Posts:"

View file

@ -363,6 +363,15 @@ fr:
Open: Open
Resolved: Resolved
Invalid: Invalid
reputation:
change: Changer le modificateur de réputation
change_note: Raison du changement
desc_html: Cela modifie les points attribués aux réactions données par l'utilisateur, augmentant ou diminuant leur impact sur le score de l'article et du commentaire. Le modificateur de réputation actuel de l'utilisateur est <strong>%{reputation_modifier}</strong>.
heading: Changer le modificateur de réputation
reputation_modifier: Nouveau modificateur de réputation
submit: Changer le modificateur de réputation
success: Modification réussie du modificateur de réputation de l'utilisateur en %{reputation_modifier}.
error: Échec de la modification du modificateur de réputation de l'utilisateur (sélectionnez entre 0 et 5).
unpublish_logs:
subtitle_html: Unpublished by %{user} on %{time}
posts: "Posts:"

View file

@ -48,6 +48,7 @@ namespace :admin do
member do
post "banish"
patch "reputation_modifier"
post "export_data"
post "full_delete"
patch "user_status"

View file

@ -0,0 +1,73 @@
require "rails_helper"
RSpec.describe "/admin/member_manager/users" do
let!(:user) { create(:user) }
let!(:admin) { create(:user, :super_admin) }
before do
sign_in(admin)
end
describe "PATCH /admin/member_manager/users/:id/reputation_modifier" do
let(:new_reputation_modifier) { 1.5 }
let(:note_content) { "Improvement in community engagement" }
it "updates the user's reputation modifier" do
patch reputation_modifier_admin_user_path(user.id), params: {
user: {
reputation_modifier: new_reputation_modifier,
new_note: note_content
}
}
user.reload
expect(user.reputation_modifier).to eq(new_reputation_modifier)
expect(flash[:success]).to be_present
end
it "creates a note with the reason for the change" do
expect do
patch reputation_modifier_admin_user_path(user.id), params: {
user: {
reputation_modifier: new_reputation_modifier,
new_note: note_content
}
}
end.to change(Note, :count).by(1)
note = Note.last
expect(note.content).to include("Changed user's reputation modifier to #{new_reputation_modifier}")
expect(note.content).to include("Reason: #{note_content}")
expect(note.reason).to eq("reputation_modifier_change")
expect(note.author_id).to eq(admin.id)
expect(note.noteable_id).to eq(user.id)
end
it "redirects to the user admin page" do
patch reputation_modifier_admin_user_path(user.id), params: {
user: {
reputation_modifier: new_reputation_modifier,
new_note: ""
}
}
expect(response).to redirect_to(admin_user_path(user))
end
context "when the update fails" do
let(:invalid_reputation_modifier) { "6" }
it "sets a flash error message" do
patch reputation_modifier_admin_user_path(user.id), params: {
user: {
reputation_modifier: invalid_reputation_modifier,
new_note: note_content
}
}
expect(flash[:error]).to be_present
expect(response).to redirect_to(admin_user_path(user))
end
end
end
end