Comment votes and vote fixups and interface

This commit is contained in:
Kira 2019-04-09 13:04:40 -07:00
parent 0503d7bb8c
commit 44ab7b1a8c
9 changed files with 131 additions and 98 deletions

View File

@ -8,7 +8,9 @@ class CommentVotesController < ApplicationController
def create
@comment = Comment.find(params[:comment_id])
@comment_vote = VoteManager.comment_vote!(comment: @comment, user: CurrentUser.user, score: params[:score])
VoteManager.comment_unvote!(comment: @comment, user: CurrentUser.user) if @comment_vote == :need_unvote
if @comment_vote == :need_unvote
VoteManager.comment_unvote!(comment: @comment, user: CurrentUser.user)
end
@comment.reload
rescue CommentVote::Error, ActiveRecord::RecordInvalid => x
@error = x
@ -28,42 +30,18 @@ class CommentVotesController < ApplicationController
end
def lock
ids = params[:id].split(/,/)
ids = params[:ids].split(/,/)
ids.each do |id|
VoteManager.lock_comment!(id)
@vote = CommentVote.find(id)
if @vote.score == nil
@vote.score = 0 # Fix unrecorded score.
end
@comment = Comment.find(@vote.comment_id)
@comment.score -= @vote.score
@comment.save
@vote.score = 0
@vote.save
VoteManager.comment_lock!(id)
end
respond_to_success('Votes locked at 0', action: 'index')
end
def delete
ids = params[:id].split(/,/)
ids = params[:ids].split(/,/)
ids.each do |id|
@vote = CommentVote.find(id)
if @vote.score == nil
@vote.score = 0 # Fix unrecorded score.
end
@comment = Comment.find(@vote.comment_id)
@comment.score -= @vote.score
@comment.save
@vote.destroy
VoteManager.admin_comment_unvote!(id)
end
respond_to_success('Votes deleted', action: 'index')
end
end

View File

@ -137,6 +137,7 @@ module ApplicationHelper
end
def link_to_ip(ip)
return '(none)' unless ip
link_to ip, moderator_ip_addrs_path(:search => {:ip_addr => ip})
end

View File

@ -2,9 +2,7 @@ class VoteManager
def self.vote!(user:, post:, score: "locked")
# TODO retry on ActiveRecord::TransactionisloationConflict?
begin
unless user.is_voter?
raise PostVote::Error.new("You do not have permission to vote")
end
raise PostVote::Error.new("You do not have permission to vote") unless user.is_voter?
PostVote.transaction(isolation: :serializable) do
vote = post.votes.create!(user: user, vote: score)
vote_cols = "score = score + #{vote.score}"
@ -52,15 +50,12 @@ class VoteManager
end
def self.comment_vote!(user:, comment:, score:)
@vote = nil
# TODO retry on ActiveRecord::TransactionisloationConflict?
score = score_modifier = score.to_i
begin
unless [1, -1].include?(score)
raise CommentVote::Error.new("Invalid vote")
end
unless user.is_voter?
raise CommentVote::Error.new("You do not have permission to vote")
end
raise CommentVote::Error.new("Invalid vote") unless [1, -1].include?(score)
raise CommentVote::Error.new("You do not have permission to vote") unless user.is_voter?
CommentVote.transaction(isolation: :serializable) do
old_vote = comment.votes.where(user_id: user.id).first
if old_vote
@ -90,6 +85,21 @@ class VoteManager
end
end
def self.comment_lock!(id)
CommentVote.transaction(isolation: :serializable) do
vote = CommentVote.find_by(id: id)
return unless vote
comment = vote.comment
Comment.where(id: comment.id).update_all("score = score - #{vote.score}")
vote.update_column(:score, 0)
end
end
def self.admin_comment_unvote!(id)
vote = CommentVote.find_by(id: id)
comment_unvote!(comment: vote.comment, user: vote.user, force: true) if vote
end
private
def self.subtract_vote(post, vote)

View File

@ -1,6 +1,6 @@
class CommentVote < ApplicationRecord
class Error < Exception ; end
class ShouldUnvote < Exception ; end
class Error < Exception;
end
belongs_to :comment
belongs_to :user
@ -9,22 +9,7 @@ class CommentVote < ApplicationRecord
validates_uniqueness_of :user_id, :scope => :comment_id, :message => "have already voted for this comment"
validate :validate_user_can_vote
validate :validate_comment_can_be_down_voted
validates_inclusion_of :score, :in => [-1, 1], :message => "must be 1 or -1"
def self.prune!
where("created_at < ?", 14.days.ago).delete_all
end
def self.search(params)
q = where("true")
return q if params.blank?
if params[:comment_id]
q = q.where("comment_id = ?", params[:comment_id].to_i)
end
q
end
validates_inclusion_of :score, :in => [-1, 0, 1], :message => "must be 1 or -1"
def validate_user_can_vote
if !user.can_comment_vote?
@ -52,7 +37,33 @@ class CommentVote < ApplicationRecord
score == -1
end
def initialize_user
self.user_id = CurrentUser.user.id
def is_locked?
score == 0
end
def initialize_user
self.user_id ||= CurrentUser.user.id
self.user_ip_addr ||= CurrentUser.ip_addr
end
module SearchMethods
def self.search(params)
q = super
if params[:comment_id].present?
q = q.where("comment_id = ?", params[:comment_id].to_i)
end
if params[:user_name].present?
user_id = User.name_to_id(params[:user_name])
q = q.where('user_id = ?', user_id) if user_id
end
q.order(id: :desc)
q
end
end
extend SearchMethods
end

