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

Merge branch 'master' into api_authentication

Dieser Commit ist enthalten in:
Angus McLeod 2019-07-12 11:00:10 +10:00 committet von GitHub
Commit dfc87bccb0
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 4AEE18F83AFDEB23
31 geänderte Dateien mit 412 neuen und 100 gelöschten Zeilen

Datei anzeigen

@ -5,6 +5,8 @@ const ACTION_TYPES = [
{ 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' } { id: 'send_to_api', name: 'Send to API' }
{ id: 'add_to_group', name: 'Add to Group' },
{ id: 'route_to', name: 'Route To' }
]; ];
const PROFILE_FIELDS = [ const PROFILE_FIELDS = [
@ -29,33 +31,10 @@ export default Ember.Component.extend({
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'), sendToApi: Ember.computed.equal('action.type', 'send_to_api'),
apiEmpty: Ember.computed.empty('action.api'), apiEmpty: Ember.computed.empty('action.api'),
addToGroup: Ember.computed.equal('action.type', 'add_to_group'),
routeTo: Ember.computed.equal('action.type', 'route_to'),
disableId: Ember.computed.not('action.isNew'), 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') @computed('availableFields')
builderWizardFields(fields) { builderWizardFields(fields) {
return fields.map((f) => ` w{${f.id}}`); return fields.map((f) => ` w{${f.id}}`);

Datei anzeigen

@ -15,7 +15,13 @@ export default Ember.Component.extend({
@computed() @computed()
presetChoices() { presetChoices() {
return [ return [
{ id: 'categories', name: I18n.t('admin.wizard.field.choices_preset.categories') } {
id: 'categories',
name: I18n.t('admin.wizard.field.choices_preset.categories')
},{
id: 'groups',
name: I18n.t('admin.wizard.field.choices_preset.groups')
}
]; ];
}, },
}); });

Datei anzeigen

@ -5,6 +5,7 @@ export default Ember.Component.extend({
classNames: 'custom-input', classNames: 'custom-input',
noneKey: 'admin.wizard.select_field', noneKey: 'admin.wizard.select_field',
noneValue: 'admin.wizard.none', noneValue: 'admin.wizard.none',
connectorNone: 'admin.wizard.none',
inputKey: 'admin.wizard.key', inputKey: 'admin.wizard.key',
customDisabled: Ember.computed.alias('input.user_field'), customDisabled: Ember.computed.alias('input.user_field'),

Datei anzeigen

@ -1,4 +1,5 @@
export default Ember.Component.extend({ export default Ember.Component.extend({
classNames: 'custom-inputs',
valuePlaceholder: 'admin.wizard.value', valuePlaceholder: 'admin.wizard.value',
actions: { actions: {

Datei anzeigen

@ -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({ export default Ember.Component.extend({
classNames: 'wizard-custom-step', classNames: 'wizard-custom-step',
@ -14,5 +14,62 @@ export default Ember.Component.extend({
currentField: fields.length ? fields[0] : null, currentField: fields.length ? fields[0] : null,
currentAction: actions.length ? actions[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: "code (Route To)"
}));
}
});
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;
},
}); });

Datei anzeigen

@ -77,6 +77,8 @@ const CustomWizard = Discourse.Model.extend({
if (s.key) step['key'] = s.key; if (s.key) step['key'] = s.key;
if (s.banner) step['banner'] = s.banner; if (s.banner) step['banner'] = s.banner;
if (s.raw_description) step['raw_description'] = s.raw_description; 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'); const fields = s.get('fields');
if (fields.length) { if (fields.length) {
@ -242,6 +244,8 @@ CustomWizard.reopenClass({
title: s.title, title: s.title,
raw_description: s.raw_description, raw_description: s.raw_description,
banner: s.banner, banner: s.banner,
required_data: s.required_data,
permitted_params: s.permitted_params,
fields, fields,
actions, actions,
isNew: false isNew: false

Datei anzeigen

@ -242,3 +242,44 @@
</div> </div>
</div> </div>
{{/if}} {{/if}}
{{#if addToGroup}}
<div class="setting">
<div class="setting-label">
<h3>{{i18n "admin.wizard.action.add_to_group.group_selection"}}</h3>
</div>
<div class="setting-value">
{{combo-box value=action.group_id
content=availableFields
nameProperty="label"
none='admin.wizard.select_field'
isDisabled=action.custom_group_enabled}}
<div class="setting-gutter">
{{input type='checkbox' checked=action.custom_group_enabled}}
<span>{{i18n 'admin.wizard.action.add_to_group.custom_group'}}</span>
{{#if action.custom_group_enabled}}
{{input value=action.group_id}}
{{/if}}
</div>
</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}}

Datei anzeigen

@ -6,7 +6,18 @@
{{/if}} {{/if}}
</div> </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"> <div class="value">
{{#if valueContent}} {{#if valueContent}}

Datei anzeigen

@ -2,6 +2,8 @@
{{wizard-custom-input input=input {{wizard-custom-input input=input
valueContent=valueContent valueContent=valueContent
keyContent=keyContent keyContent=keyContent
connectorContent=connectorContent
connectorKey=connectorKey
noneValue=noneValue noneValue=noneValue
valuePlaceholder=valuePlaceholder valuePlaceholder=valuePlaceholder
allowCustomField=allowCustomField allowCustomField=allowCustomField

Datei anzeigen

@ -43,6 +43,30 @@
</div> </div>
</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}} {{wizard-links type="field" current=currentField items=step.fields}}
{{#if currentField}} {{#if currentField}}
{{wizard-custom-field field=currentField types=wizard.fieldTypes removeField="removeField"}} {{wizard-custom-field field=currentField types=wizard.fieldTypes removeField="removeField"}}
@ -50,7 +74,10 @@
{{wizard-links type="action" current=currentAction items=step.actions}} {{wizard-links type="action" current=currentAction items=step.actions}}
{{#if currentAction}} {{#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}} {{/if}}
<label>{{i18n 'admin.wizard.action.available_fields'}}</label> <label>{{i18n 'admin.wizard.action.available_fields'}}</label>

Datei anzeigen

@ -81,8 +81,7 @@ export default Ember.TextField.extend({
includeGroups, includeGroups,
allowedUsers, allowedUsers,
includeMentionableGroups, includeMentionableGroups,
includeMessageableGroups, includeMessageableGroups
group: self.get("group")
}); });
return results; return results;

Datei anzeigen

@ -10,4 +10,4 @@ export default Ember.Component.extend({
CustomWizard.skip(this.get('wizardId')); CustomWizard.skip(this.get('wizardId'));
} }
} }
}) });

Datei anzeigen

@ -5,7 +5,9 @@ export default StepController.extend({
actions: { actions: {
goNext(response) { goNext(response) {
const next = this.get('step.next'); 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'); const id = this.get('wizard.id');
window.location.href = getUrl(`/w/${id}/steps/${next}`); window.location.href = getUrl(`/w/${id}/steps/${next}`);
} else { } else {

Datei anzeigen

@ -1,3 +1,3 @@
export default Ember.Controller.extend({ export default Ember.Controller.extend({
queryParams: ['reset'] queryParams: ['reset']
}) });

Datei anzeigen

@ -71,6 +71,10 @@ export default {
return index === 0 && !required; return index === 0 && !required;
}.property('step.index', 'wizard.required'), }.property('step.index', 'wizard.required'),
cookedTitle: function() {
return cook(this.get('step.title'));
}.property('step.title'),
cookedDescription: function() { cookedDescription: function() {
return cook(this.get('step.description')); return cook(this.get('step.description'));
}.property('step.description'), }.property('step.description'),

Datei anzeigen

@ -24,20 +24,33 @@ CustomWizard.reopenClass({
finished(result) { finished(result) {
let url = "/"; let url = "/";
if (result.redirect_to) { if (result.redirect_on_complete) {
url = result.redirect_to; url = result.redirect_on_complete;
} }
window.location.href = getUrl(url); window.location.href = getUrl(url);
} }
}); });
export function findCustomWizard(wizardId, opts = {}) { export function findCustomWizard(wizardId, params = {}) {
let url = `/w/${wizardId}`; 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 => { return ajax({ url, cache: false, dataType: 'json' }).then(result => {
const wizard = result.wizard; const wizard = result.wizard;
if (!wizard) return null; if (!wizard) return null;
if (!wizard.completed) { if (!wizard.completed) {

Datei anzeigen

@ -16,8 +16,13 @@ export default Ember.Route.extend({
const permitted = model.get('permitted'); const permitted = model.get('permitted');
const minTrust = model.get('min_trust'); const minTrust = model.get('min_trust');
const wizardId = model.get('id'); const wizardId = model.get('id');
const user = model.get('user');
const name = model.get('name');
controller.setProperties({ controller.setProperties({
requiresLogin: !user,
user,
name,
completed, completed,
notPermitted: !permitted, notPermitted: !permitted,
minTrust, minTrust,

Datei anzeigen

@ -15,9 +15,19 @@ export default Ember.Route.extend({
return model.set("wizardId", this.modelFor('custom').id); return model.set("wizardId", this.modelFor('custom').id);
}, },
setupController(controller, step) { setupController(controller, model) {
controller.setProperties({ let props = {
step, wizard: this.modelFor('custom') 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);
} }
}); });

Datei anzeigen

@ -4,10 +4,12 @@ import { findCustomWizard } from '../models/custom';
import { ajax } from 'wizard/lib/ajax'; import { ajax } from 'wizard/lib/ajax';
export default Ember.Route.extend({ export default Ember.Route.extend({
beforeModel(transition) {
this.set('queryParams', transition.intent.queryParams);
},
model(params) { model(params) {
let opts = {}; return findCustomWizard(params.wizard_id, this.get('queryParams'));
if (params.reset == 'true') opts['reset'] = true;
return findCustomWizard(params.wizard_id, opts);
}, },
afterModel() { afterModel() {

Datei anzeigen

@ -1,6 +1,6 @@
<div class='wizard-step-contents'> <div class='wizard-step-contents'>
{{#if step.title}} {{#if step.title}}
<h1 class='wizard-step-title'>{{step.title}}</h1> <h1 class='wizard-step-title'>{{cookedTitle}}</h1>
{{/if}} {{/if}}
{{#if step.description}} {{#if step.description}}

Datei anzeigen

@ -1,10 +1,15 @@
{{#if completed}} {{#if noWizard}}
{{wizard-no-access text=(i18n 'wizard.completed') wizardId=wizardId}} {{wizard-no-access text=(i18n 'wizard.none') wizardId=wizardId}}
{{else}} {{else}}
{{#if notPermitted}} {{#if requiresLogin}}
{{wizard-no-access text=(i18n 'wizard.not_permitted' level=minTrust) wizardId=wizardId}} {{wizard-no-access text=(i18n 'wizard.requires_login' name=name) wizardId=wizardId}}
{{/if}} {{else}}
{{#if noWizard}} {{#if notPermitted}}
{{wizard-no-access text=(i18n 'wizard.none') wizardId=wizardId}} {{wizard-no-access text=(i18n 'wizard.not_permitted' name=name level=minTrust) wizardId=wizardId}}
{{else}}
{{#if completed}}
{{wizard-no-access text=(i18n 'wizard.completed' name=name) wizardId=wizardId}}
{{/if}}
{{/if}}
{{/if}} {{/if}}
{{/if}} {{/if}}

Datei anzeigen

@ -1,9 +1,11 @@
<div class="step-message {{stepMessage.state}}"> <div class="step-message {{stepMessage.state}}">
{{stepMessage.text}} {{stepMessage.text}}
</div> </div>
{{wizard-step step=step {{#if step.permitted}}
wizard=wizard {{wizard-step step=step
goNext="goNext" wizard=wizard
goBack=(action "goBack") goNext="goNext"
finished="finished" goBack=(action "goBack")
showMessage="showMessage"}} finished="finished"
showMessage="showMessage"}}
{{/if}}

Datei anzeigen

@ -157,7 +157,12 @@
//// ////
.d-editor { .d-editor {
max-height: 250px; min-height: 200px;
.d-editor-input {
resize: vertical;
flex: initial;
}
} }
.d-editor-modal.hidden { .d-editor-modal.hidden {

Datei anzeigen

@ -293,6 +293,13 @@
background-color: #e45735; background-color: #e45735;
color: #ffffff; color: #ffffff;
} }
&.not-permitted {
height: 60px;
line-height: 60px;
background-color: #e45735;
color: #ffffff;
}
} }
.p-list-box { .p-list-box {

Datei anzeigen

@ -83,8 +83,13 @@
.setting-value { .setting-value {
width: initial; width: initial;
overflow: hidden;
float: none; float: none;
display: flex;
.custom-input .remove {
margin-left: 10px;
margin-top: 0;
}
} }
} }
@ -186,13 +191,18 @@
.custom-input { .custom-input {
display: flex; display: flex;
margin: 5px 0; align-items: center;
margin-bottom: 10px;
.d-icon { .d-icon {
margin: 0 auto; margin: 0 auto;
text-align: center; text-align: center;
} }
input {
margin: 0;
}
input[disabled] { input[disabled] {
background-color: $primary-low; background-color: $primary-low;
border-color: #ddd; border-color: #ddd;
@ -206,6 +216,10 @@
margin: 0 auto; margin: 0 auto;
align-self: flex-start; align-self: flex-start;
} }
.connector {
margin: 0 10px;
}
} }
.setting .add-custom-input { .setting .add-custom-input {
@ -213,12 +227,12 @@
} }
.admin-contents .wizard-submissions { .admin-contents .wizard-submissions {
padding: 0 20px; width: 100%;
display: inline-block; display: inline-block;
overflow: scroll;
table { table {
margin-top: 0; margin-top: 0;
display: inline-block;
overflow-x: scroll;
} }
} }
@ -450,3 +464,7 @@
padding: 20px; padding: 20px;
margin-bottom: 20px; margin-bottom: 20px;
} }
.wizard-step-contents{
height: unset !important;
}

Datei anzeigen

@ -72,6 +72,16 @@ en:
banner: "Banner" banner: "Banner"
banner_placeholder: "Image url" banner_placeholder: "Image url"
description: "Description" 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: field:
type: "Choose a type" type: "Choose a type"
header: "Fields" header: "Fields"
@ -88,6 +98,7 @@ en:
choices_preset: choices_preset:
label: "Preset" label: "Preset"
categories: "Categories" categories: "Categories"
groups: "Groups"
filter: "Preset Filter" filter: "Preset Filter"
choice: choice:
value: "Value" value: "Value"
@ -123,6 +134,16 @@ 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{}."
add_to_group:
label: "Add to Group"
group: "Group"
group_selection: "Group Selection"
custom_group: "Custom Group"
route_to:
label: "Route To"
url: "Url"
code: "Code"
custom_title: "Custom Title" custom_title: "Custom Title"
custom_category: custom_category:
label: "Custom Category" label: "Custom Category"
@ -237,10 +258,11 @@ en:
filter_placeholder: "Search..." filter_placeholder: "Search..."
wizard: wizard:
completed: "You have completed this wizard." completed: "You have completed the {{name}} wizard."
not_permitted: "You need to be trust level {{level}} or higher to access this wizard." not_permitted: "You need to be trust level {{level}} or higher to access the {{name}} wizard."
none: "There is no wizard here." none: "There is no wizard here."
return_to_site: "Return to {{siteName}}" return_to_site: "Return to {{siteName}}"
requires_login: "You need to be logged in to access the {{name}} wizard."
wizard_composer: wizard_composer:
show_preview: "Preview Post" show_preview: "Preview Post"

Datei anzeigen

@ -2,7 +2,6 @@ class CustomWizard::WizardController < ::ApplicationController
prepend_view_path(Rails.root.join('plugins', 'discourse-custom-wizard', 'views')) prepend_view_path(Rails.root.join('plugins', 'discourse-custom-wizard', 'views'))
layout 'wizard' layout 'wizard'
requires_login
helper_method :wizard_page_title helper_method :wizard_page_title
helper_method :theme_ids helper_method :theme_ids
@ -22,12 +21,11 @@ class CustomWizard::WizardController < ::ApplicationController
respond_to do |format| respond_to do |format|
format.json do format.json do
builder = CustomWizard::Builder.new(current_user, params[:wizard_id].underscore) builder = CustomWizard::Builder.new(current_user, params[:wizard_id].underscore)
builder_opts = {} builder_opts = {}
builder_opts[:reset] = params[:reset] if params[:reset] builder_opts[:reset] = params[:reset] if params[:reset]
if builder.wizard.present? if builder.wizard.present?
wizard = builder.build(builder_opts) wizard = builder.build(builder_opts, params)
render_serialized(wizard, WizardSerializer) render_serialized(wizard, WizardSerializer)
else else
render json: { error: I18n.t('wizard.none') } render json: { error: I18n.t('wizard.none') }
@ -52,19 +50,22 @@ class CustomWizard::WizardController < ::ApplicationController
end end
result = success_json result = success_json
submission = Array.wrap(PluginStore.get("#{wizard_id}_submissions", user.id)).last
if submission && submission['redirect_to'] if user
result.merge!(redirect_to: submission['redirect_to']) submission = Array.wrap(PluginStore.get("#{wizard_id}_submissions", user.id)).last
end
if submission && !wizard.save_submissions if submission && submission['redirect_to']
PluginStore.remove("#{wizard_id}_submissions", user.id) result.merge!(redirect_to: submission['redirect_to'])
end end
if user.custom_fields['redirect_to_wizard'] === wizard_id if submission && !wizard.save_submissions
user.custom_fields.delete('redirect_to_wizard') PluginStore.remove("#{wizard_id}_submissions", user.id)
user.save_custom_fields(true) end
if user.custom_fields['redirect_to_wizard'] === wizard_id
user.custom_fields.delete('redirect_to_wizard')
user.save_custom_fields(true)
end
end end
render json: result render json: result

Datei anzeigen

@ -2,14 +2,16 @@ class CustomWizard::Builder
attr_accessor :wizard, :updater, :submissions attr_accessor :wizard, :updater, :submissions
def initialize(user, wizard_id) def initialize(user=nil, wizard_id)
data = PluginStore.get('custom_wizard', wizard_id) data = PluginStore.get('custom_wizard', wizard_id)
return if data.blank? return if data.blank?
@steps = data['steps'] @steps = data['steps']
@wizard = CustomWizard::Wizard.new(user, data) @wizard = CustomWizard::Wizard.new(user, data)
@submissions = Array.wrap(PluginStore.get("#{wizard_id}_submissions", user.id))
if user
@submissions = Array.wrap(PluginStore.get("#{wizard_id}_submissions", user.id))
end
end end
def self.sorted_handlers def self.sorted_handlers
@ -49,10 +51,10 @@ class CustomWizard::Builder
result result
end end
result.gsub(/w\{(.*?)\}/) { |match| data[$1.to_sym] } result = result.gsub(/w\{(.*?)\}/) { |match| data[$1.to_sym] }
end end
def build(build_opts = {}) def build(build_opts = {}, params = {})
unless (@wizard.completed? && !@wizard.multiple_submissions && !@wizard.user.admin) || !@steps || !@wizard.permitted? unless (@wizard.completed? && !@wizard.multiple_submissions && !@wizard.user.admin) || !@steps || !@wizard.permitted?
reset_submissions if build_opts[:reset] reset_submissions if build_opts[:reset]
@ -63,6 +65,36 @@ class CustomWizard::Builder
step.description = step_template['description'] if step_template['description'] step.description = step_template['description'] if step_template['description']
step.banner = step_template['banner'] if step_template['banner'] step.banner = step_template['banner'] if step_template['banner']
step.key = step_template['key'] if step_template['key'] 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 if step_template['fields'] && step_template['fields'].length
step_template['fields'].each do |field_template| step_template['fields'].each do |field_template|
@ -118,8 +150,13 @@ class CustomWizard::Builder
end end
if updater.errors.empty? if updater.errors.empty?
redirect_to = data['redirect_to'] if route_to = data['route_to']
updater.result = { redirect_to: redirect_to } if redirect_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 end
end end
@ -200,9 +237,14 @@ class CustomWizard::Builder
end end
elsif field_template['choices_preset'] && field_template['choices_preset'].length > 0 elsif field_template['choices_preset'] && field_template['choices_preset'].length > 0
objects = [] objects = []
site = Site.new(Guardian.new(@wizard.user))
if field_template['choices_preset'] === 'categories' if field_template['choices_preset'] === 'categories'
objects = Site.new(Guardian.new(@wizard.user)).categories objects = site.categories
end
if field_template['choices_preset'] === 'groups'
objects = site.groups
end end
if field_template['choices_filters'] && field_template['choices_filters'].length > 0 if field_template['choices_filters'] && field_template['choices_filters'].length > 0
@ -227,7 +269,11 @@ class CustomWizard::Builder
def validate_field(field, updater, step_template) def validate_field(field, updater, step_template)
value = updater.fields[field['id']] value = updater.fields[field['id']]
min_length = field['min_length'] min_length = false
if is_text_type(field)
min_length = field['min_length']
end
if min_length && value.is_a?(String) && value.strip.length < min_length.to_i if min_length && value.is_a?(String) && value.strip.length < min_length.to_i
label = field['label'] || I18n.t("#{field['key']}.label") label = field['label'] || I18n.t("#{field['key']}.label")
@ -246,6 +292,10 @@ class CustomWizard::Builder
end end
end end
def is_text_type(field)
['text', 'textarea'].include? field['type']
end
def standardise_boolean(value) def standardise_boolean(value)
!!HasCustomFields::Helpers::CUSTOM_FIELD_TRUE.include?(value) !!HasCustomFields::Helpers::CUSTOM_FIELD_TRUE.include?(value)
end end
@ -309,6 +359,7 @@ class CustomWizard::Builder
end end
end end
else else
value = [value] if key === 'tags'
params[key.to_sym] = value params[key.to_sym] = value
end end
end end
@ -329,7 +380,7 @@ class CustomWizard::Builder
end end
unless action['skip_redirect'] unless action['skip_redirect']
data['redirect_to'] = post.topic.url data['redirect_on_complete'] = post.topic.url
end end
end end
end end
@ -358,7 +409,7 @@ class CustomWizard::Builder
updater.errors.add(:send_message, creator.errors.full_messages.join(" ")) updater.errors.add(:send_message, creator.errors.full_messages.join(" "))
else else
unless action['skip_redirect'] unless action['skip_redirect']
data['redirect_to'] = post.topic.url data['redirect_on_complete'] = post.topic.url
end end
end end
end end
@ -394,7 +445,6 @@ class CustomWizard::Builder
end end
def send_to_api(user, action, data) def send_to_api(user, action, data)
api_body = nil api_body = nil
if action['api_body'] != "" if action['api_body'] != ""
@ -416,6 +466,23 @@ class CustomWizard::Builder
end end
end end
def add_to_group(user, action, data)
if group_id = data[action['group_id']]
if group = Group.find(group_id)
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) 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

@ -9,6 +9,7 @@ class CustomWizard::StepUpdater
@step = step @step = step
@refresh_required = false @refresh_required = false
@fields = fields @fields = fields
@result = {}
end end
def update def update

Datei anzeigen

@ -18,7 +18,7 @@ class CustomWizard::Wizard
:required, :required,
:prompt_completion :prompt_completion
def initialize(user, attrs = {}) def initialize(user=nil, attrs = {})
@steps = [] @steps = []
@user = user @user = user
@first_step = nil @first_step = nil
@ -54,6 +54,8 @@ class CustomWizard::Wizard
end end
def start def start
return nil if !@user
if unfinished? && last_completed_step = ::UserHistory.where( if unfinished? && last_completed_step = ::UserHistory.where(
acting_user_id: @user.id, acting_user_id: @user.id,
action: ::UserHistory.actions[:custom_wizard_step], action: ::UserHistory.actions[:custom_wizard_step],
@ -76,6 +78,8 @@ class CustomWizard::Wizard
end end
def unfinished? def unfinished?
return nil if !@user
most_recent = ::UserHistory.where( most_recent = ::UserHistory.where(
acting_user_id: @user.id, acting_user_id: @user.id,
action: ::UserHistory.actions[:custom_wizard_step], action: ::UserHistory.actions[:custom_wizard_step],
@ -94,6 +98,8 @@ class CustomWizard::Wizard
end end
def completed? def completed?
return nil if !@user
steps = CustomWizard::Wizard.step_ids(@id) steps = CustomWizard::Wizard.step_ids(@id)
history = ::UserHistory.where( history = ::UserHistory.where(
@ -112,7 +118,7 @@ class CustomWizard::Wizard
end end
def permitted? def permitted?
user.staff? || user.trust_level.to_i >= min_trust.to_i user && (user.staff? || user.trust_level.to_i >= min_trust.to_i)
end end
def reset def reset

Datei anzeigen

@ -60,11 +60,11 @@ end
end end
class ::Wizard::Step class ::Wizard::Step
attr_accessor :title, :description, :key attr_accessor :title, :description, :key, :permitted
end end
::WizardSerializer.class_eval do ::WizardSerializer.class_eval do
attributes :id, :background, :completed, :required, :min_trust, :permitted attributes :id, :name, :background, :completed, :required, :min_trust, :permitted, :user
def id def id
object.id object.id
@ -74,6 +74,10 @@ end
object.respond_to?(:id) object.respond_to?(:id)
end end
def name
object.name
end
def background def background
object.background object.background
end end
@ -123,18 +127,28 @@ end
def include_required? def include_required?
object.respond_to?(:required) object.respond_to?(:required)
end end
def user
object.user
end
end end
::WizardStepSerializer.class_eval do ::WizardStepSerializer.class_eval do
attributes :permitted
def title def title
return object.title if object.title return PrettyText.cook(object.title) if object.title
I18n.t("#{object.key || i18n_key}.title", default: '') PrettyText.cook(I18n.t("#{object.key || i18n_key}.title", default: ''))
end end
def description def description
return object.description if object.description return object.description if object.description
PrettyText.cook(I18n.t("#{object.key || i18n_key}.description", default: '', base_url: Discourse.base_url)) PrettyText.cook(I18n.t("#{object.key || i18n_key}.description", default: '', base_url: Discourse.base_url))
end end
def permitted
object.permitted
end
end end
::WizardFieldSerializer.class_eval do ::WizardFieldSerializer.class_eval do