From 371afc45e0fa69dc2268000e6a12fbb0626e6bfd Mon Sep 17 00:00:00 2001
From: Mark VanLandingham <mark.vanlandingham@discourse.org>
Date: Wed, 24 Mar 2021 10:22:16 -0500
Subject: [PATCH] DEV: API for plugins to add post update params and handlers
 (#12505)

---
 app/controllers/posts_controller.rb    |  4 ++++
 app/models/post.rb                     |  3 ++-
 lib/plugin/instance.rb                 |  7 +++++++
 lib/post_revisor.rb                    |  6 ++++++
 spec/requests/posts_controller_spec.rb | 28 ++++++++++++++++++++++++++
 5 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb
index 19e42aa1205..7123851c98e 100644
--- a/app/controllers/posts_controller.rb
+++ b/app/controllers/posts_controller.rb
@@ -208,6 +208,10 @@ class PostsController < ApplicationController
       edit_reason: params[:post][:edit_reason]
     }
 
+    Post.plugin_permitted_update_params.keys.each do |param|
+      changes[param] = params[:post][param]
+    end
+
     raw_old = params[:post][:raw_old]
     if raw_old.present? && raw_old != post.raw
       return render_json_error(I18n.t('edit_conflict'), status: 409)
diff --git a/app/models/post.rb b/app/models/post.rb
index 7bdc472599b..306bf38645d 100644
--- a/app/models/post.rb
+++ b/app/models/post.rb
@@ -15,8 +15,9 @@ class Post < ActiveRecord::Base
     "image_url" # TODO(2021-06-01): remove
   ]
 
-  cattr_accessor :plugin_permitted_create_params
+  cattr_accessor :plugin_permitted_create_params, :plugin_permitted_update_params
   self.plugin_permitted_create_params = {}
+  self.plugin_permitted_update_params = {}
 
   # increase this number to force a system wide post rebake
   # Recreate `index_for_rebake_old` when the number is increased
diff --git a/lib/plugin/instance.rb b/lib/plugin/instance.rb
index cf27db77f18..6551987001d 100644
--- a/lib/plugin/instance.rb
+++ b/lib/plugin/instance.rb
@@ -337,6 +337,13 @@ class Plugin::Instance
     end
   end
 
+  # Add a permitted_update_param to Post, respecting if the plugin is enabled
+  def add_permitted_post_update_param(attribute, &block)
+    reloadable_patch do |plugin|
+      ::Post.plugin_permitted_update_params[attribute] = { plugin: plugin, handler: block }
+    end
+  end
+
   # Add validation method but check that the plugin is enabled
   def validate(klass, name, &block)
     klass = klass.to_s.classify.constantize
diff --git a/lib/post_revisor.rb b/lib/post_revisor.rb
index eab76c38fdf..29989c01be2 100644
--- a/lib/post_revisor.rb
+++ b/lib/post_revisor.rb
@@ -143,6 +143,12 @@ class PostRevisor
     # previous reasons are lost
     @fields.delete(:edit_reason) if @fields[:edit_reason].blank?
 
+    Post.plugin_permitted_update_params.each do |field, val|
+      if @fields.key?(field) && val[:plugin].enabled?
+        val[:handler].call(@post, @fields[field])
+      end
+    end
+
     return false unless should_revise?
 
     @post.acting_user = @editor
diff --git a/spec/requests/posts_controller_spec.rb b/spec/requests/posts_controller_spec.rb
index 1d22435ac21..bbda98993d9 100644
--- a/spec/requests/posts_controller_spec.rb
+++ b/spec/requests/posts_controller_spec.rb
@@ -527,6 +527,34 @@ describe PostsController do
       expect(response.status).to eq(403)
       expect(post.topic.reload.category_id).not_to eq(category.id)
     end
+
+    describe "with Post.plugin_permitted_update_params" do
+      before do
+        plugin = Plugin::Instance.new
+        plugin.add_permitted_post_update_param(:random_number) do |post, value|
+          post.custom_fields[:random_number] = value
+          post.save
+        end
+      end
+
+      after do
+        DiscoursePluginRegistry.reset!
+      end
+
+      it "calls blocks passed into `add_permitted_post_update_param`" do
+        sign_in(post.user)
+        put "/posts/#{post.id}.json", params: {
+          post: {
+            raw: "this is a random post",
+            raw_old: post.raw,
+            random_number: 244
+          }
+        }
+
+        expect(response.status).to eq(200)
+        expect(post.reload.custom_fields[:random_number]).to eq("244")
+      end
+    end
   end
 
   describe "#destroy_bookmark" do