FEATURE: Let users delete their own topics. (#7267)

FEATURE: Let users delete their own topics. (#7267)

diff --git a/app/assets/javascripts/discourse/models/post.js.es6 b/app/assets/javascripts/discourse/models/post.js.es6
index 9202d58..ac60a21 100644
--- a/app/assets/javascripts/discourse/models/post.js.es6
+++ b/app/assets/javascripts/discourse/models/post.js.es6
@@ -210,8 +210,12 @@ const Post = RestModel.extend({
         can_recover: true
       });
     } else {
+      const key =
+        this.get("post_number") === 1
+          ? "topic.deleted_by_author"
+          : "post.deleted_by_author";
       promise = cookAsync(
-        I18n.t("post.deleted_by_author", {
+        I18n.t(key, {
           count: Discourse.SiteSettings.delete_removed_posts_after
         })
       ).then(cooked => {
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index e784eb8..720664b 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -2208,6 +2208,10 @@ en:
           one: You have selected <b>1</b> post.
           other: "You have selected <b>{{count}}</b> posts."
 
+      deleted_by_author:
+        one: "(topic withdrawn by author, will be automatically deleted in %{count} hour unless flagged)"
+        other: "(topic withdrawn by author, will be automatically deleted in %{count} hours unless flagged)"
+
     post:
       quote_reply: "Quote"
       edit_reason: "Reason: "
diff --git a/lib/guardian/post_guardian.rb b/lib/guardian/post_guardian.rb
index f70d766..545a7d2 100644
--- a/lib/guardian/post_guardian.rb
+++ b/lib/guardian/post_guardian.rb
@@ -168,7 +168,7 @@ module PostGuardian
 
   # Deleting Methods
   def can_delete_post?(post)
-    can_see_post?(post)
+    return false if !can_see_post?(post)
 
     # Can't delete the first post
     return false if post.is_first_post?
diff --git a/lib/guardian/topic_guardian.rb b/lib/guardian/topic_guardian.rb
index d11c877..e58b158 100644
--- a/lib/guardian/topic_guardian.rb
+++ b/lib/guardian/topic_guardian.rb
@@ -86,13 +86,17 @@ module TopicGuardian
 
   # Recovery Method
   def can_recover_topic?(topic)
-    topic && topic.deleted_at && topic.user && is_staff?
+    if is_staff?
+      !!(topic && topic.deleted_at && topic.user)
+    else
+      topic && can_recover_post?(topic.ordered_posts.first)
+    end
   end
 
   def can_delete_topic?(topic)
     !topic.trashed? &&
-    is_staff? &&
-    !(topic.is_category_topic?) &&
+    (is_staff? || (topic.posts_count <= 1 && topic.created_at && topic.created_at > 24.hours.ago)) &&
+    !topic.is_category_topic? &&
     !Discourse.static_doc_topic_ids.include?(topic.id)
   end
 
diff --git a/lib/post_destroyer.rb b/lib/post_destroyer.rb
index 3c6ea4a..10e44f2 100644
--- a/lib/post_destroyer.rb
+++ b/lib/post_destroyer.rb
@@ -167,8 +167,9 @@ class PostDestroyer
     I18n.with_locale(SiteSetting.default_locale) do
 
       # don't call revise from within transaction, high risk of deadlock
+      key = @post.is_first_post? ? 'js.topic.deleted_by_author' : 'js.post.deleted_by_author'
       @post.revise(@user,
-        { raw: I18n.t('js.post.deleted_by_author', count: delete_removed_posts_after) },
+        { raw: I18n.t(key, count: delete_removed_posts_after) },
         force_new_version: true
       )
 
diff --git a/spec/components/guardian_spec.rb b/spec/components/guardian_spec.rb
index b91bfe3..48d0334 100644
--- a/spec/components/guardian_spec.rb
+++ b/spec/components/guardian_spec.rb
@@ -1774,8 +1774,14 @@ describe Guardian do
         expect(Guardian.new(Fabricate(:user)).can_delete?(post)).to be_falsey
       end
 
-      it "returns false when it's the OP, even as a moderator" do
-        post.update_attribute :post_number, 1
+      it "returns true when it's the OP" do
+        post.update!(post_number: 1)
+        expect(Guardian.new(moderator).can_delete?(post)).to be_falsey
+      end
+
+      it "returns false when it's the OP, even as a moderator if there are at least two posts" do
+        post.update!(post_number: 1)
+        Fabricate(:post, topic: post.topic)
         expect(Guardian.new(moderator).can_delete?(post)).to be_falsey
       end
 
diff --git a/spec/components/post_destroyer_spec.rb b/spec/components/post_destroyer_spec.rb
index e6758fa..115d5d2 100644
--- a/spec/components/post_destroyer_spec.rb
+++ b/spec/components/post_destroyer_spec.rb
@@ -283,7 +283,7 @@ describe PostDestroyer do
         expect(post2.deleted_at).to be_blank
         expect(post2.deleted_by).to be_blank
         expect(post2.user_deleted).to eq(true)
-        expect(post2.raw).to eq(I18n.t('js.post.deleted_by_author', count: 24))
+        expect(post2.raw).to eq(I18n.t('js.topic.deleted_by_author', count: 24))
         expect(post2.version).to eq(2)
         expect(called).to eq(1)
         expect(user_stat.reload.post_count).to eq(0)
@@ -338,6 +338,8 @@ describe PostDestroyer do
     it "accepts a delete_removed_posts_after option" do
       SiteSetting.delete_removed_posts_after = 0
 
+      post.update!(post_number: 2)
+
       PostDestroyer.new(post.user, post, delete_removed_posts_after: 1).destroy
 
       post.reload
diff --git a/spec/requests/topics_controller_spec.rb b/spec/requests/topics_controller_spec.rb
index ad9910e..36b4748 100644
--- a/spec/requests/topics_controller_spec.rb
+++ b/spec/requests/topics_controller_spec.rb
@@ -833,7 +833,7 @@ RSpec.describe TopicsController do
     describe 'when logged in' do
       let(:user) { Fabricate(:user) }
       let(:moderator) { Fabricate(:moderator) }
-      let(:topic) { Fabricate(:topic, user: user) }
+      let(:topic) { Fabricate(:topic, user: user, created_at: 48.hours.ago) }
       let!(:post) { Fabricate(:post, topic: topic, user: user, post_number: 1) }
 
       describe 'without access' do

GitHub sha: 31053f30

FIX: Let users delete topics.