0
0
Fork 1
Spiegel von https://github.com/paviliondev/discourse-custom-wizard.git synchronisiert 2024-11-09 20:02:54 +01:00

Add custom wizard integration

Dieser Commit ist enthalten in:
Angus McLeod 2019-06-03 17:09:24 +10:00
Ursprung 819c76b3ff
Commit 5ffcee1dde
14 geänderte Dateien mit 175 neuen und 30 gelöschten Zeilen

Datei anzeigen

@ -3,7 +3,8 @@ import { default as computed, observes } from 'ember-addons/ember-computed-decor
const ACTION_TYPES = [ const ACTION_TYPES = [
{ id: 'create_topic', name: 'Create Topic' }, { id: 'create_topic', name: 'Create Topic' },
{ id: 'update_profile', name: 'Update Profile' }, { 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 = [ const PROFILE_FIELDS = [
@ -26,6 +27,8 @@ export default Ember.Component.extend({
createTopic: Ember.computed.equal('action.type', 'create_topic'), createTopic: Ember.computed.equal('action.type', 'create_topic'),
updateProfile: Ember.computed.equal('action.type', 'update_profile'), updateProfile: Ember.computed.equal('action.type', 'update_profile'),
sendMessage: Ember.computed.equal('action.type', 'send_message'), 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'), disableId: Ember.computed.not('action.isNew'),
@computed('currentStepId', 'wizard.save_submissions') @computed('currentStepId', 'wizard.save_submissions')
@ -75,5 +78,21 @@ export default Ember.Component.extend({
toggleCustomCategoryWizardField() { toggleCustomCategoryWizardField() {
const user = this.get('action.custom_category_user_field'); const user = this.get('action.custom_category_user_field');
if (user) this.set('action.custom_category_wizard_field', false); 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;
} }
}); });

Datei anzeigen

@ -78,7 +78,6 @@ export default Ember.Controller.extend({
if (api.title) data['title'] = api.title; if (api.title) data['title'] = api.title;
const originalTitle = this.get('api.originalTitle'); const originalTitle = this.get('api.originalTitle');
console.log(api, originalTitle);
if (api.get('isNew') || (originalTitle && (api.title !== originalTitle))) { if (api.get('isNew') || (originalTitle && (api.title !== originalTitle))) {
refreshList = true; refreshList = true;
} }

Datei anzeigen

@ -33,7 +33,8 @@ export default Discourse.Route.extend({
afterModel(model) { afterModel(model) {
return Ember.RSVP.all([ return Ember.RSVP.all([
this._getFieldTypes(model), 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) { setupController(controller, model) {
const newWizard = this.get('newWizard'); const newWizard = this.get('newWizard');
const steps = model.get('steps') || []; const steps = model.get('steps') || [];

Datei anzeigen

@ -96,7 +96,7 @@
<label>{{i18n 'admin.wizard.action.post_builder.user_fields'}}{{builderUserFields}}</label> <label>{{i18n 'admin.wizard.action.post_builder.user_fields'}}{{builderUserFields}}</label>
<label>{{i18n 'admin.wizard.action.post_builder.wizard_fields'}}{{builderWizardFields}}</label> <label>{{i18n 'admin.wizard.action.post_builder.wizard_fields'}}{{builderWizardFields}}</label>
{{d-editor value=action.post_template {{d-editor value=action.post_template
placeholder='admin.wizard.action.post_builder.placeholder' placeholder='admin.wizard.action.interpolate_fields'
classNames='post-builder-editor'}} classNames='post-builder-editor'}}
</div> </div>
</div> </div>
@ -157,7 +157,7 @@
<label>{{i18n 'admin.wizard.action.post_builder.user_fields'}}{{builderUserFields}}</label> <label>{{i18n 'admin.wizard.action.post_builder.user_fields'}}{{builderUserFields}}</label>
<label>{{i18n 'admin.wizard.action.post_builder.wizard_fields'}}{{builderWizardFields}}</label> <label>{{i18n 'admin.wizard.action.post_builder.wizard_fields'}}{{builderWizardFields}}</label>
{{d-editor value=action.post_template {{d-editor value=action.post_template
placeholder='admin.wizard.action.post_builder.placeholder' placeholder='admin.wizard.action.interpolate_fields'
classNames='post-builder-editor'}} classNames='post-builder-editor'}}
</div> </div>
</div> </div>
@ -204,3 +204,41 @@
allowUserField=true}} allowUserField=true}}
</div> </div>
{{/if}} {{/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}}

Datei anzeigen

@ -117,6 +117,23 @@
margin-top: 5px; margin-top: 5px;
padding: 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 { .wizard-links {

Datei anzeigen

@ -103,6 +103,8 @@ en:
add_fields: "{{type}} Fields" 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." 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" 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: skip_redirect:
label: "Skip Redirect" label: "Skip Redirect"
description: "Don't redirect the user to this {{type}} after the wizard completes" description: "Don't redirect the user to this {{type}} after the wizard completes"
@ -120,12 +122,19 @@ en:
label: "Builder" label: "Builder"
user_fields: "User Fields: " user_fields: "User Fields: "
wizard_fields: "Wizard 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_title: "Custom Title"
custom_category: custom_category:
label: "Custom Category" label: "Custom Category"
wizard_field: "Wizard Field" wizard_field: "Wizard Field"
user_field: "User 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: api:
label: "API" label: "API"
nav_label: 'APIs' nav_label: 'APIs'

Datei anzeigen

@ -106,10 +106,10 @@ class CustomWizard::ApiController < ::ApplicationController
:client_secret, :client_secret,
:username, :username,
:password, :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 @auth_data ||= auth_data
end end

Datei anzeigen

@ -32,6 +32,8 @@ class CustomWizard::Api::Authorization
end end
def self.set(api_name, new_data = {}) def self.set(api_name, new_data = {})
api_name = api_name.underscore
data = self.get(api_name, data_only: true) || {} data = self.get(api_name, data_only: true) || {}
new_data.each do |k, v| new_data.each do |k, v|
@ -44,6 +46,8 @@ class CustomWizard::Api::Authorization
end end
def self.get(api_name, opts = {}) def self.get(api_name, opts = {})
api_name = api_name.underscore
if data = PluginStore.get("custom_wizard_api_#{api_name}", 'authorization') if data = PluginStore.get("custom_wizard_api_#{api_name}", 'authorization')
if opts[:data_only] if opts[:data_only]
data data
@ -60,22 +64,16 @@ class CustomWizard::Api::Authorization
end end
def self.get_header_authorization_string(name) def self.get_header_authorization_string(name)
protocol = authentication_protocol(name) auth = CustomWizard::Api::Authorization.get(name)
raise Discourse::InvalidParameters.new(:name) unless protocol.present? raise Discourse::InvalidParameters.new(:name) unless auth.present?
raise Discourse::InvalidParameters.new(:protocol) unless [BASIC_AUTH, OAUTH2_AUTH].include? protocol
if protocol = BASIC_AUTH if auth.auth_type === "basic"
username = username(name) raise Discourse::InvalidParameters.new(:username) unless auth.username.present?
raise Discourse::InvalidParameters.new(:username) unless username.present? raise Discourse::InvalidParameters.new(:password) unless auth.password.present?
password = password(name) "Basic #{Base64.strict_encode64((auth.username + ":" + auth.password).chomp)}"
raise Discourse::InvalidParameters.new(:password) unless password.present?
authorization_string = (username + ":" + password).chomp
"Basic #{Base64.strict_encode64(authorization_string)}"
else else
# must be OAUTH2 raise Discourse::InvalidParameters.new(auth.access_token) unless auth.access_token.present?
access_token = access_token(name) "Bearer #{auth.access_token}"
raise Discourse::InvalidParameters.new(access_token) unless access_token.present?
"Bearer #{access_token}"
end end
end end

Datei anzeigen

@ -16,8 +16,13 @@ class CustomWizard::Api::Endpoint
end end
def self.set(api_name, new_data) def self.set(api_name, new_data)
data = new_data[:endpoint_id] ? self.get(api_name, new_data[:endpoint_id], data_only: true) : {} if new_data['id']
endpoint_id = new_data[:endpoint_id] || SecureRandom.hex(3) 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| new_data.each do |k, v|
data[k.to_sym] = v data[k.to_sym] = v
@ -32,11 +37,10 @@ class CustomWizard::Api::Endpoint
return nil if !endpoint_id return nil if !endpoint_id
if data = PluginStore.get("custom_wizard_api_#{api_name}", "endpoint_#{endpoint_id}") if data = PluginStore.get("custom_wizard_api_#{api_name}", "endpoint_#{endpoint_id}")
data[:id] = endpoint_id
if opts[:data_only] if opts[:data_only]
data data
else else
data[:id] = endpoint_id
self.new(api_name, data) self.new(api_name, data)
end end
else else
@ -48,8 +52,8 @@ class CustomWizard::Api::Endpoint
PluginStoreRow.where("plugin_name = 'custom_wizard_api_#{api_name}' AND key LIKE 'endpoint_%'").destroy_all PluginStoreRow.where("plugin_name = 'custom_wizard_api_#{api_name}' AND key LIKE 'endpoint_%'").destroy_all
end end
def self.list def self.list(api_name)
PluginStoreRow.where("plugin_name LIKE 'custom_wizard_api_%' AND key LIKE 'endpoint_%'") PluginStoreRow.where("plugin_name LIKE 'custom_wizard_api_#{api_name}' AND key LIKE 'endpoint_%'")
.map do |record| .map do |record|
api_name = record['plugin_name'].sub("custom_wizard_api_", "") api_name = record['plugin_name'].sub("custom_wizard_api_", "")
data = ::JSON.parse(record['value']) data = ::JSON.parse(record['value'])
@ -57,4 +61,32 @@ class CustomWizard::Api::Endpoint
self.new(api_name, data) self.new(api_name, data)
end end
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 end

Datei anzeigen

@ -393,6 +393,17 @@ class CustomWizard::Builder
end end
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) def save_submissions(data, final_step)
if final_step if final_step
data['submitted_at'] = Time.now.iso8601 data['submitted_at'] = Time.now.iso8601

Datei anzeigen

@ -97,6 +97,7 @@ after_initialize do
load File.expand_path('../serializers/api/authorization_serializer.rb', __FILE__) 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/basic_api_serializer.rb', __FILE__)
load File.expand_path('../serializers/api/endpoint_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 ::UsersController.class_eval do
def wizard_path def wizard_path

Datei anzeigen

@ -14,7 +14,7 @@ class CustomWizard::ApiSerializer < ApplicationSerializer
end end
def endpoints def endpoints
if endpoints = CustomWizard::Api::Endpoint.list if endpoints = CustomWizard::Api::Endpoint.list(object.name)
ActiveModel::ArraySerializer.new( ActiveModel::ArraySerializer.new(
endpoints, endpoints,
each_serializer: CustomWizard::Api::EndpointSerializer each_serializer: CustomWizard::Api::EndpointSerializer

Datei anzeigen

@ -1,3 +1,14 @@
class CustomWizard::BasicApiSerializer < ApplicationSerializer 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 end

Datei anzeigen

@ -0,0 +1,4 @@
class CustomWizard::Api::BasicEndpointSerializer < ApplicationSerializer
attributes :id,
:name
end