Skip to content

Commit

Permalink
implement admin action for reparenting a user lobsters#1370
Browse files Browse the repository at this point in the history
  • Loading branch information
pushcx committed Dec 5, 2024
1 parent f5004d7 commit a99bd37
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 3 deletions.
3 changes: 3 additions & 0 deletions app/controllers/mod/moderator_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class Mod::ModeratorController < ApplicationController
before_action :require_logged_in_moderator
end
35 changes: 35 additions & 0 deletions app/controllers/mod/reparents_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
class Mod::ReparentsController < Mod::ModeratorController
before_action :require_logged_in_admin
before_action :load_user

def new
end

def create
if params[:reason].blank?
return redirect_to new_mod_reparent_path({}, id: @reparent_user), flash: {error: "Reason can't be blank."}
end

User.transaction do
ModNote.record_reparent!(@reparent_user, @user, params[:reason])
@reparent_user.invited_by_user = @user
@reparent_user.save!
Moderation.create!({
moderator: @user,
user: @reparent_user,
action: "Reparented user to be invited by #{@user.username}",
reason: params[:reason]
})
end
Rails.cache.delete("users_tree_#{User.last.id}") # UsersController#tree

redirect_to user_path(@reparent_user), flash: {success: "User been has reparented to you."}
end

private

def load_user
params.require(:id)
@reparent_user = User.find_by! username: params[:id]
end
end
3 changes: 2 additions & 1 deletion app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ def tree
@title = "Moderators and Administrators"
render action: "list"
else
content = Rails.cache.fetch("users_tree_#{newest_user}", expires_in: (60 * 60 * 24)) {
# Mod::ReparentsController#create knows this key
content = Rails.cache.fetch("users_tree_#{newest_user}", expires_in: 12.hours) {
users = User.select(*attrs).order("id DESC").to_a
@user_count = users.length
@users_by_parent = users.group_by(&:invited_by_user_id)
Expand Down
22 changes: 22 additions & 0 deletions app/models/mod_note.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,28 @@ def self.create_from_message(message, moderator)
)
end

def self.record_reparent!(reparent_user, mod, reason)
old_inviter_url = Rails.application.routes.url_helpers.user_url(
reparent_user.invited_by_user,
host: Rails.application.domain
)
create_without_dupe!(
moderator: mod,
user: reparent_user,
note: "Reparented from [#{reparent_user.invited_by_user.username}](#{old_inviter_url}) to #{mod.username} with reason: #{reason}"
)

reparent_user_url = Rails.application.routes.url_helpers.user_url(
reparent_user,
host: Rails.application.domain
)
create_without_dupe!(
moderator: mod,
user: reparent_user.invited_by_user,
note: "Admin reparented their invitee [#{reparent_user.username}](#{reparent_user_url}) to #{mod.username} with reason: #{reason}"
)
end

def self.tattle_on_banned_login(user)
# rubocop:disable Rails/SaveBang
create(
Expand Down
20 changes: 20 additions & 0 deletions app/views/mod/reparents/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<h1>Reparent <%= @reparent_user.username %></h1>

<%= form_with url: mod_reparents_path({}, id: @reparent_user), method: :post do |f| %>
<p>
This will reparent <%= @reparent_user.username %> to you in the invite tree, create a mod note on the user with their old inviter, and put an entry in the moderation log with the given reason.
</p>

<p>
This should be used rarely because it makes it harder to investigate sockpuppets and voting rings.
Currently the only known purpose is if this user was abused off-site by their inviter and we can't really do anything about it here except try to disassociate them.
Write a good reason here, it's going to get attention by virtue of being rare.
</p>

<div class="boxline">
<%= f.label :reason, "Reason:", :class => "required" %>
<%= f.text_field :reason, :size => 80 %>
</div>
<%= f.submit "Reparent" %>
<% end %>

6 changes: 4 additions & 2 deletions app/views/users/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@
<% if @showing_user.is_banned? %>
<div class="boxline">
<%= f.label :reason, "Reason:", :class => "required" %>
<%= f.text_field :reason, :size => 40 %>
<%= f.text_field :reason, :size => 80 %>
</div>
<%= f.submit "Unban" %>
<% end %>
Expand All @@ -272,7 +272,7 @@
<%= form_with url: user_ban_path, method: :post do |f| %>
<div class="boxline">
<%= f.label :reason, "Reason:", :class => "required" %>
<%= f.text_field :reason, :size => 40 %>
<%= f.text_field :reason, :size => 80 %>
</div>
<p>
<% if !@showing_user.is_banned? %>
Expand All @@ -284,6 +284,8 @@
</p>
<% end %>
<% end %>

<%= link_to 'Reparent', new_mod_reparent_path({}, id: @showing_user) %>
<% end %>
<% end %>
</div>
4 changes: 4 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,10 @@
get "/mod/notes(/:period)" => "mod_notes#index", :as => "mod_notes"
post "/mod/notes" => "mod_notes#create"

namespace :mod do
resources :reparents, only: [:new, :create]
end

get "/privacy" => "about#privacy"
get "/about" => "about#about"
get "/chat" => "about#chat"
Expand Down
27 changes: 27 additions & 0 deletions spec/requests/mod/reparents_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# typed: false

require "rails_helper"

describe "Mod::ReparentsController", type: :request do
context "/new form" do
it "loads" do
sign_in create(:user, :admin)
reparent_user = create(:user)
get "/mod/reparents/new", params: {id: reparent_user.username}
expect(response).to be_successful
end
end

context "reparenting" do
it "reparents the user and logs it" do
sign_in create(:user, :admin)
inviter = create(:user)
reparent_user = create(:user, invited_by_user: inviter)
post "/mod/reparents", params: {id: reparent_user.username, reason: "Abuse"}
expect(response).to redirect_to user_path(reparent_user)
reparent_user.reload
expect(reparent_user.invited_by_user).to_not be(inviter)
expect(Moderation.last.reason).to include("Abuse")
end
end
end

0 comments on commit a99bd37

Please sign in to comment.