Add site setting to approve/unapprove historical category expert posts (#36)

Add site setting to approve/unapprove historical category expert posts (#36)

diff --git a/app/jobs/regular/approve_past_category_expert_posts.rb b/app/jobs/regular/approve_past_category_expert_posts.rb
new file mode 100644
index 0000000..cfc68e6
--- /dev/null
+++ b/app/jobs/regular/approve_past_category_expert_posts.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Jobs
+  class ApprovePastCategoryExpertPosts < ::Jobs::Base
+
+    sidekiq_options queue: 'low'
+
+    def execute(args)
+      return unless SiteSetting.approve_past_posts_on_becoming_category_expert
+
+      unless args[:user_id].kind_of?(Integer)
+        raise Discourse::InvalidParameters.new(:user_id)
+      end
+
+      unless args[:category_ids].kind_of?(Array)
+        raise Discourse::InvalidParameters.new(:category_ids)
+      end
+
+      posts = Post.joins(:topic)
+        .where(user_id: args[:user_id])
+        .where(topic: { category_id: args[:category_ids] })
+
+      posts.each do |post|
+        CategoryExperts::PostHandler.new(post: post).process_new_post(skip_validations: true)
+      end
+    end
+  end
+end
diff --git a/app/jobs/regular/unapprove_past_category_expert_posts.rb b/app/jobs/regular/unapprove_past_category_expert_posts.rb
new file mode 100644
index 0000000..fc60d52
--- /dev/null
+++ b/app/jobs/regular/unapprove_past_category_expert_posts.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+module Jobs
+  class UnapprovePastCategoryExpertPosts < ::Jobs::Base
+
+    sidekiq_options queue: 'low'
+
+    def execute(args)
+      return unless SiteSetting.approve_past_posts_on_becoming_category_expert
+
+      unless args[:user_id].kind_of?(Integer)
+        raise Discourse::InvalidParameters.new(:user_id)
+      end
+
+      unless args[:category_ids].kind_of?(Array)
+        raise Discourse::InvalidParameters.new(:category_ids)
+      end
+
+      posts = Post.joins(:topic)
+        .where(user_id: args[:user_id])
+        .where(topic: { category_id: args[:category_ids] })
+
+      posts.group_by(&:topic).each do |topic, grouped_posts|
+        grouped_posts.each do |post|
+          post.custom_fields.delete(CategoryExperts::POST_APPROVED_GROUP_NAME)
+          post.custom_fields.delete(CategoryExperts::POST_PENDING_EXPERT_APPROVAL)
+          post.save
+        end
+
+        other_approved_post_count = PostCustomField
+          .where(post_id: topic.post_ids)
+          .where(name: CategoryExperts::POST_APPROVED_GROUP_NAME)
+          .count
+
+        if other_approved_post_count == 0
+          topic.custom_fields.delete(CategoryExperts::TOPIC_EXPERT_POST_GROUP_NAMES)
+          topic.save
+        end
+      end
+    end
+  end
+end
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index e5051fa..d111074 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -5,6 +5,7 @@ en:
     category_experts_posts_require_approval: "Posts from category experts require staff approval for expert decoration."
     send_category_experts_reminder_pms: "Send reminder PMs to category experts, to reply to unanswered questions"
     send_admin_category_experts_posts_reminder_pm: "Send reminder PMs to staff, to review unapproved category expert posts."
+    approve_past_posts_on_becoming_category_expert: "When a user becomes an expert, all their past posts in the category are automatically approved. When a user is removed as an expert, all their posts are unapproved."
   system_messages:
     user_add_as_category_expert:
       title: "Congratulations, You are a category expert!"
diff --git a/config/settings.yml b/config/settings.yml
index 50b593a..45c4084 100644
--- a/config/settings.yml
+++ b/config/settings.yml
@@ -12,3 +12,5 @@ plugins:
     default: false
   send_admin_category_experts_posts_reminder_pm:
     default: false
+  approve_past_posts_on_becoming_category_expert:
+    default: false
diff --git a/lib/category_experts/post_handler.rb b/lib/category_experts/post_handler.rb
index 30b2834..ba4c4f3 100644
--- a/lib/category_experts/post_handler.rb
+++ b/lib/category_experts/post_handler.rb
@@ -9,16 +9,20 @@ module CategoryExperts
       @user = user || post.user
     end
 
-    def process_new_post
-      return unless ensure_poster_is_category_expert
+    def process_new_post(skip_validations: false)
+      if !skip_validations
+        return unless ensure_poster_is_category_expert
+      end
 
       SiteSetting.category_experts_posts_require_approval ?
-        mark_post_for_approval :
-        mark_post_as_approved
+        mark_post_for_approval(skip_validations: skip_validations) :
+        mark_post_as_approved(skip_validations: skip_validations)
     end
 
-    def mark_post_for_approval
-      raise Discourse::InvalidParameters unless ensure_poster_is_category_expert
+    def mark_post_for_approval(skip_validations: false)
+      if !skip_validations
+        raise Discourse::InvalidParameters unless ensure_poster_is_category_expert
+      end
 
       post_group_name = post.custom_fields[CategoryExperts::POST_APPROVED_GROUP_NAME]
 
@@ -48,8 +52,10 @@ module CategoryExperts
       topic.save!
     end
 
-    def mark_post_as_approved
-      raise Discourse::InvalidParameters unless ensure_poster_is_category_expert
+    def mark_post_as_approved(skip_validations: false)
+      if !skip_validations
+        raise Discourse::InvalidParameters unless ensure_poster_is_category_expert
+      end
 
       post.custom_fields[CategoryExperts::POST_APPROVED_GROUP_NAME] = users_expert_group.name
       post.custom_fields[CategoryExperts::POST_PENDING_EXPERT_APPROVAL] = false
diff --git a/plugin.rb b/plugin.rb
index eb11990..216f283 100644
--- a/plugin.rb
+++ b/plugin.rb
@@ -17,6 +17,8 @@ after_initialize do
     "../app/models/category_expert_endorsement",
     "../app/models/reviewable_category_expert_suggestion",
     "../app/serializers/reviewable_category_expert_suggestion_serializer",
+    "../app/jobs/regular/approve_past_category_expert_posts",
+    "../app/jobs/regular/unapprove_past_category_expert_posts",
     "../app/jobs/scheduled/remind_admin_of_category_experts_posts_job",
     "../app/jobs/scheduled/remind_category_experts_job",
     "../lib/category_experts/post_handler",
@@ -280,6 +282,39 @@ after_initialize do
     result
   end
 
+  add_to_class(:group, :category_expert_category_ids) do
+    category_ids = []
+
+    CategoryCustomField
+      .where(name: CategoryExperts::CATEGORY_EXPERT_GROUP_IDS)
+      .where.not(value: nil)
+      .where.not(value: "")
+      .pluck(:category_id, :value)
+      .each do |category_id, group_ids|
+        category_ids.push(category_id) if group_ids.split("|").map(&:to_i).include?(self.id)
+      end
+
+    category_ids
+  end
+
+  DiscourseEvent.on(:user_added_to_group) do |user, group|
+    next if !SiteSetting.approve_past_posts_on_becoming_category_expert
+
+    category_ids = group.category_expert_category_ids
+    next if category_ids.empty?
+
+    ::Jobs.enqueue(:approve_past_category_expert_posts, user_id: user.id, category_ids: category_ids)
+  end
+
+  DiscourseEvent.on(:user_removed_from_group) do |user, group|
+    next if !SiteSetting.approve_past_posts_on_becoming_category_expert
+
+    category_ids = group.category_expert_category_ids
+    next if category_ids.empty?
+
+    ::Jobs.enqueue(:unapprove_past_category_expert_posts, user_id: user.id, category_ids: category_ids)
+  end
+
   Discourse::Application.routes.append do
     put "category-experts/endorse/:username" => "category_experts#endorse", constraints: { username: ::RouteFormat.username }
     post "category-experts/approve" => "category_experts#approve_post"
diff --git a/spec/lib/post_handler_spec.rb b/spec/lib/post_handler_spec.rb
index 2f847e0..cc5e127 100644
--- a/spec/lib/post_handler_spec.rb
+++ b/spec/lib/post_handler_spec.rb
@@ -12,8 +12,6 @@ describe CategoryExperts::PostHandler do
   fab!(:topic) { Fabricate(:topic, category: category) }
 
   before do
-    SiteSetting.enable_category_experts

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

GitHub sha: bc611e97

This commit appears in #36 which was approved by nlalonde. It was merged by markvanlan.