DEV: APIs for plugin to add custom reviewable confirm modal (#12246)

This commit is contained in:
Mark VanLandingham 2021-03-02 10:28:27 -06:00 committed by GitHub
parent 4c1e02d412
commit 4adce0d844
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 95 additions and 6 deletions

View File

@ -12,6 +12,14 @@ import showModal from "discourse/lib/show-modal";
let _components = {}; let _components = {};
const pluginReviewableParams = {};
export function addPluginReviewableParam(reviewableType, param) {
pluginReviewableParams[reviewableType]
? pluginReviewableParams[reviewableType].push(param)
: (pluginReviewableParams[reviewableType] = [param]);
}
export default Component.extend({ export default Component.extend({
adminTools: optionalService(), adminTools: optionalService(),
tagName: "", tagName: "",
@ -106,14 +114,23 @@ export default Component.extend({
let performAction = () => { let performAction = () => {
let version = reviewable.get("version"); let version = reviewable.get("version");
this.set("updating", true); this.set("updating", true);
const data = {
send_email: reviewable.sendEmail,
reject_reason: reviewable.rejectReason,
};
(pluginReviewableParams[reviewable.type] || []).forEach((param) => {
if (reviewable[param]) {
data[param] = reviewable[param];
}
});
return ajax( return ajax(
`/review/${reviewable.id}/perform/${action.id}?version=${version}`, `/review/${reviewable.id}/perform/${action.id}?version=${version}`,
{ {
type: "PUT", type: "PUT",
data: { data,
send_email: reviewable.sendEmail,
reject_reason: reviewable.rejectReason,
},
} }
) )
.then((result) => { .then((result) => {
@ -227,6 +244,7 @@ export default Component.extend({
let msg = action.get("confirm_message"); let msg = action.get("confirm_message");
let requireRejectReason = action.get("require_reject_reason"); let requireRejectReason = action.get("require_reject_reason");
let customModal = action.get("custom_modal");
if (msg) { if (msg) {
bootbox.confirm(msg, (answer) => { bootbox.confirm(msg, (answer) => {
if (answer) { if (answer) {
@ -241,6 +259,14 @@ export default Component.extend({
performConfirmed: this._performConfirmed.bind(this), performConfirmed: this._performConfirmed.bind(this),
action, action,
}); });
} else if (customModal) {
showModal(customModal, {
title: `review.${customModal}.title`,
model: this.reviewable,
}).setProperties({
performConfirmed: this._performConfirmed.bind(this),
action,
});
} else { } else {
return this._performConfirmed(action); return this._performConfirmed(action);
} }

View File

@ -44,6 +44,7 @@ import { addGTMPageChangedCallback } from "discourse/lib/page-tracker";
import { addGlobalNotice } from "discourse/components/global-notice"; import { addGlobalNotice } from "discourse/components/global-notice";
import { addNavItem } from "discourse/models/nav-item"; import { addNavItem } from "discourse/models/nav-item";
import { addPluginOutletDecorator } from "discourse/components/plugin-connector"; import { addPluginOutletDecorator } from "discourse/components/plugin-connector";
import { addPluginReviewableParam } from "discourse/components/reviewable-item";
import { addPopupMenuOptionsCallback } from "discourse/controllers/composer"; import { addPopupMenuOptionsCallback } from "discourse/controllers/composer";
import { addPostClassesCallback } from "discourse/widgets/post"; import { addPostClassesCallback } from "discourse/widgets/post";
import { addPostSmallActionIcon } from "discourse/widgets/post-small-action"; import { addPostSmallActionIcon } from "discourse/widgets/post-small-action";
@ -1222,6 +1223,9 @@ class PluginApi {
addSaveableUserOptionField(fieldName) { addSaveableUserOptionField(fieldName) {
addSaveableUserOptionField(fieldName); addSaveableUserOptionField(fieldName);
} }
addPluginReviewableParam(reviewableType, param) {
addPluginReviewableParam(reviewableType, param);
}
/** /**
* Change the default category background and text colors in the * Change the default category background and text colors in the

View File

@ -192,6 +192,11 @@ class ReviewablesController < ApplicationController
args.merge!(reject_reason: params[:reject_reason], send_email: params[:send_email] != "false") if reviewable.type == 'ReviewableUser' args.merge!(reject_reason: params[:reject_reason], send_email: params[:send_email] != "false") if reviewable.type == 'ReviewableUser'
plugin_params = DiscoursePluginRegistry.reviewable_params.select do |reviewable_param|
reviewable.type == reviewable_param[:type].to_s.classify
end
args.merge!(params.slice(*plugin_params.map { |pp| pp[:param] }).permit!)
result = reviewable.perform(current_user, params[:action_id].to_sym, args) result = reviewable.perform(current_user, params[:action_id].to_sym, args)
rescue Reviewable::InvalidAction => e rescue Reviewable::InvalidAction => e
# Consider InvalidAction an InvalidAccess # Consider InvalidAction an InvalidAccess

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class ReviewableActionSerializer < ApplicationSerializer class ReviewableActionSerializer < ApplicationSerializer
attributes :id, :icon, :button_class, :label, :confirm_message, :description, :client_action, :require_reject_reason attributes :id, :icon, :button_class, :label, :confirm_message, :description, :client_action, :require_reject_reason, :custom_modal
def label def label
I18n.t(object.label) I18n.t(object.label)
@ -30,4 +30,8 @@ class ReviewableActionSerializer < ApplicationSerializer
def include_require_reject_reason? def include_require_reject_reason?
object.require_reject_reason.present? object.require_reject_reason.present?
end end
def include_custom_modal?
object.custom_modal.present?
end
end end

View File

@ -84,6 +84,7 @@ class DiscoursePluginRegistry
define_filtered_register :user_api_key_scope_mappings define_filtered_register :user_api_key_scope_mappings
define_filtered_register :permitted_bulk_action_parameters define_filtered_register :permitted_bulk_action_parameters
define_filtered_register :reviewable_params
def self.register_auth_provider(auth_provider) def self.register_auth_provider(auth_provider)
self.auth_providers << auth_provider self.auth_providers << auth_provider

View File

@ -877,6 +877,13 @@ class Plugin::Instance
DiscoursePluginRegistry.demon_processes << demon_class DiscoursePluginRegistry.demon_processes << demon_class
end end
def add_permitted_reviewable_param(type, param)
DiscoursePluginRegistry.register_reviewable_param({
type: type,
param: param
}, self)
end
protected protected
def self.js_path def self.js_path

View File

@ -33,7 +33,7 @@ class Reviewable < ActiveRecord::Base
end end
class Action < Item class Action < Item
attr_accessor :icon, :button_class, :label, :description, :confirm_message, :client_action, :require_reject_reason attr_accessor :icon, :button_class, :label, :description, :confirm_message, :client_action, :require_reject_reason, :custom_modal
def initialize(id, icon = nil, button_class = nil, label = nil) def initialize(id, icon = nil, button_class = nil, label = nil)
super(id) super(id)

View File

@ -432,6 +432,48 @@ describe ReviewablesController do
end end
end end
describe "with reviewable params added via plugin API" do
class ::ReviewablePhony < Reviewable
def build_actions(actions, guardian, _args)
return [] unless pending?
actions.add(:approve_phony) do |action|
action.label = "js.phony.review.approve"
end
end
def perform_approve_phony(performed_by, args)
puts args.inspect
MessageBus.publish("/phony-reviewable-test", { args: args }, user_ids: [1])
create_result(:success, :approved)
end
end
before do
plugin = Plugin::Instance.new
plugin.add_permitted_reviewable_param(:reviewable_phony, :fake_id)
end
after do
DiscoursePluginRegistry.reset!
end
fab!(:reviewable_phony) { Fabricate(:reviewable, type: "ReviewablePhony") }
it "passes the added param into the reviewable class' perform method" do
MessageBus.expects(:publish)
.with("/phony-reviewable-test", { args: {
version: reviewable_phony.version,
"fake_id" => "2" }
},
{ user_ids: [1] })
.once
put "/review/#{reviewable_phony.id}/perform/approve_phony.json?version=#{reviewable_phony.version}", params: { fake_id: 2 }
expect(response.status).to eq(200)
end
end
context "#topics" do context "#topics" do
fab!(:post0) { Fabricate(:post) } fab!(:post0) { Fabricate(:post) }
fab!(:post1) { Fabricate(:post, topic: post0.topic) } fab!(:post1) { Fabricate(:post, topic: post0.topic) }