diff --git a/Gemfile b/Gemfile index 80e9f287d4f..d5d81356d2c 100644 --- a/Gemfile +++ b/Gemfile @@ -15,7 +15,6 @@ gem 'simple_handlebars_rails', path: 'vendor/gems/simple_handlebars_rails' gem 'redcarpet', require: false gem 'activerecord-postgres-hstore' -gem 'acts_as_paranoid' gem 'active_attr' # until we get ActiveModel::Model with Rails 4 gem 'airbrake', '3.1.2', require: false # errbit is broken with 3.1.3 for now gem 'clockwork', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 8e97a4263b5..3532e0c3d49 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -120,8 +120,6 @@ GEM activesupport (3.2.12) i18n (~> 0.6) multi_json (~> 1.0) - acts_as_paranoid (0.4.1) - activerecord (~> 3.2) airbrake (3.1.2) activesupport builder @@ -448,7 +446,6 @@ DEPENDENCIES active_attr active_model_serializers! activerecord-postgres-hstore - acts_as_paranoid airbrake (= 3.1.2) barber (= 0.3.0) better_errors diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb index 9d2bec01d30..dbaf94e7fb2 100644 --- a/app/controllers/invites_controller.rb +++ b/app/controllers/invites_controller.rb @@ -33,7 +33,7 @@ class InvitesController < ApplicationController invite = Invite.where(invited_by_id: current_user.id, email: params[:email]).first raise Discourse::InvalidParameters.new(:email) if invite.blank? - invite.destroy + invite.trash! render nothing: true end diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 774a4dcd5b8..f94381c1ec9 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -128,7 +128,7 @@ class PostsController < ApplicationController def recover post = find_post_from_params guardian.ensure_can_recover_post!(post) - post.recover + post.recover! render nothing: true end diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb index 74014b41a4b..ae966f8dc3b 100644 --- a/app/controllers/topics_controller.rb +++ b/app/controllers/topics_controller.rb @@ -100,7 +100,7 @@ class TopicsController < ApplicationController def destroy topic = Topic.where(id: params[:id]).first guardian.ensure_can_delete!(topic) - topic.destroy + topic.trash! render nothing: true end diff --git a/app/models/invite.rb b/app/models/invite.rb index 8427afccd9b..6a8c737a881 100644 --- a/app/models/invite.rb +++ b/app/models/invite.rb @@ -1,4 +1,7 @@ +require_dependency 'trashable' + class Invite < ActiveRecord::Base + include Trashable belongs_to :user belongs_to :topic @@ -9,8 +12,6 @@ class Invite < ActiveRecord::Base validates_presence_of :email validates_presence_of :invited_by_id - acts_as_paranoid - before_create do self.invite_key ||= SecureRandom.hex end diff --git a/app/models/post.rb b/app/models/post.rb index c4500a7470d..d5c1724ea32 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -3,19 +3,19 @@ require_dependency 'pretty_text' require_dependency 'rate_limiter' require_dependency 'post_revisor' require_dependency 'enum' +require_dependency 'trashable' require 'archetype' require 'digest/sha1' class Post < ActiveRecord::Base include RateLimiter::OnCreateRecord + include Trashable versioned if: :raw_changed? rate_limit - acts_as_paranoid - after_recover :update_flagged_posts_count belongs_to :user belongs_to :topic, counter_cache: :posts_count @@ -52,6 +52,11 @@ class Post < ActiveRecord::Base @types ||= Enum.new(:regular, :moderator_action) end + def recover! + super + update_flagged_posts_count + end + def raw_quality sentinel = TextSentinel.body_sentinel(raw) errors.add(:raw, I18n.t(:is_invalid)) unless sentinel.valid? diff --git a/app/models/post_action.rb b/app/models/post_action.rb index 0167ad38910..59b3cd12b43 100644 --- a/app/models/post_action.rb +++ b/app/models/post_action.rb @@ -1,10 +1,12 @@ require_dependency 'rate_limiter' require_dependency 'system_message' +require_dependency 'trashable' class PostAction < ActiveRecord::Base class AlreadyActed < StandardError; end include RateLimiter::OnCreateRecord + include Trashable attr_accessible :post_action_type_id, :post_id, :user_id, :post, :user, :post_action_type, :message, :related_post_id @@ -12,8 +14,6 @@ class PostAction < ActiveRecord::Base belongs_to :user belongs_to :post_action_type - acts_as_paranoid - rate_limit :post_action_rate_limiter validate :message_quality @@ -114,8 +114,7 @@ class PostAction < ActiveRecord::Base def self.remove_act(user, post, post_action_type_id) if action = where(post_id: post.id, user_id: user.id, post_action_type_id: post_action_type_id).first - action.destroy - action.deleted_at = Time.zone.now + action.trash! action.run_callbacks(:save) end end diff --git a/app/models/topic.rb b/app/models/topic.rb index 1fa8e1d9398..eef3bb19a7e 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -4,10 +4,12 @@ require_dependency 'topic_view' require_dependency 'rate_limiter' require_dependency 'text_sentinel' require_dependency 'text_cleaner' +require_dependency 'trashable' class Topic < ActiveRecord::Base include ActionView::Helpers include RateLimiter::OnCreateRecord + include Trashable def self.max_sort_order 2**31 - 1 @@ -18,9 +20,17 @@ class Topic < ActiveRecord::Base end versioned if: :new_version_required? - acts_as_paranoid - after_recover :update_flagged_posts_count - after_destroy :update_flagged_posts_count + + + def trash! + super + update_flagged_posts_count + end + + def recover! + super + update_flagged_posts_count + end rate_limit :default_rate_limiter rate_limit :limit_topics_per_day diff --git a/app/models/user.rb b/app/models/user.rb index 1d70b503656..fb9f8916dc9 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -442,11 +442,11 @@ class User < ActiveRecord::Base posts.order("post_number desc").each do |p| if p.post_number == 1 - p.topic.destroy + p.topic.trash! # TODO: But the post is not destroyed. Why? else # TODO: This should be using the PostDestroyer! - p.destroy + p.trash! end end end diff --git a/docs/SOFTWARE.md b/docs/SOFTWARE.md index a13d104ebac..bfe79c6f584 100644 --- a/docs/SOFTWARE.md +++ b/docs/SOFTWARE.md @@ -14,7 +14,6 @@ The following Ruby Gems are used in Discourse: * [rack-mini-profiler](https://rubygems.org/gems/rack-mini-profiler) * [sass](https://rubygems.org/gems/sass) * [rest-client](https://rubygems.org/gems/rest-client) -* [rails3_acts_as_paranoid](https://rubygems.org/gems/rails3_acts_as_paranoid) * [activerecord-postgres-hstore](https://rubygems.org/gems/activerecord-postgres-hstore) * [fastimage](https://rubygems.org/gems/fastimage) * [seed-fu](https://rubygems.org/gems/seed-fu) diff --git a/lib/post_destroyer.rb b/lib/post_destroyer.rb index c5a2515b011..52c88ee3564 100644 --- a/lib/post_destroyer.rb +++ b/lib/post_destroyer.rb @@ -37,8 +37,7 @@ class PostDestroyer # Feature users in the topic Jobs.enqueue(:feature_topic_users, topic_id: @post.topic_id, except_post_id: @post.id) - # Actually soft-delete the post :) - @post.destroy + @post.trash! Topic.reset_highest(@post.topic_id) @post.update_flagged_posts_count diff --git a/lib/trashable.rb b/lib/trashable.rb new file mode 100644 index 00000000000..919545579aa --- /dev/null +++ b/lib/trashable.rb @@ -0,0 +1,32 @@ +module Trashable + extend ActiveSupport::Concern + + included do + default_scope where(with_deleted_scope_sql) + end + + + module ClassMethods + def with_deleted + # lifted from acts_as_paranoid, works around http://stackoverflow.com/questions/8734669/rails-3-1-3-unscoped-scope + scope = self.scoped.with_default_scope + scope.where_values.delete(with_deleted_scope_sql) + scope + end + + def with_deleted_scope_sql + self.scoped.table[:deleted_at].eq(nil).to_sql + end + end + + def trash! + self.update_column(:deleted_at, DateTime.now) + end + + def recover! + # see: https://github.com/rails/rails/issues/8436 + self.class.unscoped.update_all({deleted_at: nil}, id: self.id) + raw_write_attribute :deleted_at, nil + end + +end diff --git a/spec/components/guardian_spec.rb b/spec/components/guardian_spec.rb index ff9e70875ea..ef461a0de93 100644 --- a/spec/components/guardian_spec.rb +++ b/spec/components/guardian_spec.rb @@ -222,14 +222,14 @@ describe Guardian do it 'correctly handles post visibility' do Guardian.new(user).can_see?(post).should be_true - post.destroy + post.trash! post.reload Guardian.new(user).can_see?(post).should be_false Guardian.new(admin).can_see?(post).should be_true - post.recover + post.recover! post.reload - topic.destroy + topic.trash! topic.reload Guardian.new(user).can_see?(post).should be_false Guardian.new(admin).can_see?(post).should be_true diff --git a/spec/components/topic_view_spec.rb b/spec/components/topic_view_spec.rb index e5eb30348e0..5ebf3f004e0 100644 --- a/spec/components/topic_view_spec.rb +++ b/spec/components/topic_view_spec.rb @@ -196,7 +196,7 @@ describe TopicView do describe "filter_posts_after" do it "returns undeleted posts after a post" do - topic_view.filter_posts_after(p1.post_number).should == [p2, p3, p5] + topic_view.filter_posts_after(p1.post_number).map(&:id).should == [p2.id, p3.id, p5.id] topic_view.should_not be_initial_load topic_view.index_offset.should == 1 topic_view.index_reverse.should be_false diff --git a/spec/controllers/invites_controller_spec.rb b/spec/controllers/invites_controller_spec.rb index 56feee6437f..473553cf2d8 100644 --- a/spec/controllers/invites_controller_spec.rb +++ b/spec/controllers/invites_controller_spec.rb @@ -29,7 +29,7 @@ describe InvitesController do end it "destroys the invite" do - Invite.any_instance.expects(:destroy) + Invite.any_instance.expects(:trash!) delete :destroy, email: invite.email end diff --git a/spec/controllers/post_actions_controller_spec.rb b/spec/controllers/post_actions_controller_spec.rb index a0a1dd2544b..29a782a7f9c 100644 --- a/spec/controllers/post_actions_controller_spec.rb +++ b/spec/controllers/post_actions_controller_spec.rb @@ -120,7 +120,7 @@ describe PostActionsController do end it "works with a deleted post" do - flagged_post.destroy + flagged_post.trash! xhr :post, :clear_flags, id: flagged_post.id, post_action_type_id: PostActionType.types[:spam] response.should be_success end diff --git a/spec/controllers/posts_controller_spec.rb b/spec/controllers/posts_controller_spec.rb index 9d067207fd4..2456d952a7c 100644 --- a/spec/controllers/posts_controller_spec.rb +++ b/spec/controllers/posts_controller_spec.rb @@ -30,7 +30,7 @@ describe PostsController do context "deleted post" do before do - post.destroy + post.trash! end it "can't find deleted posts as an anonymous user" do @@ -121,7 +121,7 @@ describe PostsController do end it "calls recover" do - Post.any_instance.expects(:recover) + Post.any_instance.expects(:recover!) xhr :put, :recover, post_id: post.id end diff --git a/spec/models/post_action_spec.rb b/spec/models/post_action_spec.rb index 980d75a5f72..edab1bd98b1 100644 --- a/spec/models/post_action_spec.rb +++ b/spec/models/post_action_spec.rb @@ -78,7 +78,7 @@ describe PostAction do it "should reset counts when a topic is deleted" do PostAction.act(codinghorror, post, PostActionType.types[:off_topic]) - post.topic.destroy + post.topic.trash! PostAction.flagged_posts_count.should == 0 end diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index 8e9be27b614..1816d3716d1 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -50,7 +50,7 @@ describe Post do let(:post) { Fabricate(:post, post_args) } before do - post.destroy + post.trash! post.reload end @@ -60,7 +60,7 @@ describe Post do describe "recovery" do before do - post.recover + post.recover! post.reload end diff --git a/spec/models/user_action_spec.rb b/spec/models/user_action_spec.rb index 8782ffdf096..8a1c3644937 100644 --- a/spec/models/user_action_spec.rb +++ b/spec/models/user_action_spec.rb @@ -58,7 +58,7 @@ describe UserAction do other_stats.should == expecting - public_topic.destroy + public_topic.trash! stats_for_user.should == [] stream_count.should == 0 @@ -66,7 +66,7 @@ describe UserAction do category = Fabricate(:category, secure: true) - public_topic.recover + public_topic.recover! public_topic.category = category public_topic.save