diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index df317fedf..f812f12bf 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -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?) diff --git a/app/models/user.rb b/app/models/user.rb index eca0764f5..58b64061f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -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| diff --git a/app/views/admin/users/show/profile/_actions.html.erb b/app/views/admin/users/show/profile/_actions.html.erb index 04839cac6..5c7e21e1e 100644 --- a/app/views/admin/users/show/profile/_actions.html.erb +++ b/app/views/admin/users/show/profile/_actions.html.erb @@ -29,6 +29,7 @@ <% if @user.identities.any? %>
  • <% end %> +
  • -
  • +
  • @@ -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" %> diff --git a/app/views/admin/users/show/profile/actions/_change_reputation.html.erb b/app/views/admin/users/show/profile/actions/_change_reputation.html.erb new file mode 100644 index 000000000..d6d1be3df --- /dev/null +++ b/app/views/admin/users/show/profile/actions/_change_reputation.html.erb @@ -0,0 +1,17 @@ +
    + <%= form_for(@user, url: reputation_modifier_admin_user_path(@user), + html: { class: "flex flex-col gap-4", method: :patch }) do |f| %> +

    <%= t("views.admin.users.reputation.desc_html", reputation_modifier: @user.reputation_modifier) %>

    +
    + <%= 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" } %> +
    +
    + <%= 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" %> +
    +
    + <%= f.button t("views.admin.users.reputation.submit"), class: "c-btn c-btn--primary", type: "submit" %> +
    + <% end %> +
    diff --git a/config/locales/views/admin/en.yml b/config/locales/views/admin/en.yml index be414eb60..6872ed877 100644 --- a/config/locales/views/admin/en.yml +++ b/config/locales/views/admin/en.yml @@ -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 %{reputation_modifier}. + 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:" diff --git a/config/locales/views/admin/fr.yml b/config/locales/views/admin/fr.yml index 83858e84f..fd4aace31 100644 --- a/config/locales/views/admin/fr.yml +++ b/config/locales/views/admin/fr.yml @@ -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 %{reputation_modifier}. + 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:" diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 38e446e4f..c6043a5db 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -48,6 +48,7 @@ namespace :admin do member do post "banish" + patch "reputation_modifier" post "export_data" post "full_delete" patch "user_status" diff --git a/spec/requests/admin/users/users_change_reputation_modifier_spec.rb b/spec/requests/admin/users/users_change_reputation_modifier_spec.rb new file mode 100644 index 000000000..4382114d6 --- /dev/null +++ b/spec/requests/admin/users/users_change_reputation_modifier_spec.rb @@ -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