comments: allow admins to remove comment votes (fix #4640)

Allow admins to remove comment votes by other users. This is done by
clicking the comment score to get to the comment vote list, then
clicking the Remove button on every vote.
This commit is contained in:
evazion
2021-03-29 23:08:49 -05:00
parent 6b91e55283
commit b3c1c753b3
10 changed files with 78 additions and 15 deletions

View File

@@ -28,12 +28,16 @@ class CommentComponent < ApplicationComponent
def upvoted?
return false if current_user.is_anonymous?
comment.votes.active.select(&:is_positive?).map(&:user_id).include?(current_user.id)
current_vote&.is_positive?
end
def downvoted?
return false if current_user.is_anonymous?
comment.votes.active.select(&:is_negative?).map(&:user_id).include?(current_user.id)
current_vote&.is_negative?
end
def current_vote
@current_vote ||= comment.votes.active.find { |v| v.user_id == current_user.id }
end
def reported?

View File

@@ -48,7 +48,7 @@
<% if current_user.is_anonymous? %>
<%= link_to upvote_icon, login_path(url: request.fullpath), class: "comment-upvote-link inactive-link" %>
<% elsif upvoted? %>
<%= link_to upvote_icon, comment_comment_votes_path(comment_id: comment.id), class: "comment-upvote-link comment-unvote-link active-link", method: :delete, remote: true %>
<%= link_to upvote_icon, comment_vote_path(current_vote), class: "comment-upvote-link comment-unvote-link active-link", method: :delete, remote: true %>
<% else %>
<%= link_to upvote_icon, comment_comment_votes_path(comment_id: comment.id, score: "1"), class: "comment-upvote-link inactive-link", method: :post, remote: true %>
<% end %>
@@ -64,7 +64,7 @@
<% if current_user.is_anonymous? %>
<%= link_to downvote_icon, login_path(url: request.fullpath), class: "comment-downvote-link inactive-link" %>
<% elsif downvoted? %>
<%= link_to downvote_icon, comment_comment_votes_path(comment_id: comment.id), class: "comment-downvote-link comment-unvote-link active-link", method: :delete, remote: true %>
<%= link_to downvote_icon, comment_vote_path(current_vote), class: "comment-downvote-link comment-unvote-link active-link", method: :delete, remote: true %>
<% else %>
<%= link_to downvote_icon, comment_comment_votes_path(comment_id: comment.id, score: "-1"), class: "comment-downvote-link inactive-link", method: :post, remote: true %>
<% end %>

View File

@@ -26,8 +26,7 @@ class CommentVotesController < ApplicationController
end
def destroy
# XXX should find by comment vote id.
@comment_vote = authorize CommentVote.active.find_by!(comment_id: params[:comment_id], user: CurrentUser.user)
@comment_vote = authorize CommentVote.find(params[:id])
@comment_vote.soft_delete(updater: CurrentUser.user)
respond_with(@comment_vote)

View File

@@ -4,7 +4,7 @@ class CommentVotePolicy < ApplicationPolicy
end
def destroy?
record.user_id == user.id
!record.is_deleted? && (record.user_id == user.id || user.is_admin?)
end
def can_see_votes?

View File

@@ -6,6 +6,7 @@
<%= fc.input :post_id, label: "Post", input_html: { value: params.dig(:search, :comment, :post_id) } %>
<% end %>
<%= f.input :comment_id, label: "Comment", input_html: { value: params[:search][:comment_id] } %>
<%= f.input :is_deleted, label: "Deleted?", as: :select, include_blank: true, selected: params[:search][:is_deleted] %>
<%= f.input :score, collection: [["+1", "1"], ["-1", "-1"]], include_blank: true, selected: params[:search][:score] %>
<%= f.submit "Search" %>
<% end %>

View File

@@ -0,0 +1,2 @@
Danbooru.Utility.notice("Vote removed");
location.reload();

View File

@@ -14,9 +14,23 @@
<%= link_to "»", comment_votes_path(search: { user_name: vote.user.name }) %>
<% end %>
<% t.column "Status" do |vote| %>
<%= "Deleted" if vote.is_deleted? %>
<% end %>
<% t.column "Created" do |vote| %>
<%= time_ago_in_words_tagged(vote.created_at) %>
<% end %>
<% t.column column: "control" do |vote| %>
<% if policy(vote).destroy? %>
<%= render PopupMenuComponent.new do |menu| %>
<%= menu.item do %>
<%= link_to "Remove", comment_vote_path(vote, variant: "listing"), remote: true, method: :delete %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<%= numbered_paginator(@comment_votes) %>

View File

@@ -8,27 +8,40 @@
<% t.column "Post" do |vote| %>
<%= post_preview(vote.comment.post, show_deleted: true) %>
<% end %>
<% t.column "Comment", td: {class: "col-expand"} do |vote| %>
<div class="prose">
<%= format_text(vote.comment.body) %>
</div>
<% end %>
<% t.column "Status" do |vote| %>
<%= "Deleted" if vote.is_deleted? %>
<% end %>
<% t.column "Score" do |vote| %>
<%= link_to sprintf("%+d", vote.score), comment_votes_path(search: { score: vote.score }) %>
<% end %>
<% t.column "Commenter" do |vote| %>
<%= link_to_user vote.comment.creator %>
<%= link_to "»", comment_votes_path(search: { comment: { creator_name: vote.comment.creator.name }}) %>
<div><%= time_ago_in_words_tagged(vote.comment.created_at) %></div>
<% end %>
<% t.column "Voter" do |vote| %>
<%= link_to_user vote.user %>
<%= link_to "»", comment_votes_path(search: { user_name: vote.user.name }) %>
<div><%= time_ago_in_words_tagged(vote.created_at) %></div>
<% end %>
<% t.column column: "control" do |vote| %>
<% if vote.user == CurrentUser.user %>
<%= link_to "unvote", comment_comment_votes_path(vote.comment), remote: true, method: :delete %>
<% if policy(vote).destroy? %>
<%= render PopupMenuComponent.new do |menu| %>
<%= menu.item do %>
<%= link_to "Remove", comment_vote_path(vote, variant: "listing"), remote: true, method: :delete %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>

View File

@@ -81,7 +81,7 @@ Rails.application.routes.draw do
post :approve
end
end
resources :comment_votes, only: [:index]
resources :comment_votes, only: [:index, :destroy]
resources :comments do
resource :votes, controller: "comment_votes", only: [:create, :destroy], as: "comment_votes"
collection do

View File

@@ -141,21 +141,51 @@ class CommentVotesControllerTest < ActionDispatch::IntegrationTest
context "#destroy" do
should "allow users to remove their own comment votes" do
@vote = create(:comment_vote, user: @user)
assert_equal(1, @vote.comment.score)
assert_difference("CommentVote.count", 0) do
delete_auth comment_comment_votes_path(@vote.comment), @user, xhr: true
delete_auth comment_vote_path(@vote), @user, xhr: true
assert_response :success
assert_equal(true, @vote.reload.is_deleted?)
assert_equal(0, @vote.comment.score)
assert_equal(false, ModAction.comment_vote_delete.exists?)
end
end
should "not allow users to remove comment votes by other users" do
should "not allow normal users to remove comment votes by other users" do
@vote = create(:comment_vote)
assert_equal(1, @vote.comment.score)
assert_difference("CommentVote.count", 0) do
delete_auth comment_comment_votes_path(@vote.comment), @user, xhr: true
assert_response 404
delete_auth comment_vote_path(@vote), @user, xhr: true, params: { variant: "listing" }
assert_response 403
assert_equal(false, @vote.reload.is_deleted?)
assert_equal(1, @vote.comment.score)
end
end
should "not allow deleting already deleted votes" do
@vote = create(:comment_vote, is_deleted: true)
delete_auth comment_vote_path(@vote), @user, xhr: true
assert_response 403
end
should "allow admins to remove comment votes by other users" do
@vote = create(:comment_vote)
assert_equal(1, @vote.comment.score)
assert_difference("CommentVote.count", 0) do
delete_auth comment_vote_path(@vote), create(:admin_user), xhr: true, params: { variant: "listing" }
assert_response :success
assert_equal(true, @vote.reload.is_deleted?)
assert_equal(0, @vote.comment.score)
assert_equal("comment_vote_delete", ModAction.last.category)
assert_match(/deleted comment vote/, ModAction.last.description)
end
end
@@ -167,7 +197,7 @@ class CommentVotesControllerTest < ActionDispatch::IntegrationTest
should "delete the current active vote" do
@vote = create(:comment_vote, comment: @comment, user: @user)
delete_auth comment_comment_votes_path(@vote.comment), @user, xhr: true
delete_auth comment_vote_path(@vote), @user, xhr: true
assert_response :success
assert_equal(true, @vote.reload.is_deleted?)