Add deleted_by to Trashable tables

This commit is contained in:
Robin Ward
2013-07-09 15:20:18 -04:00
parent 4c0fe3bc12
commit b7327942af
18 changed files with 62 additions and 38 deletions

View File

@ -77,9 +77,7 @@ Discourse.FlaggedPost = Discourse.Post.extend({
return Discourse.ajax('/admin/flags/agree/' + this.id, { type: 'POST', cache: false }); return Discourse.ajax('/admin/flags/agree/' + this.id, { type: 'POST', cache: false });
}, },
postHidden: function() { postHidden: Em.computed.alias('hidden'),
return (this.get('hidden'));
}.property(),
extraClasses: function() { extraClasses: function() {
var classes = []; var classes = [];
@ -92,9 +90,8 @@ Discourse.FlaggedPost = Discourse.Post.extend({
return classes.join(' '); return classes.join(' ');
}.property(), }.property(),
deleted: function() { deleted: Em.computed.or('deleted_at', 'topic_deleted_at')
return (this.get('deleted_at') || this.get('topic_deleted_at'));
}.property()
}); });
Discourse.FlaggedPost.reopenClass({ Discourse.FlaggedPost.reopenClass({

View File

@ -32,7 +32,7 @@ class InvitesController < ApplicationController
invite = Invite.where(invited_by_id: current_user.id, email: params[:email]).first invite = Invite.where(invited_by_id: current_user.id, email: params[:email]).first
raise Discourse::InvalidParameters.new(:email) if invite.blank? raise Discourse::InvalidParameters.new(:email) if invite.blank?
invite.trash! invite.trash!(current_user)
render nothing: true render nothing: true
end end

View File

@ -169,7 +169,7 @@ class TopicsController < ApplicationController
def destroy def destroy
topic = Topic.where(id: params[:id]).first topic = Topic.where(id: params[:id]).first
guardian.ensure_can_delete!(topic) guardian.ensure_can_delete!(topic)
topic.trash! topic.trash!(current_user)
render nothing: true render nothing: true
end end

View File

@ -54,9 +54,9 @@ class Post < ActiveRecord::Base
@types ||= Enum.new(:regular, :moderator_action) @types ||= Enum.new(:regular, :moderator_action)
end end
def trash! def trash!(trashed_by=nil)
self.topic_links.each(&:destroy) self.topic_links.each(&:destroy)
super super(trashed_by)
end end
def recover! def recover!

View File

@ -63,7 +63,7 @@ class PostAction < ActiveRecord::Base
moderator_id == -1 ? PostActionType.auto_action_flag_types.values : PostActionType.flag_types.values moderator_id == -1 ? PostActionType.auto_action_flag_types.values : PostActionType.flag_types.values
end end
PostAction.where({ post_id: post.id, post_action_type_id: actions }).update_all({ deleted_at: Time.zone.now, deleted_by: moderator_id }) PostAction.where({ post_id: post.id, post_action_type_id: actions }).update_all({ deleted_at: Time.zone.now, deleted_by_id: moderator_id })
f = actions.map{|t| ["#{PostActionType.types[t]}_count", 0]} f = actions.map{|t| ["#{PostActionType.types[t]}_count", 0]}
Post.where(id: post.id).with_deleted.update_all(Hash[*f.flatten]) Post.where(id: post.id).with_deleted.update_all(Hash[*f.flatten])
update_flagged_posts_count update_flagged_posts_count
@ -140,13 +140,13 @@ class PostAction < ActiveRecord::Base
user_id: user.id, user_id: user.id,
post_action_type_id: post_action_type_id:
post_action_type_id).first post_action_type_id).first
action.trash! action.trash!(user)
action.run_callbacks(:save) action.run_callbacks(:save)
end end
end end
def remove_act!(user) def remove_act!(user)
trash! trash!(user)
run_callbacks(:save) run_callbacks(:save)
end end
@ -390,7 +390,7 @@ end
# deleted_at :datetime # deleted_at :datetime
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# deleted_by :integer # deleted_by_id :integer
# message :text # message :text
# related_post_id :integer # related_post_id :integer
# staff_took_action :boolean default(FALSE), not null # staff_took_action :boolean default(FALSE), not null

View File

@ -21,9 +21,9 @@ class Topic < ActiveRecord::Base
versioned if: :new_version_required? versioned if: :new_version_required?
def trash! def trash!(trashed_by=nil)
update_category_topic_count_by(-1) if deleted_at.nil? update_category_topic_count_by(-1) if deleted_at.nil?
super super(trashed_by)
update_flagged_posts_count update_flagged_posts_count
end end

View File

@ -0,0 +1,8 @@
class AddDeletedByIdToPosts < ActiveRecord::Migration
def change
add_column :posts, :deleted_by_id, :integer, null: true
add_column :topics, :deleted_by_id, :integer, null: true
add_column :invites, :deleted_by_id, :integer, null: true
rename_column :post_actions, :deleted_by, :deleted_by_id
end
end

View File

@ -37,12 +37,14 @@ class PostDestroyer
# Feature users in the topic # Feature users in the topic
Jobs.enqueue(:feature_topic_users, topic_id: @post.topic_id, except_post_id: @post.id) Jobs.enqueue(:feature_topic_users, topic_id: @post.topic_id, except_post_id: @post.id)
@post.post_actions.map(&:trash!) @post.post_actions.each do |pa|
pa.trash!(@user)
end
f = PostActionType.types.map{|k,v| ["#{k}_count", 0]} f = PostActionType.types.map{|k,v| ["#{k}_count", 0]}
Post.with_deleted.where(id: @post.id).update_all(Hash[*f.flatten]) Post.with_deleted.where(id: @post.id).update_all(Hash[*f.flatten])
@post.trash! @post.trash!(@user)
Topic.reset_highest(@post.topic_id) Topic.reset_highest(@post.topic_id)
@ -59,7 +61,7 @@ class PostDestroyer
# Remove any notifications that point to this deleted post # Remove any notifications that point to this deleted post
Notification.delete_all topic_id: @post.topic_id, post_number: @post.post_number Notification.delete_all topic_id: @post.topic_id, post_number: @post.post_number
@post.topic.trash! if @post.post_number == 1 @post.topic.trash!(@user) if @post.post_number == 1
end end
end end

View File

@ -5,6 +5,8 @@ module Trashable
default_scope where(with_deleted_scope_sql) default_scope where(with_deleted_scope_sql)
# scope unscoped does not work # scope unscoped does not work
belongs_to :deleted_by, class_name: 'User'
end end
@ -24,23 +26,31 @@ module Trashable
end end
end end
def trash! def trash!(trashed_by=nil)
# note, an argument could be made that the column should probably called trashed_at # note, an argument could be made that the column should probably called trashed_at
# however, deleted_at is the terminology used in the UI # however, deleted_at is the terminology used in the UI
# #
# we could hijack use a delete! and delete - redirecting the originals elsewhere, but that is # we could hijack use a delete! and delete - redirecting the originals elsewhere, but that is
# confusing as well. So for now, we go with trash! # confusing as well. So for now, we go with trash!
# #
update_column(:deleted_at, DateTime.now) trash_update(DateTime.now, trashed_by.try(:id))
end end
def recover! def recover!
# see: https://github.com/rails/rails/issues/8436 trash_update(nil, nil)
#
# Fixed in Rails 4
#
self.class.unscoped.where(id: self.id).update_all({deleted_at: nil})
raw_write_attribute :deleted_at, nil
end end
private
def trash_update(deleted_at, deleted_by_id)
# see: https://github.com/rails/rails/issues/8436
#
# Fixed in Rails 4
#
self.class.unscoped.where(id: self.id).update_all(deleted_at: deleted_at, deleted_by_id: deleted_by_id)
raw_write_attribute :deleted_at, deleted_at
raw_write_attribute :deleted_by_id, deleted_by_id
end
end end

View File

@ -229,20 +229,22 @@ describe Guardian do
end end
describe 'a Post' do describe 'a Post' do
let(:another_admin) { Fabricate(:admin) }
it 'correctly handles post visibility' do it 'correctly handles post visibility' do
post = Fabricate(:post) post = Fabricate(:post)
topic = post.topic topic = post.topic
Guardian.new(user).can_see?(post).should be_true Guardian.new(user).can_see?(post).should be_true
post.trash! post.trash!(another_admin)
post.reload post.reload
Guardian.new(user).can_see?(post).should be_false Guardian.new(user).can_see?(post).should be_false
Guardian.new(admin).can_see?(post).should be_true Guardian.new(admin).can_see?(post).should be_true
post.recover! post.recover!
post.reload post.reload
topic.trash! topic.trash!(another_admin)
topic.reload topic.reload
Guardian.new(user).can_see?(post).should be_false Guardian.new(user).can_see?(post).should be_false
Guardian.new(admin).can_see?(post).should be_true Guardian.new(admin).can_see?(post).should be_true

View File

@ -23,6 +23,7 @@ describe PostDestroyer do
it "doesn't delete the post" do it "doesn't delete the post" do
post.deleted_at.should be_blank post.deleted_at.should be_blank
post.deleted_by.should be_blank
post.raw.should == I18n.t('js.post.deleted_by_author') post.raw.should == I18n.t('js.post.deleted_by_author')
post.version.should == 2 post.version.should == 2
end end
@ -35,6 +36,7 @@ describe PostDestroyer do
it "deletes the post" do it "deletes the post" do
post.deleted_at.should be_present post.deleted_at.should be_present
post.deleted_by.should == moderator
end end
end end
@ -45,6 +47,7 @@ describe PostDestroyer do
it "deletes the post" do it "deletes the post" do
post.deleted_at.should be_present post.deleted_at.should be_present
post.deleted_by.should == admin
end end
end end

View File

@ -29,7 +29,7 @@ describe InvitesController do
end end
it "destroys the invite" do it "destroys the invite" do
Invite.any_instance.expects(:trash!) Invite.any_instance.expects(:trash!).with(user)
delete :destroy, email: invite.email delete :destroy, email: invite.email
end end

View File

@ -137,7 +137,7 @@ describe PostActionsController do
end end
it "works with a deleted post" do it "works with a deleted post" do
flagged_post.trash! flagged_post.trash!(user)
xhr :post, :clear_flags, id: flagged_post.id, post_action_type_id: PostActionType.types[:spam] xhr :post, :clear_flags, id: flagged_post.id, post_action_type_id: PostActionType.types[:spam]
response.should be_success response.should be_success
end end

View File

@ -14,7 +14,8 @@ describe PostsController do
describe 'show' do describe 'show' do
let(:post) { Fabricate(:post, user: log_in) } let(:user) { log_in }
let(:post) { Fabricate(:post, user: user) }
it 'ensures the user can see the post' do it 'ensures the user can see the post' do
Guardian.any_instance.expects(:can_see?).with(post).returns(false) Guardian.any_instance.expects(:can_see?).with(post).returns(false)
@ -30,7 +31,7 @@ describe PostsController do
context "deleted post" do context "deleted post" do
before do before do
post.trash! post.trash!(user)
end end
it "can't find deleted posts as an anonymous user" do it "can't find deleted posts as an anonymous user" do

View File

@ -58,7 +58,7 @@ describe PostAction do
end end
context "a post is deleted" do context "a post is deleted" do
When { spam_post.trash!; spammer.reload } When { spam_post.trash!(moderator); spammer.reload }
Then { expect(spammer.reload).to be_blocked } Then { expect(spammer.reload).to be_blocked }
end end

View File

@ -215,7 +215,7 @@ describe Notification do
Notification.create!(read: false, user_id: p2.user_id, topic_id: p2.topic_id, post_number: p2.post_number, data: '[]', Notification.create!(read: false, user_id: p2.user_id, topic_id: p2.topic_id, post_number: p2.post_number, data: '[]',
notification_type: Notification.types[:liked]) notification_type: Notification.types[:liked])
p2.trash! p2.trash!(p.user)
# we may want to make notification "trashable" but for now we nuke pm notifications from deleted topics/posts # we may want to make notification "trashable" but for now we nuke pm notifications from deleted topics/posts
Notification.ensure_consistency! Notification.ensure_consistency!

View File

@ -1296,16 +1296,17 @@ describe Topic do
describe 'trash!' do describe 'trash!' do
context "its category's topic count" do context "its category's topic count" do
let(:moderator) { Fabricate(:moderator) }
let(:category) { Fabricate(:category) } let(:category) { Fabricate(:category) }
it "subtracts 1 if topic is being deleted" do it "subtracts 1 if topic is being deleted" do
topic = Fabricate(:topic, category: category) topic = Fabricate(:topic, category: category)
expect { topic.trash! }.to change { category.reload.topic_count }.by(-1) expect { topic.trash!(moderator) }.to change { category.reload.topic_count }.by(-1)
end end
it "doesn't subtract 1 if topic is already deleted" do it "doesn't subtract 1 if topic is already deleted" do
topic = Fabricate(:topic, category: category, deleted_at: 1.day.ago) topic = Fabricate(:topic, category: category, deleted_at: 1.day.ago)
expect { topic.trash! }.to_not change { category.reload.topic_count } expect { topic.trash!(moderator) }.to_not change { category.reload.topic_count }
end end
end end
end end

View File

@ -62,7 +62,7 @@ describe UserAction do
other_stats.should == expecting other_stats.should == expecting
public_topic.trash! public_topic.trash!(user)
stats_for_user.should == [] stats_for_user.should == []
stream_count.should == 0 stream_count.should == 0