1
0
Fork 0
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 { default as discourseComputed, observes, on } from 'discourse-common/utils/decorators';
import { equal, empty, or } from "@ember/object/computed"; 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"; import Component from "@ember/component";
export default Component.extend({ export default Component.extend({
classNames: 'wizard-custom-action', 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'), createTopic: equal('action.type', 'create_topic'),
updateProfile: equal('action.type', 'update_profile'), updateProfile: equal('action.type', 'update_profile'),
sendMessage: equal('action.type', 'send_message'), sendMessage: equal('action.type', 'send_message'),
@ -21,6 +21,17 @@ export default Component.extend({
publicTopicFields: or('createTopic', 'openComposer'), publicTopicFields: or('createTopic', 'openComposer'),
showSkipRedirect: or('createTopic', 'sendMessage'), 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') @discourseComputed('wizard.steps')
runAfterContent(steps) { runAfterContent(steps) {
let content = steps.map(function(step) { let content = steps.map(function(step) {

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -93,9 +93,16 @@ export default Controller.extend({
this.send("refreshWizard"); this.send("refreshWizard");
} }
}).catch((result) => { }).catch((result) => {
console.log('catch result: ', result)
this.set('saving', false); 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); 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 EmberObject from '@ember/object';
import { A } from "@ember/array"; import { A } from "@ember/array";
@ -15,8 +15,7 @@ function present(val) {
} }
function mapped(property, type) { function mapped(property, type) {
return mappedProperties[type] && return schema[type].mapped.indexOf(property) > -1;
mappedProperties[type].indexOf(property) > -1;
} }
function castCase(property, value) { function castCase(property, value) {
@ -67,36 +66,54 @@ function buildProperty(json, property, type) {
} }
function buildObject(json, type) { function buildObject(json, type) {
let params = { let props = {
isNew: false isNew: false
} }
Object.keys(json).forEach(prop => { 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) { function buildObjectArray(json, type) {
if (property === 'save_submissions' && value == false) return true; let array = A();
if (property === 'restart_on_revisit' && value == true) return true;
return false; 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) { function buildBasicProperties(json, type, props) {
return advancedProperties.steps[property] && present(value); listProperties(type).forEach((p) => {
props[p] = buildProperty(json, p, type);
if (hasAdvancedProperties(json, type)) {
result.showAdvanced = true;
}
});
return props;
} }
function objectHasAdvanced(params, type) { function hasAdvancedProperties(object, type) {
return Object.keys(params).some(p => { return Object.keys(object).some(p => {
let value = params[p]; return schema[type].advanced.indexOf(p) > -1 && present(object[p]);
let advanced = advancedProperties[type][params.type];
return advanced && advanced.indexOf(p) > -1 && present(value);
}); });
} }
/// to be removed /// to be removed: necessary due to action array being moved from step to wizard
function actionPatch(json) { function actionPatch(json) {
let actions = json.actions || []; let actions = json.actions || [];
@ -117,86 +134,32 @@ function actionPatch(json) {
function buildProperties(json) { function buildProperties(json) {
let props = { let props = {
steps: A(), steps: A()
actions: A()
}; };
if (present(json)) { if (present(json)) {
props.id = json.id;
props.existingId = true; props.existingId = true;
props = buildBasicProperties(json, 'wizard', props);
// 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;
}
});
if (present(json.steps)) { if (present(json.steps)) {
json.steps.forEach((stepJson) => { json.steps.forEach((stepJson) => {
let stepParams = { let stepProps = {
isNew: false isNew: false
}; };
properties.steps.forEach((p) => { stepProps = buildBasicProperties(stepJson, 'step', stepProps);
stepParams[p] = buildProperty(stepJson, p, 'wizard'); stepProps.fields = buildObjectArray(stepJson.fields, 'field');
if (stepHasAdvanced(p, stepJson[p])) { props.steps.pushObject(EmberObject.create(stepProps));
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)
);
}); });
}; };
// to be removed json = actionPatch(json); // to be removed - see above
json = actionPatch(json); props.actions = buildObjectArray(json.actions, 'action');
// 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);
});
}
} else { } else {
props.id = ''; listProperties('wizard').forEach(prop => {
props.name = ''; props[prop] = schema.wizard.basic[prop];
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;
} }
return props; return props;

Datei anzeigen

@ -30,7 +30,7 @@ function camelCase(string) {
}); });
} }
const profileFields = [ const userProperties = [
'name', 'name',
'email', 'email',
'avatar', 'avatar',
@ -45,164 +45,198 @@ const profileFields = [
'trust_level' 'trust_level'
]; ];
const wizardProperties = [ const wizardProperties = {
'id', basic: {
'name', id: null,
'background', name: null,
'save_submissions', background: null,
'multiple_submissions', save_submissions: true,
'after_signup', multiple_submissions: null,
'after_time', after_signup: null,
'after_time_scheduled', after_time: null,
'required', after_time_scheduled: null,
'prompt_completion', required: null,
'restart_on_revisit', prompt_completion: null,
'theme_id', restart_on_revisit: null,
'permitted', theme_id: null,
'steps', permitted: null
'actions' },
]; mapped: [
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: [
'permitted' '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', 'required_data',
'permitted_params' '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', 'prefill',
'content' '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', 'title',
'category', 'category',
'tags', 'tags',
@ -211,67 +245,46 @@ const mappedProperties = {
'recipient', 'recipient',
'profile_updates', 'profile_updates',
'group' 'group'
] ],
} advanced: [
'code',
const defaultProperties = { 'custom_fields',
action: { 'skip_redirect',
run_after: 'wizard_completion' 'required'
],
required: [
'id',
'type'
],
dependent: {
},
objectArrays: {
} }
} }
const advancedFieldTypes = [ if (Discourse.SiteSettings.wizard_api_features) {
'category', actionProperties.types.send_to_api = {
'tag', api: null,
'group', api_endpoint: null,
'dropdown' api_body: null
] }
}
const advancedFieldProperties = [ const schema = {
'prefill', wizard: wizardProperties,
'content' step: stepProperties,
] field: fieldProperties,
action: actionProperties
}
const actionTypes = [ function listProperties(type, objectType = null) {
'create_topic', let properties = Object.keys(schema[type].basic);
'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 = { if (schema[type].types && objectType) {
steps: [ properties = properties.concat(Object.keys(schema[type].types[objectType]));
'required_data', }
'permitted_params'
], return properties;
fields: advancedFieldTypes.reduce(
function(map, type) {
map[type] = advancedFieldProperties;
if (type === 'category') {
map[type].push('property');
}
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');
}
return map;
}, {}
)
} }
export { export {
@ -280,12 +293,7 @@ export {
generateId, generateId,
camelCase, camelCase,
snakeCase, snakeCase,
properties, schema,
wizardProperties, userProperties,
mappedProperties, listProperties
profileFields,
advancedProperties,
actionTypes,
actionTypeProperties,
defaultProperties
}; };

Datei anzeigen

@ -1,13 +1,9 @@
import { ajax } from 'discourse/lib/ajax'; import { ajax } from 'discourse/lib/ajax';
import EmberObject from "@ember/object"; import EmberObject from "@ember/object";
import { buildProperties, present, mapped } from '../lib/wizard-json'; 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"; import { Promise } from "rsvp";
const jsonStrings = ['api_body'];
const required = ['id', 'steps', 'type'];
const dependent = { after_time: 'after_time_scheduled' }
const CustomWizard = EmberObject.extend({ const CustomWizard = EmberObject.extend({
save() { save() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -33,85 +29,94 @@ const CustomWizard = EmberObject.extend({
}, },
buildJson(object, type, result = {}) { buildJson(object, type, result = {}) {
let allowedProperties; let objectType = object.type || null;
if (type === 'actions') { if (schema[type].types) {
if (!object.type) { if (!objectType) {
result.error = { result.error = {
type: 'required', type: 'required',
params: { params: { type, property: 'type' }
type,
property: 'type'
}
} }
return result; 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); let value = object.get(property);
if (required[property] && !value) { result = this.validateValue(property, value, type, result);
result.error = {
type: 'required',
params: { type, property }
}
}
let dependentOn = dependent[property];
if (dependentOn && value && !object[dependentOn]) {
result.error = {
type: 'dependent',
params: {
property,
dependentOn
}
}
}
if (jsonStrings[property]) {
try {
value = JSON.parse(value);
} catch (e) {
result.error = {
type: 'invalid',
params: { type, property }
}
}
}
if (result.error) { if (result.error) {
break; break;
} }
if (properties[property]) { if (mapped(property, type)) {
result[property] = []; value = this.buildMappedJson(value);
}
for (let item of value) { if (value !== undefined && value !== null) {
let itemParams = this.buildJson(item, property); result[property] = value;
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;
}
} }
}; };
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 dependent = schema[type].dependent[property];
if (dependent && value && !object[dependent]) {
result.error = {
type: 'dependent',
params: { property, dependent }
}
}
if (property === 'api_body') {
try {
value = JSON.parse(value);
} catch (e) {
result.error = {
type: 'invalid',
params: { type, property }
}
}
}
return result; return result;
}, },

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -1,6 +1,6 @@
class CustomWizard::Field class CustomWizard::Field
def self.types 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 end
def self.require_assets 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(:permitted_params) {[{"key":"param_key","value":"submission_param_key"}]}
let(:required_data) {[{"key":"nickname","connector":"equals","value":"name"}]} let(:required_data) {[{"key":"nickname","connector":"equals","value":"name"}]}
let(:required_data_message) {"Nickname is required to match your name"} let(:required_data_message) {"Nickname is required to match your name"}
let(:checkbox_field) {{"id":"checkbox","type":"checkbox","label":"Checkbox"}} let(:checkbox_field) {{"id":"checkbox","type":"checkbox","label":"Checkbox"}}
let(:composer_field) {{"id": "composer","label":"Composer","type":"composer"}} let(:composer_field) {{"id": "composer","label":"Composer","type":"composer"}}
let(:tag_field) {{"id": "tag","type": "tag","label": "Tag","limit": "2"}} 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(: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(: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(: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(: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(: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"}} let(:route_to_action) {{"id":"route_to","type":"route_to","url":"https://google.com"}}