0
0
Fork 1
Spiegel von https://github.com/paviliondev/discourse-custom-wizard.git synchronisiert 2024-11-22 09:20:29 +01:00
Dieser Commit ist enthalten in:
Angus McLeod 2020-04-10 17:57:49 +10:00
Ursprung 024ab63006
Commit 3c8dc540c8
18 geänderte Dateien mit 461 neuen und 454 gelöschten Zeilen

Datei anzeigen

@ -1,11 +1,11 @@
import { default as discourseComputed, observes, on } from 'discourse-common/utils/decorators';
import { equal, empty, or } from "@ember/object/computed";
import { actionTypes, generateName, selectKitContent } from '../lib/wizard';
import { generateName, selectKitContent, schema } from '../lib/wizard';
import Component from "@ember/component";
export default Component.extend({
classNames: 'wizard-custom-action',
actionTypes: actionTypes.map(t => ({ id: t, name: generateName(t) })),
actionTypes: Object.keys(schema.action.types).map(t => ({ id: t, name: generateName(t) })),
createTopic: equal('action.type', 'create_topic'),
updateProfile: equal('action.type', 'update_profile'),
sendMessage: equal('action.type', 'send_message'),
@ -21,6 +21,17 @@ export default Component.extend({
publicTopicFields: or('createTopic', 'openComposer'),
showSkipRedirect: or('createTopic', 'sendMessage'),
@observes('action.type')
setupDefaults() {
const defaultProperties = schema.action.types[this.action.type];
Object.keys(defaultProperties).forEach(property => {
if (defaultProperties[property]) {
this.set(`action.${property}`, defaultProperties[property]);
}
});
},
@discourseComputed('wizard.steps')
runAfterContent(steps) {
let content = steps.map(function(step) {

Datei anzeigen

@ -1,6 +1,6 @@
import { default as discourseComputed, observes } from 'discourse-common/utils/decorators';
import { equal, or } from "@ember/object/computed";
import { selectKitContent } from '../lib/wizard';
import { selectKitContent, schema } from '../lib/wizard';
import Component from "@ember/component";
export default Component.extend({
@ -16,24 +16,25 @@ export default Component.extend({
showPrefill: or('isCategory', 'isTag', 'isGroup', 'isDropdown'),
showContent: or('isCategory', 'isTag', 'isGroup', 'isDropdown'),
showLimit: or('isCategory', 'isTag'),
showMinLength: or('isText', 'isTextarea', 'isUrl'),
showMinLength: or('isText', 'isTextarea', 'isUrl', 'isComposer'),
categoryPropertyTypes: selectKitContent(['id', 'slug']),
@observes('isUpload', 'isCategory')
@observes('field.type')
setupDefaults() {
if (this.isUpload && !this.field.file_types) {
this.set('field.file_types', '.jpg,.png');
}
const defaultProperties = schema.field.types[this.field.type];
if (this.isCategory && !this.field.property) {
this.set('field.property', 'id');
Object.keys(defaultProperties).forEach(property => {
if (defaultProperties[property]) {
this.set(`field.${property}`, defaultProperties[property]);
}
});
},
@observes('field.type')
clearMappedProperties() {
this.set('field.content', null);
this.set('field.prefill', null);
clearMapped() {
schema.field.mapped.forEach(property => {
this.set(`field.${property}`, null);
});
},
setupTypeOutput(fieldType, options) {
@ -66,7 +67,6 @@ export default Component.extend({
options.wizardFieldSelection = 'key,value';
options.listSelection += ',assignment';
options.inputTypes = 'association,assignment';
options.singular = true;
options.pairConnector = 'association';
options.keyPlaceholder = 'admin.wizard.key';
options.valuePlaceholder = 'admin.wizard.value';

Datei anzeigen

@ -1,5 +1,5 @@
import { default as discourseComputed, on, observes } from 'discourse-common/utils/decorators';
import { generateName, defaultProperties } from '../lib/wizard';
import { generateName, schema } from '../lib/wizard';
import { notEmpty } from "@ember/object/computed";
import { scheduleOnce, bind } from "@ember/runloop";
import EmberObject from "@ember/object";
@ -7,7 +7,7 @@ import Component from "@ember/component";
import { A } from "@ember/array";
export default Component.extend({
classNameBindings: [':wizard-links', 'type'],
classNameBindings: [':wizard-links', 'itemType'],
items: A(),
anyLinks: notEmpty('links'),
@ -33,10 +33,10 @@ export default Component.extend({
scheduleOnce('afterRender', this, () => this.applySortable());
},
@discourseComputed('type')
header: (type) => `admin.wizard.${type}.header`,
@discourseComputed('itemType')
header: (itemType) => `admin.wizard.${itemType}.header`,
@discourseComputed('current', 'items.@each.id', 'items.@each.type')
@discourseComputed('current', 'items.@each.id', 'items.@each.type', 'items.@each.label', 'items.@each.title')
links(current, items) {
if (!items) return;
@ -65,25 +65,34 @@ export default Component.extend({
});
},
setDefaults(object, params) {
Object.keys(object).forEach(property => {
if (object[property]) {
params[property] = object[property];
}
});
return params;
},
actions: {
add() {
const items = this.items;
const type = this.type;
const newId = `${type}_${items.length + 1}`;
const itemType = this.itemType;
let params = {
id: newId,
id: `${itemType}_${items.length + 1}`,
isNew: true
};
if (type === 'step') {
params.fields = A();
if (schema[itemType].objectArrays) {
Object.keys(schema[itemType].objectArrays).forEach(objectType => {
params[objectArrays[objectType].property] = A();
});
};
if (defaultProperties[type]) {
Object.keys(defaultProperties[type]).forEach(key => {
params[key] = defaultProperties[type][key];
});
params = this.setDefaults(schema[itemType].basic, params);
if (schema[itemType].types) {
params = this.setDefaults(schema[itemType].types[params.type], params);
}
const newItem = EmberObject.create(params);

Datei anzeigen

@ -1,23 +1,23 @@
import { getOwner } from 'discourse-common/lib/get-owner';
import { newInput, selectionTypes } from '../lib/wizard-mapper';
import { default as discourseComputed, observes, on } from 'discourse-common/utils/decorators';
import { gt } from "@ember/object/computed";
import Component from "@ember/component";
import { A } from "@ember/array";
export default Component.extend({
classNames: 'wizard-mapper',
hasInput: gt('inputs.length', 0),
@discourseComputed('options.singular', 'hasInput')
canAdd(singular, hasInput) {
return !singular || !hasInput;
@discourseComputed('inputs.@each.type')
canAdd(inputs) {
return !inputs || inputs.every(i => {
return ['assignment','association'].indexOf(i.type) === -1;
});
},
@discourseComputed('options.@each.inputType')
inputOptions(options) {
let result = {
inputTypes: options.inputTypes || 'conditional,assignment',
inputTypes: options.inputTypes || 'assignment,conditional',
inputConnector: options.inputConnector || 'or',
pairConnector: options.pairConnector || null,
outputConnector: options.outputConnector || null,

Datei anzeigen

@ -1,5 +1,5 @@
import { default as discourseComputed, on } from 'discourse-common/utils/decorators';
import { profileFields } from '../lib/wizard';
import { userProperties } from '../lib/wizard';
import { scheduleOnce } from "@ember/runloop";
import Component from "@ember/component";
@ -34,7 +34,7 @@ export default Component.extend({
@discourseComputed()
userFieldList() {
return profileFields.map((f) => ` u{${f}}`);
return userProperties.map((f) => ` u{${f}}`);
},
@discourseComputed('wizardFields')

Datei anzeigen

@ -93,9 +93,16 @@ export default Controller.extend({
this.send("refreshWizard");
}
}).catch((result) => {
console.log('catch result: ', result)
this.set('saving', false);
this.set('error', I18n.t(`admin.wizard.error.${result.error}`, result.errorParams || {}));
let error = true;
if (result.error) {
let type = result.error.type;
let params = result.error.params || {};
error = I18n.t(`admin.wizard.error.${type}`, params);
}
this.set('error', error);
later(() => this.set('error', null), 10000);
});
},

Datei anzeigen

@ -1,4 +1,4 @@
import { properties, mappedProperties, advancedProperties, camelCase, snakeCase } from '../lib/wizard';
import { schema, listProperties, camelCase, snakeCase } from '../lib/wizard';
import EmberObject from '@ember/object';
import { A } from "@ember/array";
@ -15,8 +15,7 @@ function present(val) {
}
function mapped(property, type) {
return mappedProperties[type] &&
mappedProperties[type].indexOf(property) > -1;
return schema[type].mapped.indexOf(property) > -1;
}
function castCase(property, value) {
@ -67,36 +66,54 @@ function buildProperty(json, property, type) {
}
function buildObject(json, type) {
let params = {
let props = {
isNew: false
}
Object.keys(json).forEach(prop => {
params[prop] = buildProperty(json, prop, type)
props[prop] = buildProperty(json, prop, type)
});
return EmberObject.create(params);
return EmberObject.create(props);
}
function wizardHasAdvanced(property, value) {
if (property === 'save_submissions' && value == false) return true;
if (property === 'restart_on_revisit' && value == true) return true;
return false;
function buildObjectArray(json, type) {
let array = A();
if (present(json)) {
json.forEach((objJson) => {
let object = buildObject(objJson, type);
if (hasAdvancedProperties(object, type)) {
object.set('showAdvanced', true);
}
array.pushObject(object);
});
}
return array;
}
function stepHasAdvanced(property, value) {
return advancedProperties.steps[property] && present(value);
function buildBasicProperties(json, type, props) {
listProperties(type).forEach((p) => {
props[p] = buildProperty(json, p, type);
if (hasAdvancedProperties(json, type)) {
result.showAdvanced = true;
}
});
return props;
}
function objectHasAdvanced(params, type) {
return Object.keys(params).some(p => {
let value = params[p];
let advanced = advancedProperties[type][params.type];
return advanced && advanced.indexOf(p) > -1 && present(value);
function hasAdvancedProperties(object, type) {
return Object.keys(object).some(p => {
return schema[type].advanced.indexOf(p) > -1 && present(object[p]);
});
}
/// to be removed
/// to be removed: necessary due to action array being moved from step to wizard
function actionPatch(json) {
let actions = json.actions || [];
@ -117,86 +134,32 @@ function actionPatch(json) {
function buildProperties(json) {
let props = {
steps: A(),
actions: A()
steps: A()
};
if (present(json)) {
props.id = json.id;
props.existingId = true;
// to fix
properties.wizard
.filter(p => ['steps', 'actions'].indexOf(p) === -1)
.forEach((p) => {
props[p] = buildProperty(json, p, 'wizard');
if (wizardHasAdvanced(p, json[p])) {
props.showAdvanced = true;
}
});
props = buildBasicProperties(json, 'wizard', props);
if (present(json.steps)) {
json.steps.forEach((stepJson) => {
let stepParams = {
let stepProps = {
isNew: false
};
properties.steps.forEach((p) => {
stepParams[p] = buildProperty(stepJson, p, 'wizard');
stepProps = buildBasicProperties(stepJson, 'step', stepProps);
stepProps.fields = buildObjectArray(stepJson.fields, 'field');
if (stepHasAdvanced(p, stepJson[p])) {
stepParams.showAdvanced = true;
}
});
stepParams.fields = A();
if (present(stepJson.fields)) {
stepJson.fields.forEach((f) => {
let params = buildObject(f, 'fields');
if (objectHasAdvanced(params, 'fields')) {
params.showAdvanced = true;
}
stepParams.fields.pushObject(params);
});
}
props.steps.pushObject(
EmberObject.create(stepParams)
);
props.steps.pushObject(EmberObject.create(stepProps));
});
};
// to be removed
json = actionPatch(json);
// to be removed
if (present(json.actions)) {
json.actions.forEach((a) => {
let params = buildObject(a, 'actions');
if (objectHasAdvanced(params, 'actions')) {
params.showAdvanced = true;
}
props.actions.pushObject(params);
});
}
json = actionPatch(json); // to be removed - see above
props.actions = buildObjectArray(json.actions, 'action');
} else {
props.id = '';
props.name = '';
props.background = '';
props.save_submissions = true;
props.multiple_submissions = false;
props.after_signup = false;
props.after_time = false;
props.required = false;
props.prompt_completion = false;
props.restart_on_revisit = false;
props.permitted = null;
listProperties('wizard').forEach(prop => {
props[prop] = schema.wizard.basic[prop];
});
}
return props;

Datei anzeigen

@ -30,7 +30,7 @@ function camelCase(string) {
});
}
const profileFields = [
const userProperties = [
'name',
'email',
'avatar',
@ -45,164 +45,198 @@ const profileFields = [
'trust_level'
];
const wizardProperties = [
'id',
'name',
'background',
'save_submissions',
'multiple_submissions',
'after_signup',
'after_time',
'after_time_scheduled',
'required',
'prompt_completion',
'restart_on_revisit',
'theme_id',
'permitted',
'steps',
'actions'
];
const stepProperties = [
'id',
'title',
'key',
'banner',
'raw_description',
'required_data',
'required_data_message',
'permitted_params',
'fields'
]
const fieldProperties = [
'id',
'label',
'key',
'image',
'description',
'type',
'required',
'min_length',
'file_types',
'property',
'limit',
'prefill',
'content'
]
const actionProperties = [
'id',
'type',
'run_after',
'title',
'post',
'post_builder',
'post_template',
'category',
'tags',
'skip_redirect',
'custom_fields',
'required',
'recipient',
'profile_updates',
'group',
'url',
'code',
'api',
'api_endpoint',
'api_body'
]
const properties = {
wizard: wizardProperties,
steps: stepProperties,
fields: fieldProperties,
actions: actionProperties
}
const actionTypeProperties = {
create_topic: [
'id',
'type',
'run_after',
'title',
'post',
'post_builder',
'post_template',
'category',
'tags',
'skip_redirect',
'custom_fields'
],
send_message: [
'id',
'type',
'run_after',
'title',
'post',
'post_builder',
'post_template',
'skip_redirect',
'custom_fields',
'required',
'recipient'
],
open_composer: [
'id',
'type',
'run_after',
'title',
'post',
'post_builder',
'post_template',
'category',
'tags',
'custom_fields'
],
update_profile: [
'id',
'type',
'run_after',
'profile_updates',
'custom_fields'
],
add_to_group: [
'id',
'type',
'run_after',
'group'
],
route_to: [
'id',
'type',
'run_after',
'url',
'code'
],
send_to_api: [
'id',
'type',
'run_after',
'api',
'api_endpoint',
'api_body'
]
}
const mappedProperties = {
wizard: [
const wizardProperties = {
basic: {
id: null,
name: null,
background: null,
save_submissions: true,
multiple_submissions: null,
after_signup: null,
after_time: null,
after_time_scheduled: null,
required: null,
prompt_completion: null,
restart_on_revisit: null,
theme_id: null,
permitted: null
},
mapped: [
'permitted'
],
steps: [
advanced: [
'restart_on_revisit',
],
required: [
'id',
],
dependent: {
after_time: 'after_time_scheduled'
},
objectArrays: {
step: {
property: 'steps',
required: false
},
action: {
property: 'actions',
required: false
}
}
};
const stepProperties = {
basic: {
id: null,
title: null,
key: null,
banner: null,
raw_description: null,
required_data: null,
required_data_message: null,
permitted_params: null
},
mapped: [
'required_data',
'permitted_params'
],
fields: [
advanced: [
'required_data',
'permitted_params'
],
required: [
'id'
],
dependent: {
},
objectArrays: {
field: {
property: 'fields',
required: false
}
}
}
const fieldProperties = {
basic: {
id: null,
label: null,
image: null,
description: null,
required: null,
key: null,
type: 'text'
},
types: {
text: {
min_length: null
},
textarea: {
min_length: null
},
composer: {
min_length: null
},
number: {
},
url: {
min_length: null
},
'text-only': {
},
'user-selector': {
},
upload: {
file_types: '.jpg,.png'
},
dropdown: {
prefill: null,
content: null
},
tag: {
limit: null,
prefill: null,
content: null
},
category: {
limit: 1,
property: 'id',
prefill: null,
content: null
},
group: {
prefill: null,
content: null
}
},
mapped: [
'prefill',
'content'
],
actions: [
advanced: [
'prefill',
'content',
'property'
],
required: [
'id',
'type'
],
dependent: {
},
objectArrays: {
}
}
const actionProperties = {
basic: {
id: null,
run_after: 'wizard_completion',
type: 'create_topic'
},
types: {
create_topic: {
title: null,
post: null,
post_builder: null,
post_template: null,
category: null,
tags: null,
custom_fields: null,
skip_redirect: null
},
send_message: {
title: null,
post: null,
post_builder: null,
post_template: null,
skip_redirect: null,
custom_fields: null,
required: null,
recipient: null
},
open_composer: {
title: null,
post: null,
post_builder: null,
post_template: null,
category: null,
tags: null,
custom_fields: null
},
update_profile: {
profile_updates: null,
custom_fields: null
},
add_to_group: {
group: null
},
route_to: {
url: null,
code: null
}
},
mapped: [
'title',
'category',
'tags',
@ -211,67 +245,46 @@ const mappedProperties = {
'recipient',
'profile_updates',
'group'
]
}
const defaultProperties = {
action: {
run_after: 'wizard_completion'
}
}
const advancedFieldTypes = [
'category',
'tag',
'group',
'dropdown'
]
const advancedFieldProperties = [
'prefill',
'content'
]
const actionTypes = [
'create_topic',
'update_profile',
'send_message',
'send_to_api',
'add_to_group',
'route_to',
'open_composer'
].filter(function(type) {
return Discourse.SiteSettings.wizard_api_features || type !== 'send_to_api';
});
const advancedProperties = {
steps: [
'required_data',
'permitted_params'
],
fields: advancedFieldTypes.reduce(
function(map, type) {
map[type] = advancedFieldProperties;
if (type === 'category') {
map[type].push('property');
advanced: [
'code',
'custom_fields',
'skip_redirect',
'required'
],
required: [
'id',
'type'
],
dependent: {
},
objectArrays: {
}
return map;
}, {}
),
actions: actionTypes.reduce(
function(map, type) {
if (type === 'route_to') {
map[type] = ['code'];
} else if (['create_topic', 'send_message', 'open_composer', 'update_profile'].indexOf(type) > -1) {
map[type] = ['custom_fields'];
} else if (['create_topic', 'send_message'].indexOf(type) > -1) {
map[type].push('skip_redirect');
} else if (type === 'send_message') {
map[type].push('required');
}
if (Discourse.SiteSettings.wizard_api_features) {
actionProperties.types.send_to_api = {
api: null,
api_endpoint: null,
api_body: null
}
return map;
}, {}
)
}
const schema = {
wizard: wizardProperties,
step: stepProperties,
field: fieldProperties,
action: actionProperties
}
function listProperties(type, objectType = null) {
let properties = Object.keys(schema[type].basic);
if (schema[type].types && objectType) {
properties = properties.concat(Object.keys(schema[type].types[objectType]));
}
return properties;
}
export {
@ -280,12 +293,7 @@ export {
generateId,
camelCase,
snakeCase,
properties,
wizardProperties,
mappedProperties,
profileFields,
advancedProperties,
actionTypes,
actionTypeProperties,
defaultProperties
schema,
userProperties,
listProperties
};

Datei anzeigen

@ -1,13 +1,9 @@
import { ajax } from 'discourse/lib/ajax';
import EmberObject from "@ember/object";
import { buildProperties, present, mapped } from '../lib/wizard-json';
import { properties, actionTypeProperties, camelCase, snakeCase } from '../lib/wizard';
import { schema, listProperties, camelCase, snakeCase } from '../lib/wizard';
import { Promise } from "rsvp";
const jsonStrings = ['api_body'];
const required = ['id', 'steps', 'type'];
const dependent = { after_time: 'after_time_scheduled' }
const CustomWizard = EmberObject.extend({
save() {
return new Promise((resolve, reject) => {
@ -33,47 +29,84 @@ const CustomWizard = EmberObject.extend({
},
buildJson(object, type, result = {}) {
let allowedProperties;
let objectType = object.type || null;
if (type === 'actions') {
if (!object.type) {
if (schema[type].types) {
if (!objectType) {
result.error = {
type: 'required',
params: {
type,
property: 'type'
}
params: { type, property: 'type' }
}
return result;
}
allowedProperties = actionTypeProperties[object.type];
} else {
allowedProperties = properties[type];
}
for (let property of allowedProperties) {
for (let property of listProperties(type, objectType)) {
let value = object.get(property);
if (required[property] && !value) {
result = this.validateValue(property, value, type, result);
if (result.error) {
break;
}
if (mapped(property, type)) {
value = this.buildMappedJson(value);
}
if (value !== undefined && value !== null) {
result[property] = value;
}
};
if (!result.error) {
for (let arrayObjectType of Object.keys(schema[type].objectArrays)) {
let arraySchema = schema[type].objectArrays[arrayObjectType];
let objectArray = object.get(arraySchema.property);
if (arraySchema.required && !present(objectArray)) {
result.error = {
type: 'required',
params: { type, property: arraySchema.property }
}
break;
}
result[arraySchema.property] = [];
for (let item of objectArray) {
let itemProps = this.buildJson(item, arrayObjectType);
if (itemProps.error) {
result.error = itemProps.error;
break;
} else {
result[arraySchema.property].push(itemProps);
}
}
};
}
return result;
},
validateValue(property, value, type, result) {
if (schema[type].required.indexOf(property) > -1 && !value) {
result.error = {
type: 'required',
params: { type, property }
}
}
let dependentOn = dependent[property];
if (dependentOn && value && !object[dependentOn]) {
let dependent = schema[type].dependent[property];
if (dependent && value && !object[dependent]) {
result.error = {
type: 'dependent',
params: {
property,
dependentOn
}
params: { property, dependent }
}
}
if (jsonStrings[property]) {
if (property === 'api_body') {
try {
value = JSON.parse(value);
} catch (e) {
@ -84,34 +117,6 @@ const CustomWizard = EmberObject.extend({
}
}
if (result.error) {
break;
}
if (properties[property]) {
result[property] = [];
for (let item of value) {
let itemParams = this.buildJson(item, property);
if (itemParams.error) {
result.error = r.error;
break;
} else {
result[property].push(itemParams);
}
}
} else {
if (mapped(property, type)) {
value = this.buildMappedJson(value);
}
if (value !== undefined && value !== null) {
result[property] = value;
}
}
};
return result;
},

Datei anzeigen

@ -1,6 +1,6 @@
import CustomWizard from '../models/custom-wizard';
import { ajax } from 'discourse/lib/ajax';
import { selectKitContent, profileFields, generateName } from '../lib/wizard';
import { selectKitContent, userProperties, generateName } from '../lib/wizard';
import DiscourseRoute from "discourse/routes/discourse";
import { all } from "rsvp";
@ -79,7 +79,7 @@ export default DiscourseRoute.extend({
id: `user_field_${f.id}`,
name: f.name
})).concat(
profileFields.map((f) => ({
userProperties.map((f) => ({
id: f,
name: generateName(f)
}))

Datei anzeigen

@ -151,7 +151,7 @@
</div>
{{wizard-links
type="step"
itemType="step"
current=currentStep
items=model.steps}}
@ -164,7 +164,7 @@
{{/if}}
{{wizard-links
type="action"
itemType="action"
current=currentAction
items=model.actions
generateLabels=true}}

Datei anzeigen

@ -112,6 +112,7 @@
inputs=action.tags
options=(hash
tagSelection='output'
outputDefaultSelection='tag'
listSelection='output'
wizardFieldSelection=true
userFieldSelection='key,value'
@ -152,7 +153,6 @@
{{wizard-mapper
inputs=action.profile_updates
options=(hash
singular=true
inputTypes='association'
textSelection='value'
userFieldSelection='key'
@ -263,7 +263,6 @@
{{wizard-mapper
inputs=action.custom_fields
options=(hash
singular=true
inputTypes='association'
wizardFieldSelection='value'
userFieldSelection='value'

Datei anzeigen

@ -49,6 +49,7 @@
inputs=step.required_data
options=(hash
inputTypes='validation'
inputConnector='and'
wizardFieldSelection='value'
userFieldSelection='value'
keyPlaceholder="admin.wizard.submission_key"
@ -98,7 +99,7 @@
{{/if}}
{{/if}}
{{wizard-links type="field" current=currentField items=step.fields}}
{{wizard-links itemType="field" current=currentField items=step.fields}}
{{#if currentField}}
{{wizard-custom-field

Datei anzeigen

@ -31,20 +31,19 @@ body.admin-wizard {
}
.wizard-settings,
.wizard-custom-step {
.wizard-custom-step,
.wizard-custom-action {
@extend .wizard-settings-parent;
@extend .wizard-settings-group;
}
.wizard-basic-details,
.wizard-custom-field,
.wizard-custom-action,
.advanced-settings {
@extend .wizard-settings-group;
}
.wizard-custom-field,
.wizard-custom-action {
.wizard-custom-field {
position: relative;
background: transparent;
background-color: $setting-background;

Datei anzeigen

@ -97,7 +97,7 @@ en:
error:
required: "{{type}} requires {{property}}"
invalid: "{{property}} is invalid"
dependent: "{{property}} is dependent on {{dependentOn}}"
dependent: "{{property}} is dependent on {{dependent}}"
step:
header: "Steps"

Datei anzeigen

@ -106,18 +106,21 @@ class CustomWizard::AdminController < ::ApplicationController
def dependent_properties
{
wizard: {
after_time: 'after_time_scheduled'
},
step: {},
field: {},
action: {}
}
end
def check_required(object, type, error)
object.each do |property, value|
required = required_properties[type].include?(property)
if required && property.blank?
required_properties[type].each do |property|
if object[property].blank?
error = {
type: 'required',
params: { property: property }
params: { type: type, property: property }
}
end
end
@ -125,14 +128,12 @@ class CustomWizard::AdminController < ::ApplicationController
error
end
def check_depdendent(object, error)
object.each do |property, value|
dependent = dependent_properties[property]
if dependent && object[dependent].blank?
def check_depdendent(object, type, error)
dependent_properties[type].each do |property, dependent|
if object[property] && object[dependent].blank?
error = {
type: 'dependent',
params: { dependent: dependent, property: property }
params: { property: property, dependent: dependent }
}
end
end
@ -144,17 +145,18 @@ class CustomWizard::AdminController < ::ApplicationController
error = nil
error = check_required(wizard, :wizard, error)
error = check_depdendent(wizard, error)
error = check_depdendent(wizard, :wizard, error)
if !error
wizard['steps'].each do |step|
error = check_required(step, :step, error)
error = check_depdendent(step, error)
error = check_depdendent(step, :step, error)
break if error.present?
if step['fields'].present?
step['fields'].each do |field|
error = check_required(field, :field, error)
error = check_depdendent(field, error)
error = check_depdendent(field, :field, error)
break if error.present?
end
end
@ -163,10 +165,11 @@ class CustomWizard::AdminController < ::ApplicationController
if wizard['actions'].present?
wizard['actions'].each do |action|
error = check_required(action, :action, error)
error = check_depdendent(action, error)
error = check_depdendent(action, :action, error)
break if error.present?
end
end
end
if error
{ error: error }
@ -209,10 +212,10 @@ class CustomWizard::AdminController < ::ApplicationController
existing_wizard = PluginStore.get('custom_wizard', wizard['id']) || {}
validation = validate_wizard(wizard)
return validation[:error] if validation[:error]
return validation if validation[:error]
after_time_validation = validate_after_time(wizard, existing_wizard)
return after_time_validation[:error] if after_time_validation[:error]
return after_time_validation if after_time_validation[:error]
wizard['steps'].each do |step|
if step['raw_description']
@ -220,7 +223,7 @@ class CustomWizard::AdminController < ::ApplicationController
end
end
result = {
{
wizard: wizard,
existing_wizard: existing_wizard,
new_after_time: after_time_validation[:new]

Datei anzeigen

@ -1,6 +1,6 @@
class CustomWizard::Field
def self.types
@types ||= ['checkbox', 'composer', 'dropdown', 'tag', 'category', 'group', 'image', 'text', 'textarea', 'text-only', 'number', 'upload', 'user-selector', 'url']
@types ||= ['checkbox', 'composer', 'dropdown', 'tag', 'category', 'group', 'text', 'textarea', 'text-only', 'number', 'upload', 'user-selector', 'url']
end
def self.require_assets

Datei anzeigen

@ -18,6 +18,7 @@ describe CustomWizard::Builder do
let(:permitted_params) {[{"key":"param_key","value":"submission_param_key"}]}
let(:required_data) {[{"key":"nickname","connector":"equals","value":"name"}]}
let(:required_data_message) {"Nickname is required to match your name"}
let(:checkbox_field) {{"id":"checkbox","type":"checkbox","label":"Checkbox"}}
let(:composer_field) {{"id": "composer","label":"Composer","type":"composer"}}
let(:tag_field) {{"id": "tag","type": "tag","label": "Tag","limit": "2"}}
@ -28,6 +29,7 @@ describe CustomWizard::Builder do
let(:text_only_field) {{"id": "text_only","type": "text-only","label": "Text only"}}
let(:upload_field) {{"id": "upload","type": "upload","file_types": ".jpg,.png,.pdf","label": "Upload"}}
let(:user_selector_field) {{"id": "user_selector","type": "user-selector","label": "User selector"}}
let(:create_topic_action) {{"id":"create_topic","type":"create_topic","title":"text","post":"textarea"}}
let(:send_message_action) {{"id":"send_message","type":"send_message","title":"text","post":"textarea","username":"angus"}}
let(:route_to_action) {{"id":"route_to","type":"route_to","url":"https://google.com"}}