disown old coments

A user who wanted to 'start fresh' turned a bunch of discussions into swiss
cheese by deleting old comments. This allows disowning old comments, similar
to the option to disown all comments when self-deleting.
This commit is contained in:
Peter Bhat Harkins 2018-05-31 12:48:10 -05:00
parent 859f195e07
commit 28754db22b
12 changed files with 142 additions and 13 deletions

View File

@ -483,6 +483,16 @@ $(document).ready(function() {
}
});
$(document).on("click", "a.comment_disownor", function() {
if (confirm("Are you sure you want to disown this comment?")) {
var li = $(this).closest(".comment");
$.post("/comments/" + $(li).attr("data-shortid") + "/disown",
function(d) {
$(li).replaceWith(d);
});
}
});
$(document).on("click", "a.comment_moderator", function() {
var reason = prompt("Moderation reason:");
if (reason == null || reason == "")

View File

@ -139,6 +139,18 @@ class CommentsController < ApplicationController
:content_type => "text/html", :locals => { :comment => comment }
end
def disown
if !((comment = find_comment) && comment.is_disownable_by_user?(@user))
return render :plain => "can't find comment", :status => 400
end
InactiveUser.disown! comment
comment = find_comment
render :partial => "comment", :layout => false,
:content_type => "text/html", :locals => { :comment => comment }
end
def update
if !((comment = find_comment) && comment.is_editable_by_user?(@user))
return render :plain => "can't find comment", :status => 400

View File

@ -21,7 +21,7 @@ class SettingsController < ApplicationController
@user.delete!
if params[:disown].present?
@user.disown_comments!
InactiveUser.disown_all_by_author! @user
end
reset_session
flash[:success] = "Your account has been deleted."

View File

@ -27,6 +27,7 @@ class Comment < ApplicationRecord
scope :active, -> { where(:is_deleted => false, :is_moderated => false) }
DOWNVOTABLE_DAYS = 7
DELETEABLE_DAYS = DOWNVOTABLE_DAYS * 2
# the lowest a score can go
DOWNVOTABLE_MIN_SCORE = -10
@ -324,12 +325,16 @@ class Comment < ApplicationRecord
if user && user.is_moderator?
return true
elsif user && user.id == self.user_id
return true
return self.created_at >= DELETEABLE_DAYS.days.ago
else
return false
end
end
def is_disownable_by_user?(user)
user && user.id == self.user_id && self.created_at && self.created_at < DELETEABLE_DAYS.days.ago
end
def is_downvotable?
if self.created_at && self.score > DOWNVOTABLE_MIN_SCORE
Time.current - self.created_at <= DOWNVOTABLE_DAYS.days

View File

@ -0,0 +1,21 @@
module InactiveUser
def self.inactive_user
@inactive_user ||= User.find_by!(username: 'inactive-user')
end
def self.disown! comment
author = comment.user
comment.update_column(:user_id, inactive_user.id)
refresh_comment_counts! author
end
def self.disown_all_by_author! author
author.comments.update_all(:user_id => inactive_user.id)
refresh_comment_counts! author
end
def self.refresh_comment_counts! user
user.update_comments_posted_count! if user
inactive_user.update_comments_posted_count!
end
end

View File

@ -366,12 +366,6 @@ class User < ApplicationRecord
end
end
def disown_comments!
inactive_user = User.find_by!(:username => 'inactive-user')
self.comments.update_all(:user_id => inactive_user.id)
inactive_user.update_comments_posted_count!
end
def disable_2fa!
self.totp_secret = nil
self.save!

View File

@ -91,14 +91,17 @@ class="comment <%= comment.current_vote ? (comment.current_vote[:vote] == 1 ?
<% if comment.is_gone? && comment.is_undeletable_by_user?(@user) %>
|
<a tabindex="0" class="comment_undeletor">undelete</a>
<a tabindex="0" class="comment_undeletor" href="#">undelete</a>
<% elsif !comment.is_gone? && comment.is_deletable_by_user?(@user) %>
|
<% if @user && @user.is_moderator? && @user.id != comment.user_id %>
<a tabindex="0" class="comment_moderator">delete</a>
<a tabindex="0" class="comment_moderator" href="#">delete</a>
<% else %>
<a tabindex="0" class="comment_deletor">delete</a>
<a tabindex="0" class="comment_deletor" href="#">delete</a>
<% end %>
<% elsif !comment.is_gone? && comment.is_disownable_by_user?(@user) %>
|
<a tabindex="0" class="comment_disownor" href="#">disown</a>
<% end %>
<% if @user && !comment.story.is_gone? && !comment.is_gone? %>

View File

@ -89,6 +89,7 @@ Lobsters::Application.routes.draw do
post "delete"
post "undelete"
post "disown"
end
end
get "/comments/page/:page" => "comments#index"

View File

@ -3,7 +3,7 @@ require "rails_helper"
describe TagsController do
let(:user_id) { 5 }
before do
stub_login_as double('admin', id: user_id, is_active?: true, username: 'admin')
stub_login_as User.make!(id: user_id)
allow(controller).to receive(:require_logged_in_admin)
end

View File

@ -0,0 +1,82 @@
require 'rails_helper'
# uses page.driver.post because we're not running a full js engine,
# so the call can't just be click_on('delete'), etc.
RSpec.feature "Commenting" do
let(:story) { Story.make! title: "Example Story" }
let(:user) { User.make! username: 'user' }
before(:each) { stub_login_as user }
scenario 'posting a comment' do
visit "/s/#{story.short_id}"
expect(page).to have_button('Post')
fill_in 'comment', with: 'An example comment'
click_on 'Post'
visit "/s/#{story.short_id}"
expect(page).to have_content('example comment')
end
feature "deleting comments" do
scenario 'deleting a comment' do
comment = Comment.make!(
comment: 'An example comment',
user_id: user.id,
story_id: story.id,
created_at: 1.day.ago,
)
visit "/s/#{story.short_id}"
expect(page).to have_link('delete')
page.driver.post "/comments/#{comment.short_id}/delete"
visit "/s/#{story.short_id}"
expect(page).to have_link('undelete')
comment.reload
expect(comment.is_deleted?)
end
scenario 'trying to delete old comments' do
comment = Comment.make!(
comment: 'An example comment',
user_id: user.id,
story_id: story.id,
created_at: 90.days.ago,
)
visit "/s/#{story.short_id}"
expect(page).not_to have_link('delete')
page.driver.post "/comments/#{comment.short_id}/delete"
comment.reload
expect(!comment.is_deleted?)
end
end
feature "disowning comments" do
scenario 'disowning a comment' do
# bypass validations to create inactive-user:
User.make!.tap {|u| u.update_column :username, 'inactive-user' }
comment = Comment.make! user_id: user.id, story_id: story.id, created_at: 90.days.ago
visit "/s/#{story.short_id}"
expect(page).to have_link('disown')
page.driver.post "/comments/#{comment.short_id}/disown"
comment.reload
expect(comment.user).not_to eq(user)
visit "/s/#{story.short_id}"
expect(page).to have_content('inactive-user')
end
scenario 'trying to disown recent comments' do
comment = Comment.make! user_id: user.id, story_id: story.id, created_at: 1.day.ago
visit "/s/#{story.short_id}"
expect(page).not_to have_link('disown')
page.driver.post "/comments/#{comment.short_id}/disown"
puts page.body
comment.reload
expect(comment.user).to eq(user)
end
end
end

View File

@ -1,6 +1,6 @@
module AuthenticationHelper
def stub_login_as user
allow(User).to receive(:find_by).and_return(user)
user.update_column(:session_token, 'asdf')
allow_any_instance_of(ApplicationController).to receive(:session).and_return(u: 'asdf')
end
end

View File

@ -46,6 +46,7 @@ Comment.blueprint do
story_id { Story.make!.id }
comment { "comment text #{sn}" }
hat { nil }
created_at { Time.current }
end
Message.blueprint do