FEATURE: Review every post using the review queue. (#12734)

FEATURE: Review every post using the review queue. (#12734)

  • FEATURE: Review every post using the review queue.

If the review_every_post setting is enabled, posts created and edited by regular uses are sent to the review queue so staff can review them. We’ll skip PMs and posts created or edited by TL4 or staff users.

Staff can choose to:

  • Approve the post (nothing happens)
  • Approve and restore the post (if deleted)
  • Approve and unhide the post (if hidden)
  • Reject and delete it
  • Reject and keep deleted (if deleted)
  • Reject and suspend the user
  • Reject and silence the user
  • Update config/locales/server.en.yml

Co-authored-by: Robin Ward robin.ward@gmail.com

Co-authored-by: Robin Ward robin.ward@gmail.com

diff --git a/app/assets/javascripts/discourse/app/components/reviewable-flagged-post.js b/app/assets/javascripts/discourse/app/components/reviewable-flagged-post.js
deleted file mode 100644
index 84c27f1..0000000
--- a/app/assets/javascripts/discourse/app/components/reviewable-flagged-post.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import Component from "@ember/component";
-import discourseComputed from "discourse-common/utils/decorators";
-import { gt } from "@ember/object/computed";
-import { historyHeat } from "discourse/widgets/post-edits-indicator";
-import { longDate } from "discourse/lib/formatter";
-import showModal from "discourse/lib/show-modal";
-
-export default Component.extend({
-  hasEdits: gt("reviewable.post_version", 1),
-
-  @discourseComputed("reviewable.post_updated_at")
-  historyClass(updatedAt) {
-    return historyHeat(this.siteSettings, new Date(updatedAt));
-  },
-
-  @discourseComputed("reviewable.post_updated_at")
-  editedDate(updatedAt) {
-    return longDate(updatedAt);
-  },
-
-  actions: {
-    showEditHistory() {
-      let postId = this.get("reviewable.post_id");
-      this.store.find("post", postId).then((post) => {
-        let historyController = showModal("history", {
-          model: post,
-          modalClass: "history-modal",
-        });
-        historyController.refresh(postId, "latest");
-        historyController.set("post", post);
-        historyController.set("topicController", null);
-      });
-    },
-  },
-});
diff --git a/app/assets/javascripts/discourse/app/components/reviewable-post-edits.js b/app/assets/javascripts/discourse/app/components/reviewable-post-edits.js
new file mode 100644
index 0000000..84c27f1
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/components/reviewable-post-edits.js
@@ -0,0 +1,35 @@
+import Component from "@ember/component";
+import discourseComputed from "discourse-common/utils/decorators";
+import { gt } from "@ember/object/computed";
+import { historyHeat } from "discourse/widgets/post-edits-indicator";
+import { longDate } from "discourse/lib/formatter";
+import showModal from "discourse/lib/show-modal";
+
+export default Component.extend({
+  hasEdits: gt("reviewable.post_version", 1),
+
+  @discourseComputed("reviewable.post_updated_at")
+  historyClass(updatedAt) {
+    return historyHeat(this.siteSettings, new Date(updatedAt));
+  },
+
+  @discourseComputed("reviewable.post_updated_at")
+  editedDate(updatedAt) {
+    return longDate(updatedAt);
+  },
+
+  actions: {
+    showEditHistory() {
+      let postId = this.get("reviewable.post_id");
+      this.store.find("post", postId).then((post) => {
+        let historyController = showModal("history", {
+          model: post,
+          modalClass: "history-modal",
+        });
+        historyController.refresh(postId, "latest");
+        historyController.set("post", post);
+        historyController.set("topicController", null);
+      });
+    },
+  },
+});
diff --git a/app/assets/javascripts/discourse/app/templates/components/reviewable-flagged-post.hbs b/app/assets/javascripts/discourse/app/templates/components/reviewable-flagged-post.hbs
index f18feef..553f12d 100644
--- a/app/assets/javascripts/discourse/app/templates/components/reviewable-flagged-post.hbs
+++ b/app/assets/javascripts/discourse/app/templates/components/reviewable-flagged-post.hbs
@@ -1,12 +1,6 @@
 <div class="flagged-post-header">
   {{reviewable-topic-link reviewable=reviewable tagName=""}}
-  {{#if hasEdits}}
-    <a href {{action "showEditHistory"}}
-      class="has-edits {{historyClass}}"
-      title={{i18n "post.last_edited_on" dateTime=editedDate}}>
-      {{d-icon "pencil-alt"}}
-    </a>
-  {{/if}}
+  {{reviewable-post-edits reviewable=reviewable tagName=""}}
 </div>
 
 <div class="post-contents-wrapper">
diff --git a/app/assets/javascripts/discourse/app/templates/components/reviewable-post-edits.hbs b/app/assets/javascripts/discourse/app/templates/components/reviewable-post-edits.hbs
new file mode 100644
index 0000000..e6f7581
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/templates/components/reviewable-post-edits.hbs
@@ -0,0 +1,7 @@
+{{#if hasEdits}}
+  <a href {{action "showEditHistory"}}
+    class="has-edits {{historyClass}}"
+    title={{i18n "post.last_edited_on" dateTime=editedDate}}>
+    {{d-icon "pencil-alt"}}
+  </a>
+{{/if}}
diff --git a/app/assets/javascripts/discourse/app/templates/components/reviewable-post.hbs b/app/assets/javascripts/discourse/app/templates/components/reviewable-post.hbs
new file mode 100644
index 0000000..2731067
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/templates/components/reviewable-post.hbs
@@ -0,0 +1,19 @@
+<div class="flagged-post-header">
+  {{reviewable-topic-link reviewable=reviewable tagName=""}}
+  {{reviewable-post-edits reviewable=reviewable tagName=""}}
+</div>
+
+<div class="post-contents-wrapper">
+  {{reviewable-created-by user=reviewable.target_created_by tagName=""}}
+  <div class="post-contents">
+    {{reviewable-post-header reviewable=reviewable createdBy=reviewable.target_created_by tagName=""}}
+    <div class="post-body">
+      {{#if reviewable.blank_post}}
+        <p>{{i18n "review.deleted_post"}}</p>
+      {{else}}
+        {{html-safe reviewable.cooked}}
+      {{/if}}
+    </div>
+    {{yield}}
+  </div>
+</div>
diff --git a/app/models/reviewable.rb b/app/models/reviewable.rb
index 438fb51..cb96447 100644
--- a/app/models/reviewable.rb
+++ b/app/models/reviewable.rb
@@ -95,7 +95,7 @@ class Reviewable < ActiveRecord::Base
   end
 
   def self.types
-    %w[ReviewableFlaggedPost ReviewableQueuedPost ReviewableUser]
+    %w[ReviewableFlaggedPost ReviewableQueuedPost ReviewableUser ReviewablePost]
   end
 
   def self.custom_filters
diff --git a/app/models/reviewable_post.rb b/app/models/reviewable_post.rb
new file mode 100644
index 0000000..8dbb065
--- /dev/null
+++ b/app/models/reviewable_post.rb
@@ -0,0 +1,111 @@
+# frozen_string_literal: true
+
+class ReviewablePost < Reviewable
+  def self.action_aliases
+    { reject_and_silence: :reject_and_suspend }
+  end
+
+  def self.queue_for_review_if_possible(post, created_or_edited_by)
+    return unless SiteSetting.review_every_post
+    return if post.post_type != Post.types[:regular] || post.topic.private_message?
+    return if Reviewable.where(target: post, status: Reviewable.statuses[:pending]).exists?
+    return if created_or_edited_by.bot? || created_or_edited_by.staff? || created_or_edited_by.has_trust_level?(TrustLevel[4])
+    system_user = Discourse.system_user
+
+    needs_review!(
+      target: post,
+      topic: post.topic,
+      created_by: system_user,
+      reviewable_by_moderator: true,
+      potential_spam: false
+    ).tap do |reviewable|
+      reviewable.add_score(
+        system_user,
+        ReviewableScore.types[:needs_approval],
+        force_review: true
+      )
+    end
+  end
+
+  def build_actions(actions, guardian, args)
+    return unless pending?
+
+    if post.trashed? && guardian.can_recover_post?(post)
+      build_action(actions, :approve_and_restore, icon: 'check')
+    elsif post.hidden?
+      build_action(actions, :approve_and_unhide, icon: 'check')
+    else
+      build_action(actions, :approve, icon: 'check')
+    end
+
+    reject = actions.add_bundle(
+      "#{id}-reject", icon: 'times', label: 'reviewables.actions.reject.bundle_title'
+    )
+
+    if post.trashed?
+      build_action(actions, :reject_and_keep_deleted, icon: 'trash-alt', bundle: reject)
+    elsif guardian.can_delete_post_or_topic?(post)
+      build_action(actions, :reject_and_delete, icon: 'trash-alt', bundle: reject)
+    end
+
+    if guardian.can_suspend?(target_created_by)
+      build_action(actions, :reject_and_suspend, icon: 'ban', bundle: reject, client_action: 'suspend')
+      build_action(actions, :reject_and_silence, icon: 'microphone-slash', bundle: reject, client_action: 'silence')
+    end
+  end
+
+  def perform_approve(performed_by, _args)

[... diff too long, it was truncated ...]

GitHub sha: 6b613e30

This commit appears in #12734 which was approved by eviltrout. It was merged by romanrizzi.