Add required data && permitted params to steps
Dieser Commit ist enthalten in:
Ursprung
f693a2d25a
Commit
650dc498a4
21 geänderte Dateien mit 274 neuen und 62 gelöschten Zeilen
|
@ -4,7 +4,8 @@ const ACTION_TYPES = [
|
|||
{ id: 'create_topic', name: 'Create Topic' },
|
||||
{ id: 'update_profile', name: 'Update Profile' },
|
||||
{ id: 'send_message', name: 'Send Message' },
|
||||
{ id: 'add_to_group', name: 'Add to Group' }
|
||||
{ id: 'add_to_group', name: 'Add to Group' },
|
||||
{ id: 'route_to', name: 'Route To' }
|
||||
];
|
||||
|
||||
const PROFILE_FIELDS = [
|
||||
|
@ -28,33 +29,9 @@ export default Ember.Component.extend({
|
|||
updateProfile: Ember.computed.equal('action.type', 'update_profile'),
|
||||
sendMessage: Ember.computed.equal('action.type', 'send_message'),
|
||||
addToGroup: Ember.computed.equal('action.type', 'add_to_group'),
|
||||
routeTo: Ember.computed.equal('action.type', 'route_to'),
|
||||
disableId: Ember.computed.not('action.isNew'),
|
||||
|
||||
@computed('currentStepId', 'wizard.save_submissions')
|
||||
availableFields(currentStepId, saveSubmissions) {
|
||||
const allSteps = this.get('wizard.steps');
|
||||
let steps = allSteps;
|
||||
let fields = [];
|
||||
|
||||
if (!saveSubmissions) {
|
||||
steps = [allSteps.findBy('id', currentStepId)];
|
||||
}
|
||||
|
||||
steps.forEach((s) => {
|
||||
if (s.fields && s.fields.length > 0) {
|
||||
let stepFields = s.fields.map((f) => {
|
||||
return Ember.Object.create({
|
||||
id: f.id,
|
||||
label: `${f.id} (${s.id})`
|
||||
});
|
||||
});
|
||||
fields.push(...stepFields);
|
||||
}
|
||||
});
|
||||
|
||||
return fields;
|
||||
},
|
||||
|
||||
@computed('availableFields')
|
||||
builderWizardFields(fields) {
|
||||
return fields.map((f) => ` w{${f.id}}`);
|
||||
|
|
|
@ -5,6 +5,7 @@ export default Ember.Component.extend({
|
|||
classNames: 'custom-input',
|
||||
noneKey: 'admin.wizard.select_field',
|
||||
noneValue: 'admin.wizard.none',
|
||||
connectorNone: 'admin.wizard.none',
|
||||
inputKey: 'admin.wizard.key',
|
||||
customDisabled: Ember.computed.alias('input.user_field'),
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export default Ember.Component.extend({
|
||||
classNames: 'custom-inputs',
|
||||
valuePlaceholder: 'admin.wizard.value',
|
||||
|
||||
actions: {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { observes } from 'ember-addons/ember-computed-decorators';
|
||||
import { observes, default as computed } from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: 'wizard-custom-step',
|
||||
|
@ -14,5 +14,62 @@ export default Ember.Component.extend({
|
|||
currentField: fields.length ? fields[0] : null,
|
||||
currentAction: actions.length ? actions[0] : null
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@computed('availableFields', 'wizard.steps')
|
||||
requiredContent(availableFields, steps) {
|
||||
let content = availableFields;
|
||||
let actions = [];
|
||||
|
||||
steps.forEach(s => {
|
||||
actions.push(...s.actions);
|
||||
});
|
||||
|
||||
actions.forEach(a => {
|
||||
if (a.type === 'route_to' && a.code) {
|
||||
content.push(Ember.Object.create({
|
||||
id: a.code,
|
||||
label: "Route To Code"
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
return content;
|
||||
},
|
||||
|
||||
@computed
|
||||
requiredConnectorContent() {
|
||||
const label = (id) => I18n.t(`admin.wizard.step.required_data.connector.${id}`);
|
||||
return [
|
||||
{
|
||||
id: 'equals',
|
||||
label: label('equals')
|
||||
}
|
||||
];
|
||||
},
|
||||
|
||||
@computed('step.id', 'wizard.save_submissions')
|
||||
availableFields(currentStepId, saveSubmissions) {
|
||||
const allSteps = this.get('wizard.steps');
|
||||
let steps = allSteps;
|
||||
let fields = [];
|
||||
|
||||
if (!saveSubmissions) {
|
||||
steps = [allSteps.findBy('id', currentStepId)];
|
||||
}
|
||||
|
||||
steps.forEach((s) => {
|
||||
if (s.fields && s.fields.length > 0) {
|
||||
let stepFields = s.fields.map((f) => {
|
||||
return Ember.Object.create({
|
||||
id: f.id,
|
||||
label: `${f.id} (${s.id})`
|
||||
});
|
||||
});
|
||||
fields.push(...stepFields);
|
||||
}
|
||||
});
|
||||
|
||||
return fields;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -76,6 +76,8 @@ const CustomWizard = Discourse.Model.extend({
|
|||
if (s.key) step['key'] = s.key;
|
||||
if (s.banner) step['banner'] = s.banner;
|
||||
if (s.raw_description) step['raw_description'] = s.raw_description;
|
||||
if (s.required_data) step['required_data'] = s.required_data;
|
||||
if (s.permitted_params) step['permitted_params'] = s.permitted_params;
|
||||
|
||||
const fields = s.get('fields');
|
||||
if (fields.length) {
|
||||
|
@ -231,6 +233,8 @@ CustomWizard.reopenClass({
|
|||
title: s.title,
|
||||
raw_description: s.raw_description,
|
||||
banner: s.banner,
|
||||
required_data: s.required_data,
|
||||
permitted_params: s.permitted_params,
|
||||
fields,
|
||||
actions,
|
||||
isNew: false
|
||||
|
|
|
@ -226,3 +226,22 @@
|
|||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if routeTo}}
|
||||
<div class="setting">
|
||||
<div class="setting-label">
|
||||
<h3>{{i18n "admin.wizard.action.route_to.url"}}</h3>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{input value=action.url}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting">
|
||||
<div class="setting-label">
|
||||
<h3>{{i18n "admin.wizard.action.route_to.code"}}</h3>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{input value=action.code}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
|
|
@ -6,7 +6,18 @@
|
|||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{d-icon 'arrow-right'}}
|
||||
<div class="connector">
|
||||
{{#if connectorContent}}
|
||||
{{combo-box value=input.connector
|
||||
content=connectorContent
|
||||
nameProperty="label"
|
||||
none=connectorNone}}
|
||||
{{/if}}
|
||||
|
||||
{{#if connectorKey}}
|
||||
{{i18n connectorKey}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="value">
|
||||
{{#if valueContent}}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
{{wizard-custom-input input=input
|
||||
valueContent=valueContent
|
||||
keyContent=keyContent
|
||||
connectorContent=connectorContent
|
||||
connectorKey=connectorKey
|
||||
noneValue=noneValue
|
||||
valuePlaceholder=valuePlaceholder
|
||||
allowCustomField=allowCustomField
|
||||
|
|
|
@ -43,6 +43,30 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting full">
|
||||
<div class="setting-label">
|
||||
<h3>{{i18n 'admin.wizard.step.required_data.label'}}</h3>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{wizard-custom-inputs inputs=step.required_data
|
||||
inputKey='admin.wizard.step.required_data.key'
|
||||
valueContent=requiredContent
|
||||
connectorContent=requiredConnectorContent}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting full">
|
||||
<div class="setting-label">
|
||||
<h3>{{i18n 'admin.wizard.step.permitted_params.label'}}</h3>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{wizard-custom-inputs inputs=step.permitted_params
|
||||
inputKey='admin.wizard.step.permitted_params.key'
|
||||
valuePlaceholder='admin.wizard.step.permitted_params.value'
|
||||
connectorKey='admin.wizard.step.permitted_params.connector'}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{wizard-links type="field" current=currentField items=step.fields}}
|
||||
{{#if currentField}}
|
||||
{{wizard-custom-field field=currentField types=wizard.fieldTypes removeField="removeField"}}
|
||||
|
@ -50,7 +74,10 @@
|
|||
|
||||
{{wizard-links type="action" current=currentAction items=step.actions}}
|
||||
{{#if currentAction}}
|
||||
{{wizard-custom-action action=currentAction wizard=wizard removeAction="removeAction" currentStepId=step.id}}
|
||||
{{wizard-custom-action action=currentAction
|
||||
wizard=wizard
|
||||
removeAction="removeAction"
|
||||
availableFields=availableFields}}
|
||||
{{/if}}
|
||||
|
||||
<label>{{i18n 'admin.wizard.action.available_fields'}}</label>
|
||||
|
|
|
@ -5,7 +5,9 @@ export default StepController.extend({
|
|||
actions: {
|
||||
goNext(response) {
|
||||
const next = this.get('step.next');
|
||||
if (response.refresh_required) {
|
||||
if (response.route_to) {
|
||||
window.location.href = response.route_to;
|
||||
} else if (response.refresh_required) {
|
||||
const id = this.get('wizard.id');
|
||||
window.location.href = getUrl(`/w/${id}/steps/${next}`);
|
||||
} else {
|
||||
|
|
|
@ -24,16 +24,30 @@ CustomWizard.reopenClass({
|
|||
|
||||
finished(result) {
|
||||
let url = "/";
|
||||
if (result.redirect_to) {
|
||||
url = result.redirect_to;
|
||||
if (result.redirect_on_complete) {
|
||||
url = result.redirect_on_complete;
|
||||
}
|
||||
window.location.href = getUrl(url);
|
||||
}
|
||||
});
|
||||
|
||||
export function findCustomWizard(wizardId, opts = {}) {
|
||||
export function findCustomWizard(wizardId, params = {}) {
|
||||
let url = `/w/${wizardId}`;
|
||||
if (opts.reset) url += '?reset=true';
|
||||
|
||||
let paramKeys = Object.keys(params).filter(k => {
|
||||
if (k === 'wizard_id') return false;
|
||||
return !!params[k];
|
||||
});
|
||||
|
||||
if (paramKeys.length) {
|
||||
url += '?';
|
||||
paramKeys.forEach((k,i) => {
|
||||
if (i > 0) {
|
||||
url += '&';
|
||||
}
|
||||
url += `${k}=${params[k]}`;
|
||||
});
|
||||
}
|
||||
|
||||
return ajax({ url, cache: false, dataType: 'json' }).then(result => {
|
||||
const wizard = result.wizard;
|
||||
|
|
|
@ -15,9 +15,19 @@ export default Ember.Route.extend({
|
|||
return model.set("wizardId", this.modelFor('custom').id);
|
||||
},
|
||||
|
||||
setupController(controller, step) {
|
||||
controller.setProperties({
|
||||
step, wizard: this.modelFor('custom')
|
||||
});
|
||||
setupController(controller, model) {
|
||||
let props = {
|
||||
step: model,
|
||||
wizard: this.modelFor('custom')
|
||||
};
|
||||
|
||||
if (!model.permitted) {
|
||||
props['stepMessage'] = {
|
||||
state: 'not-permitted',
|
||||
text: "You're not allowed to view this step."
|
||||
};
|
||||
}
|
||||
|
||||
controller.setProperties(props);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -4,10 +4,12 @@ import { findCustomWizard } from '../models/custom';
|
|||
import { ajax } from 'wizard/lib/ajax';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
beforeModel(transition) {
|
||||
this.set('queryParams', transition.intent.queryParams);
|
||||
},
|
||||
|
||||
model(params) {
|
||||
let opts = {};
|
||||
if (params.reset === 'true') opts['reset'] = true;
|
||||
return findCustomWizard(params.wizard_id, opts);
|
||||
return findCustomWizard(params.wizard_id, this.get('queryParams'));
|
||||
},
|
||||
|
||||
afterModel() {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
<div class="step-message {{stepMessage.state}}">
|
||||
{{stepMessage.text}}
|
||||
</div>
|
||||
{{wizard-step step=step
|
||||
wizard=wizard
|
||||
goNext="goNext"
|
||||
goBack=(action "goBack")
|
||||
finished="finished"
|
||||
showMessage="showMessage"}}
|
||||
{{#if step.permitted}}
|
||||
{{wizard-step step=step
|
||||
wizard=wizard
|
||||
goNext="goNext"
|
||||
goBack=(action "goBack")
|
||||
finished="finished"
|
||||
showMessage="showMessage"}}
|
||||
{{/if}}
|
||||
|
|
|
@ -293,6 +293,13 @@
|
|||
background-color: #e45735;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
&.not-permitted {
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
background-color: #e45735;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.p-list-box {
|
||||
|
|
|
@ -83,8 +83,13 @@
|
|||
|
||||
.setting-value {
|
||||
width: initial;
|
||||
overflow: hidden;
|
||||
float: none;
|
||||
display: flex;
|
||||
|
||||
.custom-input .remove {
|
||||
margin-left: 10px;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,13 +174,18 @@
|
|||
|
||||
.custom-input {
|
||||
display: flex;
|
||||
margin: 5px 0;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.d-icon {
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
input {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input[disabled] {
|
||||
background-color: $primary-low;
|
||||
border-color: #ddd;
|
||||
|
@ -189,6 +199,10 @@
|
|||
margin: 0 auto;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.connector {
|
||||
margin: 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.setting .add-custom-input {
|
||||
|
|
|
@ -71,6 +71,16 @@ en:
|
|||
banner: "Banner"
|
||||
banner_placeholder: "Image url"
|
||||
description: "Description"
|
||||
required_data:
|
||||
label: "Required Data"
|
||||
key: 'Submission key'
|
||||
connector:
|
||||
equals: "Equals"
|
||||
permitted_params:
|
||||
label: "Permitted Params"
|
||||
key: 'Param'
|
||||
value: 'Submission key'
|
||||
connector: "Save as"
|
||||
field:
|
||||
type: "Choose a type"
|
||||
header: "Fields"
|
||||
|
@ -127,6 +137,10 @@ en:
|
|||
group: "Group"
|
||||
group_selection: "Group Selection"
|
||||
custom_group: "Custom Group"
|
||||
route_to:
|
||||
label: "Route To"
|
||||
url: "Url"
|
||||
code: "Code"
|
||||
custom_title: "Custom Title"
|
||||
custom_category:
|
||||
label: "Custom Category"
|
||||
|
|
|
@ -25,7 +25,7 @@ class CustomWizard::WizardController < ::ApplicationController
|
|||
builder_opts[:reset] = params[:reset] if params[:reset]
|
||||
|
||||
if builder.wizard.present?
|
||||
wizard = builder.build(builder_opts)
|
||||
wizard = builder.build(builder_opts, params)
|
||||
render_serialized(wizard, WizardSerializer)
|
||||
else
|
||||
render json: { error: I18n.t('wizard.none') }
|
||||
|
|
|
@ -51,10 +51,10 @@ class CustomWizard::Builder
|
|||
result
|
||||
end
|
||||
|
||||
result.gsub(/w\{(.*?)\}/) { |match| data[$1.to_sym] }
|
||||
result = result.gsub(/w\{(.*?)\}/) { |match| data[$1.to_sym] }
|
||||
end
|
||||
|
||||
def build(build_opts = {})
|
||||
def build(build_opts = {}, params = {})
|
||||
unless (@wizard.completed? && !@wizard.multiple_submissions && !@wizard.user.admin) || !@steps || !@wizard.permitted?
|
||||
|
||||
reset_submissions if build_opts[:reset]
|
||||
|
@ -65,6 +65,36 @@ class CustomWizard::Builder
|
|||
step.description = step_template['description'] if step_template['description']
|
||||
step.banner = step_template['banner'] if step_template['banner']
|
||||
step.key = step_template['key'] if step_template['key']
|
||||
step.permitted = true
|
||||
|
||||
if permitted_params = step_template['permitted_params']
|
||||
permitted_data = {}
|
||||
|
||||
permitted_params.each do |param|
|
||||
key = param['key'].to_sym
|
||||
permitted_data[key] = params[key] if params[key]
|
||||
end
|
||||
|
||||
if permitted_data.present?
|
||||
current_data = @submissions.last || {}
|
||||
save_submissions(current_data.merge(permitted_data), false)
|
||||
end
|
||||
end
|
||||
|
||||
if required_data = step_template['required_data']
|
||||
if !@submissions.last && required_data.length
|
||||
step.permitted = false
|
||||
next
|
||||
end
|
||||
|
||||
required_data.each do |rd|
|
||||
if rd['connector'] === 'equals'
|
||||
step.permitted = @submissions.last[rd['key']] == @submissions.last[rd['value']]
|
||||
end
|
||||
end
|
||||
|
||||
next if !step.permitted
|
||||
end
|
||||
|
||||
if step_template['fields'] && step_template['fields'].length
|
||||
step_template['fields'].each do |field_template|
|
||||
|
@ -120,8 +150,13 @@ class CustomWizard::Builder
|
|||
end
|
||||
|
||||
if updater.errors.empty?
|
||||
redirect_to = data['redirect_to']
|
||||
updater.result = { redirect_to: redirect_to } if redirect_to
|
||||
if route_to = data['route_to']
|
||||
updater.result[:route_to] = route_to
|
||||
end
|
||||
|
||||
if redirect_on_complete = data['redirect_on_complete']
|
||||
updater.result[:redirect_on_complete] = redirect_on_complete
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -345,7 +380,7 @@ class CustomWizard::Builder
|
|||
end
|
||||
|
||||
unless action['skip_redirect']
|
||||
data['redirect_to'] = post.topic.url
|
||||
data['redirect_on_complete'] = post.topic.url
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -374,7 +409,7 @@ class CustomWizard::Builder
|
|||
updater.errors.add(:send_message, creator.errors.full_messages.join(" "))
|
||||
else
|
||||
unless action['skip_redirect']
|
||||
data['redirect_to'] = post.topic.url
|
||||
data['redirect_on_complete'] = post.topic.url
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -410,16 +445,22 @@ class CustomWizard::Builder
|
|||
end
|
||||
|
||||
def add_to_group(user, action, data)
|
||||
puts "GROUP NAME: #{data[action['group_id']]}"
|
||||
if group_id = data[action['group_id']]
|
||||
puts "GROUP: #{Group.find(group_id)}"
|
||||
if group = Group.find(group_id)
|
||||
puts "HERE IS THE GROUP: #{group.inspect}"
|
||||
group.add(user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def route_to(user, action, data)
|
||||
url = CustomWizard::Builder.fill_placeholders(action['url'], user, data)
|
||||
if action['code']
|
||||
data[action['code']] = SecureRandom.hex(8)
|
||||
url += "&#{action['code']}=#{data[action['code']]}"
|
||||
end
|
||||
data['route_to'] = URI.encode(url)
|
||||
end
|
||||
|
||||
def save_submissions(data, final_step)
|
||||
if final_step
|
||||
data['submitted_at'] = Time.now.iso8601
|
||||
|
|
|
@ -9,6 +9,7 @@ class CustomWizard::StepUpdater
|
|||
@step = step
|
||||
@refresh_required = false
|
||||
@fields = fields
|
||||
@result = {}
|
||||
end
|
||||
|
||||
def update
|
||||
|
|
|
@ -60,7 +60,7 @@ end
|
|||
end
|
||||
|
||||
class ::Wizard::Step
|
||||
attr_accessor :title, :description, :key
|
||||
attr_accessor :title, :description, :key, :permitted
|
||||
end
|
||||
|
||||
::WizardSerializer.class_eval do
|
||||
|
@ -134,6 +134,8 @@ end
|
|||
end
|
||||
|
||||
::WizardStepSerializer.class_eval do
|
||||
attributes :permitted
|
||||
|
||||
def title
|
||||
return PrettyText.cook(object.title) if object.title
|
||||
PrettyText.cook(I18n.t("#{object.key || i18n_key}.title", default: ''))
|
||||
|
@ -143,6 +145,10 @@ end
|
|||
return object.description if object.description
|
||||
PrettyText.cook(I18n.t("#{object.key || i18n_key}.description", default: '', base_url: Discourse.base_url))
|
||||
end
|
||||
|
||||
def permitted
|
||||
object.permitted
|
||||
end
|
||||
end
|
||||
|
||||
::WizardFieldSerializer.class_eval do
|
||||
|
|
Laden …
In neuem Issue referenzieren