Spiegel von
https://github.com/paviliondev/discourse-custom-wizard.git
synchronisiert 2024-11-22 09:20:29 +01:00
various
Dieser Commit ist enthalten in:
Ursprung
515f0b3393
Commit
2046648862
18 geänderte Dateien mit 580 neuen und 446 gelöschten Zeilen
|
@ -1,7 +1,5 @@
|
|||
import {
|
||||
default as computed,
|
||||
observes
|
||||
} from 'discourse-common/utils/decorators';
|
||||
import { default as discourseComputed, observes } from 'discourse-common/utils/decorators';
|
||||
import { equal, not, empty } from "@ember/object/computed";
|
||||
import {
|
||||
actionTypes,
|
||||
generateName,
|
||||
|
@ -12,37 +10,37 @@ import {
|
|||
export default Ember.Component.extend({
|
||||
classNames: 'wizard-custom-action',
|
||||
types: actionTypes.map(t => ({ id: t, name: generateName(t) })),
|
||||
createTopic: Ember.computed.equal('action.type', 'create_topic'),
|
||||
updateProfile: Ember.computed.equal('action.type', 'update_profile'),
|
||||
sendMessage: Ember.computed.equal('action.type', 'send_message'),
|
||||
sendToApi: Ember.computed.equal('action.type', 'send_to_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'),
|
||||
createTopic: equal('action.type', 'create_topic'),
|
||||
updateProfile: equal('action.type', 'update_profile'),
|
||||
sendMessage: equal('action.type', 'send_message'),
|
||||
sendToApi: equal('action.type', 'send_to_api'),
|
||||
apiEmpty: empty('action.api'),
|
||||
addToGroup: equal('action.type', 'add_to_group'),
|
||||
routeTo: equal('action.type', 'route_to'),
|
||||
disableId: not('action.isNew'),
|
||||
groupPropertyTypes: generateSelectKitContent(['id', 'name']),
|
||||
|
||||
@computed('action.type')
|
||||
@discourseComputed('action.type')
|
||||
basicTopicFields(actionType) {
|
||||
return ['create_topic', 'send_message', 'open_composer'].indexOf(actionType) > -1;
|
||||
},
|
||||
|
||||
@computed('action.type')
|
||||
@discourseComputed('action.type')
|
||||
publicTopicFields(actionType) {
|
||||
return ['create_topic', 'open_composer'].indexOf(actionType) > -1;
|
||||
},
|
||||
|
||||
@computed('action.type')
|
||||
@discourseComputed('action.type')
|
||||
newTopicFields(actionType) {
|
||||
return ['create_topic', 'send_message'].indexOf(actionType) > -1;
|
||||
},
|
||||
|
||||
@computed('wizardFields')
|
||||
@discourseComputed('wizardFields')
|
||||
categoryFields(fields) {
|
||||
return fields.filter(f => f.type == 'category');
|
||||
},
|
||||
|
||||
@computed('wizardFields')
|
||||
@discourseComputed('wizardFields')
|
||||
tagFields(fields) {
|
||||
return fields.filter(f => f.type == 'tag');
|
||||
},
|
||||
|
@ -59,7 +57,7 @@ export default Ember.Component.extend({
|
|||
this.set('action.custom_category_wizard_field', false);
|
||||
},
|
||||
|
||||
@computed('wizard.apis')
|
||||
@discourseComputed('wizard.apis')
|
||||
availableApis(apis) {
|
||||
return apis.map(a => {
|
||||
return {
|
||||
|
@ -69,7 +67,7 @@ export default Ember.Component.extend({
|
|||
});
|
||||
},
|
||||
|
||||
@computed('wizard.apis', 'action.api')
|
||||
@discourseComputed('wizard.apis', 'action.api')
|
||||
availableEndpoints(apis, api) {
|
||||
if (!api) return [];
|
||||
return apis.find(a => a.name === api).endpoints;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { default as computed, observes, on } from 'discourse-common/utils/decorators';
|
||||
import { default as discourseComputed, observes, on } from 'discourse-common/utils/decorators';
|
||||
import { equal, not, or } from "@ember/object/computed";
|
||||
import { generateSelectKitContent } from '../lib/custom-wizard';
|
||||
|
||||
|
@ -17,10 +17,10 @@ export default Ember.Component.extend({
|
|||
prefillEnabled: or('isCategory', 'isTag', 'isGroup'),
|
||||
contentEnabled: or('isCategory', 'isTag', 'isGroup'),
|
||||
|
||||
@computed('field.type')
|
||||
@discourseComputed('field.type')
|
||||
isInput: (type) => type === 'text' || type === 'textarea' || type === 'url',
|
||||
|
||||
@computed('field.type')
|
||||
@discourseComputed('field.type')
|
||||
isCategoryOrTag: (type) => type === 'tag' || type === 'category',
|
||||
|
||||
@on('didInsertElement')
|
||||
|
@ -31,7 +31,7 @@ export default Ember.Component.extend({
|
|||
}
|
||||
},
|
||||
|
||||
@computed('field.type')
|
||||
@discourseComputed('field.type')
|
||||
prefillOptions(fieldType) {
|
||||
if (!this.prefillEnabled) return {};
|
||||
|
||||
|
@ -48,7 +48,7 @@ export default Ember.Component.extend({
|
|||
return options;
|
||||
},
|
||||
|
||||
@computed('field.type')
|
||||
@discourseComputed('field.type')
|
||||
contentOptions(fieldType) {
|
||||
if (!this.contentEnabled) return {};
|
||||
|
||||
|
|
|
@ -1,27 +1,25 @@
|
|||
import {
|
||||
observes,
|
||||
on,
|
||||
default as computed
|
||||
} from 'discourse-common/utils/decorators';
|
||||
import { observes, on, default as discourseComputed } from 'discourse-common/utils/decorators';
|
||||
import { not } from "@ember/object/computed";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: 'wizard-custom-step',
|
||||
currentField: null,
|
||||
currentAction: null,
|
||||
disableId: Ember.computed.not('step.isNew'),
|
||||
disableId: not('step.isNew'),
|
||||
|
||||
@on('didInsertElement')
|
||||
@observes('step')
|
||||
resetCurrentObjects() {
|
||||
const fields = this.get('step.fields');
|
||||
const actions = this.get('step.actions');
|
||||
const fields = this.step.fields;
|
||||
const actions = this.step.actions;
|
||||
|
||||
this.setProperties({
|
||||
currentField: fields.length ? fields[0] : null,
|
||||
currentAction: actions.length ? actions[0] : null
|
||||
});
|
||||
},
|
||||
|
||||
@computed('wizardFields', 'wizard.steps')
|
||||
@discourseComputed('wizardFields', 'wizard.steps')
|
||||
requiredContent(wizardFields, steps) {
|
||||
let content = wizardFields;
|
||||
let actions = [];
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
import { computed, set } from "@ember/object";
|
||||
import { alias, equal } from "@ember/object/computed";
|
||||
import {
|
||||
newPair,
|
||||
connectorContent,
|
||||
inputTypesContent
|
||||
} from '../lib/custom-wizard';
|
||||
import { newPair, connectorContent, inputTypesContent } from '../lib/mapper';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNameBindings: [':mapper-input', 'type'],
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { connectorContent } from '../lib/custom-wizard';
|
||||
import { connectorContent } from '../lib/mapper';
|
||||
import { gt, or, alias } from "@ember/object/computed";
|
||||
import { computed, observes } from "@ember/object";
|
||||
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
import { alias } from "@ember/object/computed";
|
||||
import { computed } from "@ember/object";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
observes,
|
||||
} from "discourse-common/utils/decorators";
|
||||
import { defaultSelectionType } from '../lib/custom-wizard';
|
||||
import { default as discourseComputed, observes } from "discourse-common/utils/decorators";
|
||||
import { getOwner } from 'discourse-common/lib/get-owner';
|
||||
import { defaultSelectionType } from '../lib/mapper';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: 'mapper-selector',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { getOwner } from 'discourse-common/lib/get-owner';
|
||||
import { on } from 'discourse-common/utils/decorators';
|
||||
import { newInput } from '../lib/custom-wizard';
|
||||
import { newInput } from '../lib/mapper';
|
||||
import { default as discourseComputed } from 'discourse-common/utils/decorators';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
|
|
|
@ -27,6 +27,77 @@ 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'
|
||||
];
|
||||
|
||||
const stepProperties = [
|
||||
'id',
|
||||
'title',
|
||||
'key',
|
||||
'banner',
|
||||
'raw_description',
|
||||
'required_data',
|
||||
'required_data_message',
|
||||
'permitted_params'
|
||||
]
|
||||
|
||||
const fieldProperties = [
|
||||
'id',
|
||||
'label',
|
||||
'key',
|
||||
'image',
|
||||
'description',
|
||||
'type',
|
||||
'required',
|
||||
'min_length',
|
||||
'file_types',
|
||||
'property',
|
||||
'limit',
|
||||
'prefill',
|
||||
'content',
|
||||
]
|
||||
|
||||
const actionProperties = [
|
||||
'id',
|
||||
'type',
|
||||
'title',
|
||||
'post',
|
||||
'post_template',
|
||||
'category',
|
||||
'tags',
|
||||
'skip_redirect',
|
||||
'custom_fields',
|
||||
'required',
|
||||
'recipient',
|
||||
'profile_updates',
|
||||
'group',
|
||||
'url',
|
||||
'code',
|
||||
'api',
|
||||
'api_endpoint',
|
||||
'api_body'
|
||||
]
|
||||
|
||||
const properties = {
|
||||
wizard: wizardProperties,
|
||||
step: stepProperties,
|
||||
field: fieldProperties,
|
||||
action: actionProperties
|
||||
}
|
||||
|
||||
const actionTypes = [
|
||||
'create_topic',
|
||||
'update_profile',
|
||||
|
@ -39,158 +110,12 @@ const actionTypes = [
|
|||
'open_composer'
|
||||
];
|
||||
|
||||
// Inputs
|
||||
|
||||
const selectableInputTypes = [
|
||||
'conditional',
|
||||
'assignment'
|
||||
]
|
||||
|
||||
function defaultInputType(options = {}) {
|
||||
if (!options.hasOutput) return 'pair';
|
||||
if (!options.inputTypes) return selectableInputTypes[0];
|
||||
return options.inputTypes.split(',')[0];
|
||||
}
|
||||
|
||||
function mapInputTypes(types) {
|
||||
return types.map(function(type) {
|
||||
return {
|
||||
id: type,
|
||||
name: I18n.t(`admin.wizard.input.${type}.name`)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function inputTypesContent(options = {}) {
|
||||
return options.inputTypes ?
|
||||
mapInputTypes(options.inputTypes.split(',')) :
|
||||
mapInputTypes(selectableInputTypes);
|
||||
}
|
||||
|
||||
// Connectors
|
||||
|
||||
const connectors = {
|
||||
output: [
|
||||
'then',
|
||||
'set',
|
||||
],
|
||||
pair: [
|
||||
'equal',
|
||||
'greater',
|
||||
'less',
|
||||
'greater_or_equal',
|
||||
'less_or_equal'
|
||||
]
|
||||
}
|
||||
|
||||
function connectorItem(connector) {
|
||||
return {
|
||||
id: connector,
|
||||
name: I18n.t(`admin.wizard.connector.${connector}`)
|
||||
};
|
||||
}
|
||||
|
||||
function defaultConnector(connectorType, inputType, opts = {}) {
|
||||
if (opts[`${connectorType}Connector`]) return opts[`${connectorType}Connector`];
|
||||
if (inputType === 'assignment') return 'set';
|
||||
return connectorType === 'output' ? 'then' : 'equal';
|
||||
}
|
||||
|
||||
function connectorContent(connectorType, inputType, opts) {
|
||||
let connector = opts[`${connectorType}Connector`] || defaultConnector(connectorType, inputType, opts);
|
||||
if (connector) return [connectorItem(connector)];
|
||||
|
||||
return connectors[connectorType].map(function(connector) {
|
||||
return connectorItem(connector);
|
||||
});
|
||||
}
|
||||
|
||||
// Selectors
|
||||
|
||||
const selectionTypes = [
|
||||
'text',
|
||||
'wizard',
|
||||
'user',
|
||||
'group',
|
||||
'category',
|
||||
'tag'
|
||||
]
|
||||
|
||||
function defaultSelectionType(inputType, options = {}) {
|
||||
if (options[`${inputType}DefaultSelection`]) {
|
||||
return options[`${inputType}DefaultSelection`];
|
||||
}
|
||||
|
||||
let type = selectionTypes[0];
|
||||
|
||||
for (let t of selectionTypes) {
|
||||
let inputTypes = options[`${t}Selection`];
|
||||
|
||||
if (inputTypes === true ||
|
||||
((typeof inputTypes === 'string') &&
|
||||
inputTypes.split(',').indexOf(inputType) > -1)) {
|
||||
type = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
// items
|
||||
|
||||
function newPair(inputType, options = {}) {
|
||||
let params = {
|
||||
index: options.index,
|
||||
pairCount: options.pairCount,
|
||||
key: '',
|
||||
key_type: defaultSelectionType('key', options),
|
||||
value: '',
|
||||
value_type: defaultSelectionType('value', options),
|
||||
connector: defaultConnector('pair', inputType, options)
|
||||
}
|
||||
|
||||
return Ember.Object.create(params);
|
||||
}
|
||||
|
||||
function newInput(options = {}) {
|
||||
const inputType = defaultInputType(options);
|
||||
|
||||
let params = {
|
||||
type: inputType,
|
||||
pairs: Ember.A(
|
||||
[
|
||||
newPair(
|
||||
inputType,
|
||||
Object.assign({},
|
||||
options,
|
||||
{ index: 0, pairCount: 1 }
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
if (options.hasOutput) {
|
||||
params['output_type'] = defaultSelectionType('output', options);
|
||||
params['connector'] = defaultConnector('output', inputType, options);
|
||||
}
|
||||
|
||||
return Ember.Object.create(params);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
export {
|
||||
generateSelectKitContent,
|
||||
profileFields,
|
||||
actionTypes,
|
||||
generateName,
|
||||
defaultInputType,
|
||||
defaultSelectionType,
|
||||
connectorContent,
|
||||
inputTypesContent,
|
||||
newInput,
|
||||
newPair,
|
||||
generateId
|
||||
generateId,
|
||||
properties,
|
||||
wizardProperties,
|
||||
profileFields,
|
||||
actionTypes
|
||||
};
|
241
assets/javascripts/discourse/lib/json.js.es6
Normale Datei
241
assets/javascripts/discourse/lib/json.js.es6
Normale Datei
|
@ -0,0 +1,241 @@
|
|||
import { properties } from '../lib/custom-wizard';
|
||||
import { mappedProperties } from '../lib/mapper';
|
||||
import { EmberObject } from '@ember/object';
|
||||
|
||||
function present(val) {
|
||||
return val && val.length;
|
||||
}
|
||||
|
||||
function mapped(property, type) {
|
||||
return mappedProperties[type] &&
|
||||
mappedProperties[type].indexOf(property) > -1;
|
||||
}
|
||||
|
||||
function buildJson(object, type) {
|
||||
let result = {};
|
||||
|
||||
properties[type].forEach((p) => {
|
||||
let value = object.get(p);
|
||||
|
||||
if (value) {
|
||||
result[p] = mapped(p, type) ? buildMappedJson(value) : value;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
function buildMappedJson(inputs) {
|
||||
if (!inputs || !inputs.length) return false;
|
||||
|
||||
let result = [];
|
||||
|
||||
inputs.forEach(inpt => {
|
||||
let input = {
|
||||
type: inpt.type,
|
||||
};
|
||||
|
||||
if (present(inpt.output)) {
|
||||
input.output = inpt.output;
|
||||
input.output_type = inpt.output_type;
|
||||
input.connector = inpt.connector;
|
||||
}
|
||||
|
||||
if (present(inpt.pairs)) {
|
||||
input.pairs = [];
|
||||
|
||||
inpt.pairs.forEach(pr => {
|
||||
if (present(pr.key) && present(pr.value)) {
|
||||
|
||||
let pairParams = {
|
||||
index: pr.index,
|
||||
key: pr.key,
|
||||
key_type: pr.key_type,
|
||||
value: pr.value,
|
||||
value_type: pr.value_type,
|
||||
connector: pr.connector
|
||||
}
|
||||
|
||||
input.pairs.push(pairParams);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ((input.type === 'assignment' && present(input.output)) ||
|
||||
(input.type === 'conditional' && present(input.pairs)) ||
|
||||
(input.type === 'pair' && present(input.pairs))) {
|
||||
|
||||
result.push(input);
|
||||
}
|
||||
});
|
||||
|
||||
if (!result.length) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
buildStepJson(object) {
|
||||
let steps = [];
|
||||
let error = null;
|
||||
|
||||
object.some((s) => {
|
||||
let step = buildJson(s, 'step');
|
||||
let fields = s.fields;
|
||||
|
||||
if (fields.length) {
|
||||
step.fields = [];
|
||||
|
||||
fields.some((f) => {
|
||||
if (!f.type) {
|
||||
error = 'type_required';
|
||||
return;
|
||||
}
|
||||
|
||||
step.fields.push(
|
||||
buildJson(f, 'field')
|
||||
);
|
||||
});
|
||||
|
||||
if (error) return;
|
||||
}
|
||||
|
||||
let actions = s.actions;
|
||||
|
||||
if (actions.length) {
|
||||
step.actions = [];
|
||||
|
||||
actions.some((a) => {
|
||||
if (a.api_body) {
|
||||
try {
|
||||
JSON.parse(a.api_body);
|
||||
} catch (e) {
|
||||
error = 'invalid_api_body';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
step.actions.push(
|
||||
buildJson(a, 'action')
|
||||
);
|
||||
});
|
||||
|
||||
if (error) return;
|
||||
}
|
||||
|
||||
steps.push(step);
|
||||
});
|
||||
|
||||
if (error) {
|
||||
return { error };
|
||||
} else {
|
||||
return { steps };
|
||||
};
|
||||
}
|
||||
|
||||
function buildObject(json, type) {
|
||||
let params = {
|
||||
isNew: false
|
||||
}
|
||||
|
||||
Object.keys(json).forEach(prop => {
|
||||
if (mapped(prop, type)) {
|
||||
let inputs = [];
|
||||
|
||||
json[prop].forEach(inputJson => {
|
||||
let input = {}
|
||||
|
||||
Object.keys(inputJson).forEach(inputKey => {
|
||||
if (inputKey === 'pairs') {
|
||||
let pairs = [];
|
||||
let pairCount = inputJson.pairs.length;
|
||||
|
||||
inputJson.pairs.forEach(pairJson => {
|
||||
let pair = pairJson;
|
||||
pair.pairCount = pairCount;
|
||||
pairs.push(EmberObject.create(pair));
|
||||
});
|
||||
} else {
|
||||
input[inputKey] = inputJson[inputKey];
|
||||
}
|
||||
});
|
||||
|
||||
inputs.push(EmberObject.create(input));
|
||||
});
|
||||
|
||||
params[prop] = Ember.A(inputs);
|
||||
} else {
|
||||
params[prop] = json[prop];
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
EmberObject.create();
|
||||
}
|
||||
|
||||
function buildWizardProperties(json) {
|
||||
let steps = Ember.A();
|
||||
let props = {
|
||||
steps
|
||||
};
|
||||
|
||||
if (present(json)) {
|
||||
props.id = json.id;
|
||||
props.existingId = true;
|
||||
|
||||
properties.wizard.forEach((p) => {
|
||||
props[p] = json[p];
|
||||
});
|
||||
|
||||
if (present(json.steps)) {
|
||||
json.steps.forEach((s) => {
|
||||
let fields = Ember.A();
|
||||
|
||||
if (present(s.fields)) {
|
||||
s.fields.forEach((f) => {
|
||||
fields.pushObject(buildObject(f, 'field'));
|
||||
});
|
||||
}
|
||||
|
||||
let actions = Ember.A();
|
||||
if (s.actions && s.actions.length) {
|
||||
s.actions.forEach((a) => {
|
||||
const actionParams = { isNew: false };
|
||||
const action = EmberObject.create($.extend(a, actionParams));
|
||||
actions.pushObject(action);
|
||||
});
|
||||
}
|
||||
|
||||
steps.pushObject(EmberObject.create({
|
||||
id: s.id,
|
||||
key: s.key,
|
||||
title: s.title,
|
||||
raw_description: s.raw_description,
|
||||
banner: s.banner,
|
||||
required_data: s.required_data,
|
||||
required_data_message: s.required_data_message,
|
||||
permitted_params: s.permitted_params,
|
||||
fields,
|
||||
actions,
|
||||
isNew: false
|
||||
}));
|
||||
});
|
||||
};
|
||||
} 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;
|
||||
props['steps'] = Ember.A();
|
||||
}
|
||||
|
||||
return props;
|
||||
}
|
174
assets/javascripts/discourse/lib/mapper.js.es6
Normale Datei
174
assets/javascripts/discourse/lib/mapper.js.es6
Normale Datei
|
@ -0,0 +1,174 @@
|
|||
const mappedProperties = {
|
||||
wizard: [
|
||||
'permitted'
|
||||
],
|
||||
step: [
|
||||
'required_data',
|
||||
'permitted_params'
|
||||
],
|
||||
field: [
|
||||
'choices',
|
||||
'prefill',
|
||||
'content'
|
||||
],
|
||||
action: [
|
||||
'title',
|
||||
'category',
|
||||
'tags',
|
||||
'custom_fields',
|
||||
'required',
|
||||
'recipient',
|
||||
'profile_updates',
|
||||
'group'
|
||||
]
|
||||
}
|
||||
|
||||
// Inputs
|
||||
|
||||
const selectableInputTypes = [
|
||||
'conditional',
|
||||
'assignment'
|
||||
]
|
||||
|
||||
function defaultInputType(options = {}) {
|
||||
if (!options.hasOutput) return 'pair';
|
||||
if (!options.inputTypes) return selectableInputTypes[0];
|
||||
return options.inputTypes.split(',')[0];
|
||||
}
|
||||
|
||||
function mapInputTypes(types) {
|
||||
return types.map(function(type) {
|
||||
return {
|
||||
id: type,
|
||||
name: I18n.t(`admin.wizard.input.${type}.name`)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function inputTypesContent(options = {}) {
|
||||
return options.inputTypes ?
|
||||
mapInputTypes(options.inputTypes.split(',')) :
|
||||
mapInputTypes(selectableInputTypes);
|
||||
}
|
||||
|
||||
// Connectors
|
||||
|
||||
const connectors = {
|
||||
output: [
|
||||
'then',
|
||||
'set',
|
||||
],
|
||||
pair: [
|
||||
'equal',
|
||||
'greater',
|
||||
'less',
|
||||
'greater_or_equal',
|
||||
'less_or_equal'
|
||||
]
|
||||
}
|
||||
|
||||
function connectorItem(connector) {
|
||||
return {
|
||||
id: connector,
|
||||
name: I18n.t(`admin.wizard.connector.${connector}`)
|
||||
};
|
||||
}
|
||||
|
||||
function defaultConnector(connectorType, inputType, opts = {}) {
|
||||
if (opts[`${connectorType}Connector`]) return opts[`${connectorType}Connector`];
|
||||
if (inputType === 'assignment') return 'set';
|
||||
return connectorType === 'output' ? 'then' : 'equal';
|
||||
}
|
||||
|
||||
function connectorContent(connectorType, inputType, opts) {
|
||||
let connector = opts[`${connectorType}Connector`] || defaultConnector(connectorType, inputType, opts);
|
||||
if (connector) return [connectorItem(connector)];
|
||||
|
||||
return connectors[connectorType].map(function(connector) {
|
||||
return connectorItem(connector);
|
||||
});
|
||||
}
|
||||
|
||||
// Selectors
|
||||
|
||||
const selectionTypes = [
|
||||
'text',
|
||||
'wizard',
|
||||
'user',
|
||||
'group',
|
||||
'category',
|
||||
'tag'
|
||||
]
|
||||
|
||||
function defaultSelectionType(inputType, options = {}) {
|
||||
if (options[`${inputType}DefaultSelection`]) {
|
||||
return options[`${inputType}DefaultSelection`];
|
||||
}
|
||||
|
||||
let type = selectionTypes[0];
|
||||
|
||||
for (let t of selectionTypes) {
|
||||
let inputTypes = options[`${t}Selection`];
|
||||
|
||||
if (inputTypes === true ||
|
||||
((typeof inputTypes === 'string') &&
|
||||
inputTypes.split(',').indexOf(inputType) > -1)) {
|
||||
type = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
// items
|
||||
|
||||
function newPair(inputType, options = {}) {
|
||||
let params = {
|
||||
index: options.index,
|
||||
pairCount: options.pairCount,
|
||||
key: '',
|
||||
key_type: defaultSelectionType('key', options),
|
||||
value: '',
|
||||
value_type: defaultSelectionType('value', options),
|
||||
connector: defaultConnector('pair', inputType, options)
|
||||
}
|
||||
|
||||
return Ember.Object.create(params);
|
||||
}
|
||||
|
||||
function newInput(options = {}) {
|
||||
const inputType = defaultInputType(options);
|
||||
|
||||
let params = {
|
||||
type: inputType,
|
||||
pairs: Ember.A(
|
||||
[
|
||||
newPair(
|
||||
inputType,
|
||||
Object.assign({},
|
||||
options,
|
||||
{ index: 0, pairCount: 1 }
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
if (options.hasOutput) {
|
||||
params['output_type'] = defaultSelectionType('output', options);
|
||||
params['connector'] = defaultConnector('output', inputType, options);
|
||||
}
|
||||
|
||||
return Ember.Object.create(params);
|
||||
}
|
||||
|
||||
export {
|
||||
mappedProperties,
|
||||
defaultInputType,
|
||||
defaultSelectionType,
|
||||
connectorContent,
|
||||
inputTypesContent,
|
||||
newInput,
|
||||
newPair,
|
||||
}
|
|
@ -1,57 +1,38 @@
|
|||
import { ajax } from 'discourse/lib/ajax';
|
||||
import EmberObject from "@ember/object";
|
||||
|
||||
const wizardProperties = [
|
||||
'name',
|
||||
'background',
|
||||
'save_submissions',
|
||||
'multiple_submissions',
|
||||
'after_signup',
|
||||
'after_time',
|
||||
'after_time_scheduled',
|
||||
'required',
|
||||
'prompt_completion',
|
||||
'restart_on_revisit',
|
||||
'theme_id',
|
||||
'permitted'
|
||||
];
|
||||
import { buildStepJson, buildJson, buildWizardProperties } from '../lib/json';
|
||||
|
||||
const CustomWizard = EmberObject.extend({
|
||||
save() {
|
||||
return new Ember.RSVP.Promise((resolve, reject) => {
|
||||
|
||||
const id = this.get('id');
|
||||
if (!id || !id.underscore()) return reject({ error: 'id_required' });
|
||||
|
||||
let wizard = { id: id.underscore() };
|
||||
|
||||
wizardProperties.forEach((p) => {
|
||||
const value = this.get(p);
|
||||
if (value) wizard[p] = value;
|
||||
});
|
||||
|
||||
if (wizard['after_time'] && !wizard['after_time_scheduled']) {
|
||||
return reject({ error: 'after_time_need_time' });
|
||||
let wizardJson = buildJson(this, 'wizard');
|
||||
|
||||
if (wizardJson.after_time && !wizardJson.after_time_scheduled) {
|
||||
reject({
|
||||
error: 'after_time_need_time'
|
||||
});
|
||||
};
|
||||
|
||||
const steps = this.get('steps');
|
||||
if (steps.length > 0) {
|
||||
const stepsResult = this.buildSteps(steps);
|
||||
if (stepsResult.error) {
|
||||
reject({ error: stepsResult.error });
|
||||
|
||||
if (this.steps.length > 0) {
|
||||
let stepsResult = buildStepJson(this.steps);
|
||||
|
||||
if (stepsResult.error ||
|
||||
!stepsResult.steps ||
|
||||
stepsResult.steps.length < 1) {
|
||||
|
||||
reject({
|
||||
error: stepsResult.error || 'steps_required'
|
||||
});
|
||||
} else {
|
||||
wizard['steps'] = stepsResult.steps;
|
||||
wizardJson.steps = stepsResult.steps;
|
||||
}
|
||||
}
|
||||
|
||||
if (steps.length < 1 || !wizard['steps'] || wizard['steps'].length < 1) {
|
||||
return reject({ error: 'steps_required' });
|
||||
}
|
||||
|
||||
|
||||
ajax("/admin/wizards/custom/save", {
|
||||
type: 'PUT',
|
||||
data: {
|
||||
wizard: JSON.stringify(wizard)
|
||||
wizard: JSON.stringify(wizardJson)
|
||||
}
|
||||
}).then((result) => {
|
||||
if (result.error) {
|
||||
|
@ -63,107 +44,6 @@ const CustomWizard = EmberObject.extend({
|
|||
});
|
||||
},
|
||||
|
||||
buildSteps(stepsObj) {
|
||||
let steps = [];
|
||||
let error = null;
|
||||
|
||||
stepsObj.some((s) => {
|
||||
if (!s.id || !s.id.underscore()) {
|
||||
error = 'id_required';
|
||||
return;
|
||||
};
|
||||
|
||||
let step = { id: s.id.underscore() };
|
||||
|
||||
if (s.title) step['title'] = s.title;
|
||||
if (s.key) step['key'] = s.key;
|
||||
if (s.banner) step['banner'] = s.banner;
|
||||
if (s.raw_description) step['raw_description'] = s.raw_description;
|
||||
if (s.required_data) step['required_data'] = s.required_data;
|
||||
if (s.required_data_message) step['required_data_message'] = s.required_data_message;
|
||||
if (s.permitted_params) step['permitted_params'] = s.permitted_params;
|
||||
|
||||
const fields = s.get('fields');
|
||||
if (fields.length) {
|
||||
step['fields'] = [];
|
||||
|
||||
fields.some((f) => {
|
||||
let id = f.id;
|
||||
|
||||
if (!id || !id.underscore()) {
|
||||
error = 'id_required';
|
||||
return;
|
||||
}
|
||||
|
||||
if (!f.type) {
|
||||
error = 'type_required';
|
||||
return;
|
||||
}
|
||||
|
||||
f.set('id', id.underscore());
|
||||
|
||||
if (f.label === '') delete f.label;
|
||||
if (f.description === '') delete f.description;
|
||||
|
||||
if (f.type === 'dropdown') {
|
||||
const choices = f.choices;
|
||||
if ((!choices || choices.length < 1) && !f.choices_key) {
|
||||
error = 'field.need_choices';
|
||||
return;
|
||||
}
|
||||
|
||||
if (f.dropdown_none === '') delete f.dropdown_none;
|
||||
}
|
||||
|
||||
delete f.isNew;
|
||||
|
||||
step['fields'].push(f);
|
||||
});
|
||||
|
||||
if (error) return;
|
||||
}
|
||||
|
||||
const actions = s.actions;
|
||||
if (actions.length) {
|
||||
step['actions'] = [];
|
||||
|
||||
actions.some((a) => {
|
||||
let id = a.get('id');
|
||||
if (!id || !id.underscore()) {
|
||||
error = 'id_required';
|
||||
return;
|
||||
}
|
||||
//check if api_body is valid JSON
|
||||
let api_body = a.get('api_body');
|
||||
if (api_body) {
|
||||
try {
|
||||
JSON.parse(api_body);
|
||||
} catch (e) {
|
||||
error = 'invalid_api_body';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
a.set('id', id.underscore());
|
||||
|
||||
delete a.isNew;
|
||||
|
||||
step['actions'].push(a);
|
||||
});
|
||||
|
||||
if (error) return;
|
||||
}
|
||||
|
||||
steps.push(step);
|
||||
});
|
||||
|
||||
if (error) {
|
||||
return { error };
|
||||
} else {
|
||||
return { steps };
|
||||
};
|
||||
},
|
||||
|
||||
remove() {
|
||||
return ajax("/admin/wizards/custom/remove", {
|
||||
type: 'DELETE',
|
||||
|
@ -191,87 +71,11 @@ CustomWizard.reopenClass({
|
|||
});
|
||||
},
|
||||
|
||||
create(w) {
|
||||
create(wizardJson = {}) {
|
||||
const wizard = this._super.apply(this);
|
||||
let steps = Ember.A();
|
||||
let props = { steps };
|
||||
|
||||
if (w) {
|
||||
props['id'] = w.id;
|
||||
props['existingId'] = true;
|
||||
|
||||
wizardProperties.forEach((p) => {
|
||||
props[p] = w[p];
|
||||
});
|
||||
|
||||
if (w.steps && w.steps.length) {
|
||||
w.steps.forEach((s) => {
|
||||
// clean empty strings
|
||||
Object.keys(s).forEach((key) => (s[key] === '') && delete s[key]);
|
||||
|
||||
let fields = Ember.A();
|
||||
|
||||
if (s.fields && s.fields.length) {
|
||||
s.fields.forEach((f) => {
|
||||
Object.keys(f).forEach((key) => (f[key] === '') && delete f[key]);
|
||||
|
||||
const fieldParams = { isNew: false };
|
||||
let field = Ember.Object.create($.extend(f, fieldParams));
|
||||
|
||||
if (f.choices) {
|
||||
let choices = Ember.A();
|
||||
|
||||
f.choices.forEach((c) => {
|
||||
choices.pushObject(Ember.Object.create(c));
|
||||
});
|
||||
|
||||
field.set('choices', choices);
|
||||
}
|
||||
|
||||
fields.pushObject(field);
|
||||
});
|
||||
}
|
||||
|
||||
let actions = Ember.A();
|
||||
if (s.actions && s.actions.length) {
|
||||
s.actions.forEach((a) => {
|
||||
const actionParams = { isNew: false };
|
||||
const action = Ember.Object.create($.extend(a, actionParams));
|
||||
actions.pushObject(action);
|
||||
});
|
||||
}
|
||||
|
||||
steps.pushObject(Ember.Object.create({
|
||||
id: s.id,
|
||||
key: s.key,
|
||||
title: s.title,
|
||||
raw_description: s.raw_description,
|
||||
banner: s.banner,
|
||||
required_data: s.required_data,
|
||||
required_data_message: s.required_data_message,
|
||||
permitted_params: s.permitted_params,
|
||||
fields,
|
||||
actions,
|
||||
isNew: false
|
||||
}));
|
||||
});
|
||||
};
|
||||
} 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;
|
||||
props['steps'] = Ember.A();
|
||||
};
|
||||
|
||||
wizard.setProperties(props);
|
||||
|
||||
wizard.setProperties(buildWizardProperties(wizardJson));
|
||||
|
||||
return wizard;
|
||||
}
|
||||
|
|
|
@ -80,9 +80,11 @@
|
|||
inputs=action.category
|
||||
options=(hash
|
||||
hasOutput=true
|
||||
categorySelection='output'
|
||||
textSelection='key,value'
|
||||
wizardSelection=true
|
||||
userSelection='key,value'
|
||||
categorySelection='output'
|
||||
outputDefaultSelection='category'
|
||||
)}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,41 +1,41 @@
|
|||
<div class="type-selector">
|
||||
{{#if textEnabled}}
|
||||
{{input-type-toggle
|
||||
{{wizard-mapper-selector-type
|
||||
activeType=activeType
|
||||
type='text'
|
||||
toggle=(action 'toggleType')}}
|
||||
{{/if}}
|
||||
|
||||
{{#if wizardEnabled}}
|
||||
{{input-type-toggle
|
||||
{{wizard-mapper-selector-type
|
||||
activeType=activeType
|
||||
type='wizard'
|
||||
toggle=(action 'toggleType')}}
|
||||
{{/if}}
|
||||
|
||||
{{#if userEnabled}}
|
||||
{{input-type-toggle
|
||||
{{wizard-mapper-selector-type
|
||||
activeType=activeType
|
||||
type='user'
|
||||
toggle=(action 'toggleType')}}
|
||||
{{/if}}
|
||||
|
||||
{{#if categoryEnabled}}
|
||||
{{input-type-toggle
|
||||
{{wizard-mapper-selector-type
|
||||
activeType=activeType
|
||||
type='category'
|
||||
toggle=(action 'toggleType')}}
|
||||
{{/if}}
|
||||
|
||||
{{#if tagEnabled}}
|
||||
{{input-type-toggle
|
||||
{{wizard-mapper-selector-type
|
||||
activeType=activeType
|
||||
type='tag'
|
||||
toggle=(action 'toggleType')}}
|
||||
{{/if}}
|
||||
|
||||
{{#if groupEnabled}}
|
||||
{{input-type-toggle
|
||||
{{wizard-mapper-selector-type
|
||||
activeType=activeType
|
||||
type='group'
|
||||
toggle=(action 'toggleType')}}
|
||||
|
|
|
@ -24,7 +24,6 @@ export default Ember.Route.extend({
|
|||
name,
|
||||
completed,
|
||||
notPermitted: !permitted,
|
||||
minTrust,
|
||||
wizardId
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -31,7 +31,7 @@ class CustomWizard::Wizard
|
|||
@required = false
|
||||
@needs_categories = false
|
||||
@needs_groups = false
|
||||
|
||||
|
||||
attrs.each do |key, value|
|
||||
setter = "#{key}="
|
||||
send(setter, value) if respond_to?(setter.to_sym, false)
|
||||
|
@ -150,40 +150,38 @@ class CustomWizard::Wizard
|
|||
@groups ||= ::Site.new(Guardian.new(@user)).groups
|
||||
end
|
||||
|
||||
def self.templates(filter = nil)
|
||||
rows = [*PluginStoreRow.where(plugin_name: 'custom_wizard')]
|
||||
rows = rows.select { |r| r.value[filter] } if filter
|
||||
rows
|
||||
def self.filter_records(filter)
|
||||
PluginStoreRow.where("
|
||||
plugin_name = 'custom_wizard' AND
|
||||
(value::json ->> '#{filter}')::boolean IS TRUE
|
||||
")
|
||||
end
|
||||
|
||||
def self.after_signup(user)
|
||||
if (temps = templates('after_signup')).any?
|
||||
wizard = nil
|
||||
if (records = filter_records('after_signup')).any?
|
||||
result = false
|
||||
|
||||
temps
|
||||
.sort_by { |t| template.value['permitted'].present? }
|
||||
.each do |template|
|
||||
wizard = CustomWizard::Wizard.new(user, template)
|
||||
|
||||
records
|
||||
.sort_by { |record| record.value['permitted'].present? }
|
||||
.each do |record|
|
||||
wizard = CustomWizard::Wizard.new(user, JSON.parse(record.value))
|
||||
|
||||
if wizard.permitted?
|
||||
wizard = wizard
|
||||
result = wizard
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
wizard
|
||||
result
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def self.prompt_completion(user)
|
||||
if (temps = templates('prompt_completion')).any?
|
||||
temps.reduce([]) do |result, w|
|
||||
data = ::JSON.parse(w.value)
|
||||
id = data['id']
|
||||
name = data['name']
|
||||
wizard = CustomWizard::Wizard.new(user, id: id, name: name)
|
||||
if (records = filter_records('prompt_completion')).any?
|
||||
records.reduce([]) do |result, record|
|
||||
wizard = CustomWizard::Wizard.new(user, ::JSON.parse(record.value))
|
||||
result.push(id: id, name: name) if !wizard.completed?
|
||||
result
|
||||
end
|
||||
|
@ -193,8 +191,8 @@ class CustomWizard::Wizard
|
|||
end
|
||||
|
||||
def self.restart_on_revisit
|
||||
if (temps = templates('restart_on_revisit')).any?
|
||||
temps.first.key
|
||||
if (records = filter_records('restart_on_revisit')).any?
|
||||
records.first.key
|
||||
else
|
||||
false
|
||||
end
|
||||
|
|
|
@ -89,6 +89,8 @@ after_initialize do
|
|||
if user &&
|
||||
user.first_seen_at.blank? &&
|
||||
wizard = CustomWizard::Wizard.after_signup(user)
|
||||
|
||||
byebug
|
||||
|
||||
if !wizard.completed?
|
||||
custom_redirect = true
|
||||
|
|
Laden …
In neuem Issue referenzieren