View File

@ -23,6 +23,7 @@ class PostVote < ApplicationRecord
def initialize_attributes
self.user_id ||= CurrentUser.user.id
self.user_ip_addr ||= CurrentUser.ip_addr
if vote == "up"
self.score = 1

View File

@ -17,7 +17,8 @@
</table>
</div>
<table class='striped' style='width:70%;' id='votes'>
<table class="striped" id='votes'>
<thead>
<tr>
<th style='width:8%;'>ID</th>
<th style='width:8%;'>Comment</th>
@ -26,62 +27,73 @@
<th style='width:10%;'>Vote</th>
<th style='width:16%;'>Created</th>
<th style='width:17%;'>Updated</th>
<!-- <th>IP</th>-->
<th>IP</th>
</tr>
</thead>
<tbody>
<% @comment_votes.each do |vote| %>
<tr id="r<%= vote.id %>" >
<tr id="r<%= vote.id %>">
<td><%= vote.id %></td>
<td><%= link_to vote.comment_id, comment_path(vote.comment) %></td>
<td><%= mod_link_to_user vote.comment.creator, :negative %></td>
<td><%= mod_link_to_user vote.user, :negative %></td>
<td><% if vote.score == 1 %><span class='greentext'>Up</span><% elsif vote.score == 0 %><span class='yellowtext'>Locked</span><% elsif vote.score == nil %>Unrecorded<% else %><span class='redtext'>Down</span><% end %></td>
<td title="Created at <%= vote.created_at.strftime("%c") %>"><%= time_ago_in_words(vote.created_at) %> ago</td>
<td title="Updated at <%= vote.updated_at.strftime("%c") %>"><%= time_ago_in_words(vote.updated_at) %> ago</td>
<!-- <td><%#= link_to_ip vote.ip_addr %></td>-->
<td>
<% if vote.score == 1 %><span class='greentext'>Up</span>
<% elsif vote.score == 0 %><span class='yellowtext'>Locked</span>
<% elsif vote.score == nil %>Unrecorded
<% else %><span class='redtext'>Down</span>
<% end %></td>
<td title="Created at <%= vote.created_at.strftime("%c") %>"><%= time_ago_in_words(vote.created_at) %>ago
</td>
<td title="Updated at <%= vote.updated_at.strftime("%c") %>"><%= time_ago_in_words(vote.updated_at) %>ago
</td>
<td><%= link_to_ip vote.user_ip_addr %></td>
</tr>
<% end %>
</tbody>
</table>
<br />
<%= button_to_function "Lock Votes", "CommentVotes.lock()", id: "undo" %> Set the votes to 0, preventing the user from voting on the image again<br />
<br/>
<%= button_to_function "Lock Votes", "CommentVotes.lock()", id: "undo" %> Set the votes to 0, preventing the user
from voting on the image again<br/>
<%= button_to_function "Delete Votes", "CommentVotes.delete()", id: "undo" %> Remove the votes
<script type="text/javascript">
$(function() {
$("#votes").on('click', 'tbody tr', function(evt) {
$(function () {
$("#votes").on('click', 'tbody tr', function (evt) {
$(this).toggleClass('selected');
});
});
function selectedVotes() {
return $("#votes>tbody>tr.selected").map(function() {
return $("#votes>tbody>tr.selected").map(function () {
return $(this).attr('id').substr(1);
}).get();
}
CommentVotes = {};
CommentVotes.lock = function() {
CommentVotes.lock = function () {
const votes = selectedVotes();
if(!votes.length) return;
if (!votes.length) return;
$.ajax({
url: '/comment_votes/lock.json',
method: "post",
data: {
ids: votes.join(',')
}
}).done(function() {
}).done(function () {
$(window).trigger('danbooru:notice', 'Comment votes locked.');
});
};
CommentVotes.delete = function() {
CommentVotes.delete = function () {
const votes = selectedVotes();
if(!votes.length) return;
if (!votes.length) return;
$.ajax({
url: '/comment_votes/delete.json',
method: "post",
data: {
ids: votes.join(',')
}
}).done(function() {
}).done(function () {
$(window).trigger('danbooru:notice', 'Comment votes deleted.');
});
};

View File

@ -17,7 +17,8 @@
</table>
</div>
<table class='striped' style='width:70%;' id='votes'>
<table class='striped' id='votes'>
<thead>
<tr>
<th style='width:8%;'>ID</th>
<th style='width:8%;'>Post</th>
@ -25,61 +26,71 @@
<th style='width:10%;'>Vote</th>
<th style='width:16%;'>Created</th>
<th style='width:17%;'>Updated</th>
<!-- <th>IP</th>-->
<th>IP</th>
</tr>
</thead>
<tbody>
<% @post_votes.each do |vote| %>
<tr id="r<%= vote.id %>" >
<tr id="r<%= vote.id %>">
<td><%= vote.id %></td>
<td><%= link_to vote.post_id, post_path(id: vote.post_id) %></td>
<td><%= mod_link_to_user vote.user, :negative %></td>
<td><% if vote.score == 1 %><span class='greentext'>Up</span><% elsif vote.score == 0 %><span class='yellowtext'>Locked</span><% else %><span class='redtext'>Down</span><% end %></td>
<td title="Created at <%= vote.created_at.strftime("%c") %>"><%= time_ago_in_words(vote.created_at) %> ago</td>
<td title="Updated at <%= vote.updated_at.strftime("%c") %>"><%= time_ago_in_words(vote.updated_at) %> ago</td>
<!-- <td><%#= link_to_ip vote.ip_addr %></td>-->
<td>
<% if vote.score == 1 %><span class='greentext'>Up</span>
<% elsif vote.score == 0 %><span class='yellowtext'>Locked</span>
<% else %><span class='redtext'>Down</span>
<% end %></td>
<td title="Created at <%= vote.created_at.strftime("%c") %>"><%= time_ago_in_words(vote.created_at) %>ago
</td>
<td title="Updated at <%= vote.updated_at.strftime("%c") %>"><%= time_ago_in_words(vote.updated_at) %>ago
</td>
<td><%= link_to_ip vote.user_ip_addr %></td>
</tr>
</tbody>
<% end %>
</table>
<br />
<%= button_to_function "Lock Votes", "PostVotes.lock()", id: "undo" %> Set the votes to 0, preventing the user from voting on the image again<br />
</table>
<br/>
<%= button_to_function "Lock Votes", "PostVotes.lock()", id: "undo" %> Set the votes to 0, preventing the user from
voting on the image again<br/>
<%= button_to_function "Delete Votes", "PostVotes.delete()", id: "undo" %> Remove the votes
<script type="text/javascript">
$(function() {
$("#votes").on('click', 'tbody tr', function(evt) {
$(function () {
$("#votes").on('click', 'tbody tr', function (evt) {
$(this).toggleClass('selected');
});
});
function selectedVotes() {
return $("#votes>tbody>tr.selected").map(function() {
return $("#votes>tbody>tr.selected").map(function () {
return $(this).attr('id').substr(1);
}).get();
}
PostVotes = {};
PostVotes.lock = function() {
PostVotes.lock = function () {
const votes = selectedVotes();
if(!votes.length) return;
if (!votes.length) return;
$.ajax({
url: '/post_votes/lock.json',
method: "post",
data: {
ids: votes.join(',')
}
}).done(function() {
}).done(function () {
$(window).trigger('danbooru:notice', 'Post votes locked.');
});
};
PostVotes.delete = function() {
PostVotes.delete = function () {
const votes = selectedVotes();
if(!votes.length) return;
if (!votes.length) return;
$.ajax({
url: '/post_votes/delete.json',
method: "post",
data: {
ids: votes.join(',')
}
}).done(function() {
}).done(function () {
$(window).trigger('danbooru:notice', 'Post votes deleted.');
});
};

View File

@ -0,0 +1,6 @@
class AddIpToVotes < ActiveRecord::Migration[5.2]
def change
add_column :comment_votes, :user_ip_addr, 'inet', null: true
add_column :post_votes, :user_ip_addr, 'inet', null: true
end
end

View File

@ -598,7 +598,8 @@ CREATE TABLE public.comment_votes (
user_id integer NOT NULL,
score integer NOT NULL,
created_at timestamp without time zone,
updated_at timestamp without time zone
updated_at timestamp without time zone,
user_ip_addr inet
);
@ -1653,7 +1654,8 @@ CREATE TABLE public.post_votes (
user_id integer NOT NULL,
score integer NOT NULL,
created_at timestamp without time zone,
updated_at timestamp without time zone
updated_at timestamp without time zone,
user_ip_addr inet
);
@ -4691,6 +4693,7 @@ INSERT INTO "schema_migrations" (version) VALUES
('20190317024446'),
('20190324111703'),
('20190331193644'),
('20190403174011');
('20190403174011'),
('20190409195837');