Spiegel von
https://github.com/paviliondev/discourse-custom-wizard.git
synchronisiert 2024-11-25 18:50:27 +01:00
Add custom wizard integration
Dieser Commit ist enthalten in:
Ursprung
819c76b3ff
Commit
5ffcee1dde
14 geänderte Dateien mit 175 neuen und 30 gelöschten Zeilen
|
@ -3,7 +3,8 @@ import { default as computed, observes } from 'ember-addons/ember-computed-decor
|
|||
const ACTION_TYPES = [
|
||||
{ id: 'create_topic', name: 'Create Topic' },
|
||||
{ id: 'update_profile', name: 'Update Profile' },
|
||||
{ id: 'send_message', name: 'Send Message' }
|
||||
{ id: 'send_message', name: 'Send Message' },
|
||||
{ id: 'send_to_api', name: 'Send to API' }
|
||||
];
|
||||
|
||||
const PROFILE_FIELDS = [
|
||||
|
@ -26,6 +27,8 @@ export default Ember.Component.extend({
|
|||
createTopic: Ember.computed.equal('action.type', 'create_topic'),
|
||||
updateProfile: Ember.computed.equal('action.type', 'update_profile'),
|
||||
sendMessage: Ember.computed.equal('action.type', 'send_message'),
|
||||
sendToApi: Ember.computed.equal('action.type', 'send_to_api'),
|
||||
apiEmpty: Ember.computed.empty('action.api'),
|
||||
disableId: Ember.computed.not('action.isNew'),
|
||||
|
||||
@computed('currentStepId', 'wizard.save_submissions')
|
||||
|
@ -75,5 +78,21 @@ export default Ember.Component.extend({
|
|||
toggleCustomCategoryWizardField() {
|
||||
const user = this.get('action.custom_category_user_field');
|
||||
if (user) this.set('action.custom_category_wizard_field', false);
|
||||
},
|
||||
|
||||
@computed('wizard.apis')
|
||||
availableApis(apis) {
|
||||
return apis.map(a => {
|
||||
return {
|
||||
id: a.name,
|
||||
name: a.title
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
@computed('wizard.apis', 'action.api')
|
||||
availableEndpoints(apis, api) {
|
||||
if (!api) return [];
|
||||
return apis.find(a => a.name === api).endpoints;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -78,7 +78,6 @@ export default Ember.Controller.extend({
|
|||
if (api.title) data['title'] = api.title;
|
||||
|
||||
const originalTitle = this.get('api.originalTitle');
|
||||
console.log(api, originalTitle);
|
||||
if (api.get('isNew') || (originalTitle && (api.title !== originalTitle))) {
|
||||
refreshList = true;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,8 @@ export default Discourse.Route.extend({
|
|||
afterModel(model) {
|
||||
return Ember.RSVP.all([
|
||||
this._getFieldTypes(model),
|
||||
this._getThemes(model)
|
||||
this._getThemes(model),
|
||||
this._getApis(model)
|
||||
]);
|
||||
},
|
||||
|
||||
|
@ -48,6 +49,11 @@ export default Discourse.Route.extend({
|
|||
});
|
||||
},
|
||||
|
||||
_getApis(model) {
|
||||
return ajax('/admin/wizards/apis')
|
||||
.then((result) => model.set('apis', result));
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
const newWizard = this.get('newWizard');
|
||||
const steps = model.get('steps') || [];
|
||||
|
|
|
@ -96,7 +96,7 @@
|
|||
<label>{{i18n 'admin.wizard.action.post_builder.user_fields'}}{{builderUserFields}}</label>
|
||||
<label>{{i18n 'admin.wizard.action.post_builder.wizard_fields'}}{{builderWizardFields}}</label>
|
||||
{{d-editor value=action.post_template
|
||||
placeholder='admin.wizard.action.post_builder.placeholder'
|
||||
placeholder='admin.wizard.action.interpolate_fields'
|
||||
classNames='post-builder-editor'}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -157,7 +157,7 @@
|
|||
<label>{{i18n 'admin.wizard.action.post_builder.user_fields'}}{{builderUserFields}}</label>
|
||||
<label>{{i18n 'admin.wizard.action.post_builder.wizard_fields'}}{{builderWizardFields}}</label>
|
||||
{{d-editor value=action.post_template
|
||||
placeholder='admin.wizard.action.post_builder.placeholder'
|
||||
placeholder='admin.wizard.action.interpolate_fields'
|
||||
classNames='post-builder-editor'}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -204,3 +204,41 @@
|
|||
allowUserField=true}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if sendToApi}}
|
||||
<div class="setting">
|
||||
<div class="setting-label">
|
||||
<h3>{{i18n "admin.wizard.action.send_to_api.api"}}</h3>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{combo-box value=action.api
|
||||
content=availableApis
|
||||
none='admin.wizard.action.send_to_api.select_an_api'
|
||||
isDisabled=action.custom_title_enabled}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-label">
|
||||
<h3>{{i18n "admin.wizard.action.send_to_api.endpoint"}}</h3>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{combo-box value=action.api_endpoint
|
||||
content=availableEndpoints
|
||||
none='admin.wizard.action.send_to_api.select_an_endpoint'
|
||||
isDisabled=apiEmpty}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting api-body">
|
||||
<div class="setting-label">
|
||||
<h3>{{i18n "admin.wizard.action.send_to_api.body"}}</h3>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
<label>{{i18n 'admin.wizard.action.post_builder.user_fields'}}{{builderUserFields}}</label>
|
||||
<label>{{i18n 'admin.wizard.action.post_builder.wizard_fields'}}{{builderWizardFields}}</label>
|
||||
{{textarea value=action.api_body
|
||||
placeholder=(i18n 'admin.wizard.action.interpolate_fields')}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
|
|
@ -117,6 +117,23 @@
|
|||
margin-top: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.api-body {
|
||||
width: 100%;
|
||||
|
||||
.setting-label {
|
||||
max-width: 70px;
|
||||
}
|
||||
|
||||
.setting-value {
|
||||
width: calc(100% - 180px);
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
min-height: 150px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wizard-links {
|
||||
|
|
|
@ -103,6 +103,8 @@ en:
|
|||
add_fields: "{{type}} Fields"
|
||||
available_fields: "* If 'Save wizard submissions' is disabled, only the fields of the current step are available to the current step's actions."
|
||||
topic_attr: "Topic Attribute"
|
||||
interpolate_fields: "Insert wizard fields using the field_id in w{}. Insert user fields using field key in u{}."
|
||||
|
||||
skip_redirect:
|
||||
label: "Skip Redirect"
|
||||
description: "Don't redirect the user to this {{type}} after the wizard completes"
|
||||
|
@ -120,12 +122,19 @@ en:
|
|||
label: "Builder"
|
||||
user_fields: "User Fields: "
|
||||
wizard_fields: "Wizard Fields: "
|
||||
placeholder: "Insert wizard fields using the field_id in w{}. Insert user fields using field key in u{}."
|
||||
custom_title: "Custom Title"
|
||||
custom_category:
|
||||
label: "Custom Category"
|
||||
wizard_field: "Wizard Field"
|
||||
user_field: "User Field"
|
||||
send_to_api:
|
||||
label: "Send to API"
|
||||
api: "API"
|
||||
endpoint: "Endpoint"
|
||||
select_an_api: "Select an API"
|
||||
select_an_endpoint: "Select an endpoint"
|
||||
body: "Request body JSON"
|
||||
|
||||
api:
|
||||
label: "API"
|
||||
nav_label: 'APIs'
|
||||
|
|
|
@ -106,10 +106,10 @@ class CustomWizard::ApiController < ::ApplicationController
|
|||
:client_secret,
|
||||
:username,
|
||||
:password,
|
||||
:params
|
||||
:auth_params
|
||||
)
|
||||
|
||||
auth_data[:params] = JSON.parse(auth_data[:params]) if auth_data[:params].present?
|
||||
auth_data[:auth_params] = JSON.parse(auth_data[:auth_params]) if auth_data[:auth_params].present?
|
||||
|
||||
@auth_data ||= auth_data
|
||||
end
|
||||
|
|
|
@ -32,6 +32,8 @@ class CustomWizard::Api::Authorization
|
|||
end
|
||||
|
||||
def self.set(api_name, new_data = {})
|
||||
api_name = api_name.underscore
|
||||
|
||||
data = self.get(api_name, data_only: true) || {}
|
||||
|
||||
new_data.each do |k, v|
|
||||
|
@ -44,6 +46,8 @@ class CustomWizard::Api::Authorization
|
|||
end
|
||||
|
||||
def self.get(api_name, opts = {})
|
||||
api_name = api_name.underscore
|
||||
|
||||
if data = PluginStore.get("custom_wizard_api_#{api_name}", 'authorization')
|
||||
if opts[:data_only]
|
||||
data
|
||||
|
@ -60,22 +64,16 @@ class CustomWizard::Api::Authorization
|
|||
end
|
||||
|
||||
def self.get_header_authorization_string(name)
|
||||
protocol = authentication_protocol(name)
|
||||
raise Discourse::InvalidParameters.new(:name) unless protocol.present?
|
||||
raise Discourse::InvalidParameters.new(:protocol) unless [BASIC_AUTH, OAUTH2_AUTH].include? protocol
|
||||
auth = CustomWizard::Api::Authorization.get(name)
|
||||
raise Discourse::InvalidParameters.new(:name) unless auth.present?
|
||||
|
||||
if protocol = BASIC_AUTH
|
||||
username = username(name)
|
||||
raise Discourse::InvalidParameters.new(:username) unless username.present?
|
||||
password = password(name)
|
||||
raise Discourse::InvalidParameters.new(:password) unless password.present?
|
||||
authorization_string = (username + ":" + password).chomp
|
||||
"Basic #{Base64.strict_encode64(authorization_string)}"
|
||||
if auth.auth_type === "basic"
|
||||
raise Discourse::InvalidParameters.new(:username) unless auth.username.present?
|
||||
raise Discourse::InvalidParameters.new(:password) unless auth.password.present?
|
||||
"Basic #{Base64.strict_encode64((auth.username + ":" + auth.password).chomp)}"
|
||||
else
|
||||
# must be OAUTH2
|
||||
access_token = access_token(name)
|
||||
raise Discourse::InvalidParameters.new(access_token) unless access_token.present?
|
||||
"Bearer #{access_token}"
|
||||
raise Discourse::InvalidParameters.new(auth.access_token) unless auth.access_token.present?
|
||||
"Bearer #{auth.access_token}"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -16,8 +16,13 @@ class CustomWizard::Api::Endpoint
|
|||
end
|
||||
|
||||
def self.set(api_name, new_data)
|
||||
data = new_data[:endpoint_id] ? self.get(api_name, new_data[:endpoint_id], data_only: true) : {}
|
||||
endpoint_id = new_data[:endpoint_id] || SecureRandom.hex(3)
|
||||
if new_data['id']
|
||||
data = self.get(api_name, new_data['id'], data_only: true)
|
||||
endpoint_id = new_data['id']
|
||||
else
|
||||
data = {}
|
||||
endpoint_id = SecureRandom.hex(3)
|
||||
end
|
||||
|
||||
new_data.each do |k, v|
|
||||
data[k.to_sym] = v
|
||||
|
@ -32,11 +37,10 @@ class CustomWizard::Api::Endpoint
|
|||
return nil if !endpoint_id
|
||||
|
||||
if data = PluginStore.get("custom_wizard_api_#{api_name}", "endpoint_#{endpoint_id}")
|
||||
data[:id] = endpoint_id
|
||||
|
||||
if opts[:data_only]
|
||||
data
|
||||
else
|
||||
data[:id] = endpoint_id
|
||||
self.new(api_name, data)
|
||||
end
|
||||
else
|
||||
|
@ -48,8 +52,8 @@ class CustomWizard::Api::Endpoint
|
|||
PluginStoreRow.where("plugin_name = 'custom_wizard_api_#{api_name}' AND key LIKE 'endpoint_%'").destroy_all
|
||||
end
|
||||
|
||||
def self.list
|
||||
PluginStoreRow.where("plugin_name LIKE 'custom_wizard_api_%' AND key LIKE 'endpoint_%'")
|
||||
def self.list(api_name)
|
||||
PluginStoreRow.where("plugin_name LIKE 'custom_wizard_api_#{api_name}' AND key LIKE 'endpoint_%'")
|
||||
.map do |record|
|
||||
api_name = record['plugin_name'].sub("custom_wizard_api_", "")
|
||||
data = ::JSON.parse(record['value'])
|
||||
|
@ -57,4 +61,32 @@ class CustomWizard::Api::Endpoint
|
|||
self.new(api_name, data)
|
||||
end
|
||||
end
|
||||
|
||||
def self.request(api_name, endpoint_id, body)
|
||||
endpoint = self.get(api_name, endpoint_id)
|
||||
auth = CustomWizard::Api::Authorization.get_header_authorization_string(api_name)
|
||||
|
||||
connection = Excon.new(
|
||||
URI.parse(URI.encode(endpoint.url)).to_s,
|
||||
:headers => {
|
||||
"Authorization" => auth,
|
||||
"Accept" => "application/json, */*",
|
||||
"Content-Type" => "application/json"
|
||||
}
|
||||
)
|
||||
|
||||
params = {
|
||||
method: endpoint.method
|
||||
}
|
||||
|
||||
if body
|
||||
body = JSON.generate(body)
|
||||
body.delete! '\\'
|
||||
params[:body] = body
|
||||
end
|
||||
|
||||
response = connection.request(params)
|
||||
|
||||
JSON.parse(response.body)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -393,6 +393,17 @@ class CustomWizard::Builder
|
|||
end
|
||||
end
|
||||
|
||||
def send_to_api(user, action, data)
|
||||
api_body = CustomWizard::Builder.fill_placeholders(JSON.generate(JSON.parse(action['api_body'])), user, data)
|
||||
result = CustomWizard::Api::Endpoint.request(action['api'], action['api_endpoint'], api_body)
|
||||
|
||||
if result['error']
|
||||
updater.errors.add(:send_message, result['error'])
|
||||
else
|
||||
## add validation callback
|
||||
end
|
||||
end
|
||||
|
||||
def save_submissions(data, final_step)
|
||||
if final_step
|
||||
data['submitted_at'] = Time.now.iso8601
|
||||
|
|
|
@ -97,6 +97,7 @@ after_initialize do
|
|||
load File.expand_path('../serializers/api/authorization_serializer.rb', __FILE__)
|
||||
load File.expand_path('../serializers/api/basic_api_serializer.rb', __FILE__)
|
||||
load File.expand_path('../serializers/api/endpoint_serializer.rb', __FILE__)
|
||||
load File.expand_path('../serializers/api/basic_endpoint_serializer.rb', __FILE__)
|
||||
|
||||
::UsersController.class_eval do
|
||||
def wizard_path
|
||||
|
|
|
@ -14,7 +14,7 @@ class CustomWizard::ApiSerializer < ApplicationSerializer
|
|||
end
|
||||
|
||||
def endpoints
|
||||
if endpoints = CustomWizard::Api::Endpoint.list
|
||||
if endpoints = CustomWizard::Api::Endpoint.list(object.name)
|
||||
ActiveModel::ArraySerializer.new(
|
||||
endpoints,
|
||||
each_serializer: CustomWizard::Api::EndpointSerializer
|
||||
|
|
|
@ -1,3 +1,14 @@
|
|||
class CustomWizard::BasicApiSerializer < ApplicationSerializer
|
||||
attributes :name, :title
|
||||
attributes :name,
|
||||
:title,
|
||||
:endpoints
|
||||
|
||||
def endpoints
|
||||
if endpoints = CustomWizard::Api::Endpoint.list(object.name)
|
||||
ActiveModel::ArraySerializer.new(
|
||||
endpoints,
|
||||
each_serializer: CustomWizard::Api::BasicEndpointSerializer
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
4
serializers/api/basic_endpoint_serializer.rb
Normale Datei
4
serializers/api/basic_endpoint_serializer.rb
Normale Datei
|
@ -0,0 +1,4 @@
|
|||
class CustomWizard::Api::BasicEndpointSerializer < ApplicationSerializer
|
||||
attributes :id,
|
||||
:name
|
||||
end
|
Laden …
In neuem Issue referenzieren