From 2055804e95825453a6ce1386a0d355b96ae0e016 Mon Sep 17 00:00:00 2001
From: Robin Ward <robin.ward@gmail.com>
Date: Thu, 4 Apr 2019 15:51:36 -0400
Subject: [PATCH] FIX: The option to delete replies was missing from the new
 review queue

---
 app/models/reviewable_flagged_post.rb       | 39 ++++++++++++++++++
 config/locales/server.en.yml                | 12 +++++-
 spec/models/reviewable_flagged_post_spec.rb | 44 ++++++++++++++++++++-
 3 files changed, 92 insertions(+), 3 deletions(-)

diff --git a/app/models/reviewable_flagged_post.rb b/app/models/reviewable_flagged_post.rb
index 4270aafcf6e..4b1acb01468 100644
--- a/app/models/reviewable_flagged_post.rb
+++ b/app/models/reviewable_flagged_post.rb
@@ -67,7 +67,25 @@ class ReviewableFlaggedPost < Reviewable
     if guardian.is_staff?
       delete = actions.add_bundle("#{id}-delete", icon: "far-trash-alt", label: "reviewables.actions.delete.title")
       build_action(actions, :delete_and_ignore, icon: 'external-link-alt', bundle: delete)
+      if post.reply_count > 0
+        build_action(
+          actions,
+          :delete_and_ignore_replies,
+          icon: 'external-link-alt',
+          confirm: true,
+          bundle: delete
+        )
+      end
       build_action(actions, :delete_and_agree, icon: 'thumbs-up', bundle: delete)
+      if post.reply_count > 0
+        build_action(
+          actions,
+          :delete_and_agree_replies,
+          icon: 'external-link-alt',
+          bundle: delete,
+          confirm: true
+        )
+      end
     end
   end
 
@@ -192,11 +210,32 @@ class ReviewableFlaggedPost < Reviewable
     result
   end
 
+  def perform_delete_and_ignore_replies(performed_by, args)
+    result = perform_ignore(performed_by, args)
+
+    replies = PostReply.where(post_id: post.id).includes(:reply).map(&:reply)
+    PostDestroyer.new(performed_by, post).destroy
+    replies.each { |reply| PostDestroyer.new(performed_by, reply).destroy }
+
+    result
+  end
+
   def perform_delete_and_agree(performed_by, args)
     result = agree(performed_by, args)
     PostDestroyer.new(performed_by, post).destroy
     result
   end
+
+  def perform_delete_and_agree_replies(performed_by, args)
+    result = agree(performed_by, args)
+
+    replies = PostReply.where(post_id: post.id).includes(:reply).map(&:reply)
+    PostDestroyer.new(performed_by, post).destroy
+    replies.each { |reply| PostDestroyer.new(performed_by, reply).destroy }
+
+    result
+  end
+
 protected
 
   def agree(performed_by, args)
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index 03c62f725e4..a37ee802f3f 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -4397,11 +4397,19 @@ en:
       delete:
         title: "Delete..."
       delete_and_ignore:
-        title: "Delete Post and Ignore flag"
+        title: "Delete Post and Ignore"
         description: "Delete post; if the first post, delete the topic as well"
+      delete_and_ignore_replies:
+        title: "Delete Post + Replies and Ignore"
+        description: "Delete post and all of its replies; if the first post, delete the topic as well"
+        confirm: "Are you sure you want to delete the replies to the post as well?"
       delete_and_agree:
-        title: "Delete Post and Agree with flag"
+        title: "Delete Post and Agree"
         description: "Delete post; if the first post, delete the topic as well"
+      delete_and_agree_replies:
+        title: "Delete Post + Replies and Agree"
+        description: "Delete post and all of its replies; if the first post, delete the topic as well"
+        confirm: "Are you sure you want to delete the replies to the post as well?"
       disagree_and_restore:
         title: "Disagree and Restore Post"
         description: "Restore the post so that all users can see it."
diff --git a/spec/models/reviewable_flagged_post_spec.rb b/spec/models/reviewable_flagged_post_spec.rb
index 0faf8d2527c..7ea36fb0abe 100644
--- a/spec/models/reviewable_flagged_post_spec.rb
+++ b/spec/models/reviewable_flagged_post_spec.rb
@@ -34,7 +34,9 @@ RSpec.describe ReviewableFlaggedPost, type: :model do
         expect(actions.has?(:disagree)).to eq(true)
         expect(actions.has?(:ignore)).to eq(true)
         expect(actions.has?(:delete_and_ignore)).to eq(true)
+        expect(actions.has?(:delete_and_ignore_replies)).to eq(false)
         expect(actions.has?(:delete_and_agree)).to eq(true)
+        expect(actions.has?(:delete_and_replies)).to eq(false)
 
         expect(actions.has?(:disagree_and_restore)).to eq(false)
       end
@@ -44,6 +46,13 @@ RSpec.describe ReviewableFlaggedPost, type: :model do
         expect(reviewable.actions_for(guardian).has?(:agree_and_restore)).to eq(true)
       end
 
+      it "returns delete replies options if there are replies" do
+        post.update(reply_count: 3)
+        expect(reviewable.actions_for(guardian).has?(:delete_and_agree_replies)).to eq(true)
+        expect(reviewable.actions_for(guardian).has?(:delete_and_ignore_replies)).to eq(true)
+      end
+
+
       it "returns appropriate actions for a hidden post" do
         post.update(hidden: true, hidden_at: Time.now)
         expect(reviewable.actions_for(guardian).has?(:agree_and_hide)).to eq(false)
@@ -107,13 +116,29 @@ RSpec.describe ReviewableFlaggedPost, type: :model do
       expect(score.reload).to be_ignored
     end
 
-    it "delete_and_defer ignores the flags and deletes post" do
+    it "delete_and_ignore ignores the flags and deletes post" do
       reviewable.perform(moderator, :delete_and_ignore)
       expect(reviewable).to be_ignored
       expect(score.reload).to be_ignored
       expect(post.reload.deleted_at).to be_present
     end
 
+    it "delete_and_ignore_replies ignores the flags and deletes post + replies" do
+      reply = PostCreator.create(
+        Fabricate(:user),
+        raw: 'this is the reply text',
+        reply_to_post_number: post.post_number,
+        topic_id: post.topic_id
+      )
+      post.reload
+
+      reviewable.perform(moderator, :delete_and_ignore_replies)
+      expect(reviewable).to be_ignored
+      expect(score.reload).to be_ignored
+      expect(post.reload.deleted_at).to be_present
+      expect(reply.reload.deleted_at).to be_present
+    end
+
     it "delete_and_agree agrees with the flags and deletes post" do
       reviewable.perform(moderator, :delete_and_agree)
       expect(reviewable).to be_approved
@@ -121,6 +146,23 @@ RSpec.describe ReviewableFlaggedPost, type: :model do
       expect(post.reload.deleted_at).to be_present
     end
 
+    it "delete_and_agree_replies agrees w/ the flags and deletes post + replies" do
+      reply = PostCreator.create(
+        Fabricate(:user),
+        raw: 'this is the reply text',
+        reply_to_post_number: post.post_number,
+        topic_id: post.topic_id
+      )
+      post.reload
+
+      reviewable.perform(moderator, :delete_and_agree_replies)
+      expect(reviewable).to be_approved
+      expect(score.reload).to be_agreed
+      expect(post.reload.deleted_at).to be_present
+      expect(reply.reload.deleted_at).to be_present
+    end
+
+
     it "disagrees with the flags" do
       reviewable.perform(moderator, :disagree)
       expect(reviewable).to be_rejected