Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ class UsersController < ApplicationController

before_action :redirect_to_sign_in, only: [:filters], unless: [:user_signed_in?, :json_request?]

before_action :verify_moderator, only: [:mod, :destroy, :soft_delete, :role_toggle, :full_log,
before_action :verify_moderator, only: [:mod, :destroy, :soft_delete, :undelete, :role_toggle, :full_log,
:annotate, :annotations, :mod_privileges, :mod_privilege_action]
before_action :set_user, only: [:show, :mod, :destroy, :soft_delete, :posts, :role_toggle, :full_log, :activity,
before_action :set_user, only: [:show, :mod, :destroy, :soft_delete, :undelete, :posts,
:role_toggle, :full_log, :activity,
:annotate, :annotations, :mod_privileges, :mod_privilege_action,
:vote_summary, :network, :avatar]
before_action :check_deleted, only: [:show, :posts, :activity]
before_action :verify_user_not_deleted, only: [:undelete]

def index
@sort_param = { reputation: :reputation, age: :created_at }[params[:sort]&.to_sym] || :reputation
Expand Down Expand Up @@ -355,6 +357,18 @@ def soft_delete
render json: { status: 'success', user: @user.id }
end

def undelete
status = @user.community_user&.undelete(current_user)

if status
render json: { status: 'success', user: @user.id }
else
render json: { status: 'failed',
message: 'Failed to undelete user profile',
user: @user.id }
end
end

def edit_profile
render layout: 'without_sidebar'
end
Expand Down Expand Up @@ -699,5 +713,11 @@ def check_deleted
not_found!
end
end

# Explicitly checks that the requested used is not deleted
# NOTE: This guard is not enough to guarantee that the user is not deleted on the current community
def verify_user_not_deleted
not_found! if @user.deleted?
end
end
# rubocop:enable Metrics/ClassLength
14 changes: 14 additions & 0 deletions app/models/community_user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,18 @@ def soft_delete(attribute_to)

update(deleted: true, deleted_by: attribute_to, deleted_at: DateTime.now)
end

# Undeletes the community user if it's soft-deleted
# @param attribute_to [User] user to attribute the action to
# @return [Boolean] whether the community user has been undeleted
def undelete(attribute_to)
return true unless deleted?

AuditLog.moderator_audit(event_type: 'profile_undelete',
related: self,
user: attribute_to,
comment: attributes_print(join: "\n"))

update(deleted: false, deleted_by: nil, deleted_at: nil)
end
end
2 changes: 1 addition & 1 deletion app/views/abilities/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<%= @user.username %> has not yet earned this ability.
<% end %>

<% if @user.community_user.privilege? 'mod' %>
<% if @user.at_least_moderator? %>
<% if @user.id == current_user&.id %>
However, as a moderator you still have all abilities.
<% else %>
Expand Down
23 changes: 18 additions & 5 deletions app/views/users/mod.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,24 @@
<p><strong>Take care!</strong> Actions in this section may not be reversible, and you will not be asked to confirm
after initiating an action.</p>
<div class="delete-actions">
<%= link_to 'Delete community profile', soft_delete_user_path(@user.id, type: 'profile'), remote: true,
method: :delete, class: 'js-soft-delete button is-danger is-filled', role: 'button' %>
<% if current_user.is_global_moderator || current_user.is_global_admin %>
<%= link_to 'Delete user network-wide', soft_delete_user_path(@user.id, type: 'user'), remote: true,
method: :delete, class: 'js-soft-delete button is-danger is-filled', role: 'button' %>
<% is_deleted = @user.community_user&.deleted? || false %>
<%= link_to "#{is_deleted ? 'Undelete' : 'Delete'} community profile",
is_deleted ? undelete_user_path(@user.id) : soft_delete_user_path(@user.id, type: 'profile'),
remote: true,
method: is_deleted ? :post : :delete,
class: 'js-soft-delete button is-danger is-filled',
disabled: @user.deleted?,
title: @user.deleted? ? 'The user is deleted network-wide' : '',
role: 'button' %>
<% if current_user.at_least_global_moderator? %>
<%= link_to 'Delete user network-wide',
soft_delete_user_path(@user.id, type: 'user'),
remote: true,
method: :delete,
class: 'js-soft-delete button is-danger is-filled',
disabled: @user.deleted?,
title: @user.deleted? ? 'The user is already deleted' : '',
role: 'button' %>
<% end %>
<% if current_user.is_global_admin %>
<%= link_to 'Feed to STAT (180 days)', hellban_user_path(@user), method: :post, class: 'button is-danger is-filled', role: 'button' %>
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
post 'settings/:name', to: 'site_settings#update', as: :update_site_setting

delete 'users/delete/:id', to: 'users#soft_delete', as: :soft_delete_user
post 'users/undelete/:id', to: 'users#undelete', as: :undelete_user

get 'privileges', to: 'admin#privileges', as: :admin_privileges
get 'privileges/:name', to: 'admin#show_privilege', as: :admin_privilege
Expand Down
43 changes: 43 additions & 0 deletions test/controllers/users_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,43 @@ class UsersControllerTest < ActionController::TestCase
end
end

test 'should require authentication to undelete user profiles' do
del_usr = users(:deleted_profile)

try_undelete_user(del_usr)

assert_json_failure(:not_found)
end

test 'moderators and higher should be able to undelete user profiles' do
del_usr = users(:deleted_profile)

users.select(&:at_least_moderator?).each do |user|
sign_in(user)

try_undelete_user(del_usr)
@user = assigns(:user)

assert_json_success
assert_not_nil @user
assert_not @user.community_user.deleted?
end
end

test 'users that are deleted network-wide should not be undeletable' do
del_usr = users(:deleted_account)

users.select(&:at_least_moderator?).each do |user|
sign_in(user)

try_undelete_user(del_usr)
del_usr.reload

assert_json_failure(:not_found)
assert del_usr.community_user.deleted?
end
end

test 'should soft-delete user' do
sign_in users(:global_admin)

Expand Down Expand Up @@ -637,6 +674,12 @@ def try_soft_delete_user(type, user)
format: :json }
end

# @param user [User] user to undelete
def try_undelete_user(user)
post :undelete, params: { id: user.id,
format: :json }
end

def try_save_preference(name, value, community: nil)
post :set_preference, params: {
community: community,
Expand Down
3 changes: 3 additions & 0 deletions test/fixtures/community_users.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ sample_deleted_account:
community: sample
is_admin: false
is_moderator: false
deleted: true
deleted_at: 2020-01-02T00:00:00.000000Z
deleted_by: global_admin
reputation: 1

sample_deleted_profile:
Expand Down
Loading