0
0
Fork 1
Spiegel von https://github.com/paviliondev/discourse-custom-wizard.git synchronisiert 2024-09-19 23:31:11 +02:00
Dieser Commit ist enthalten in:
Angus McLeod 2020-04-01 16:03:26 +11:00
Ursprung 66bd90a56e
Commit 3bb16f1fb5
29 geänderte Dateien mit 811 neuen und 775 gelöschten Zeilen

Datei anzeigen

@ -49,14 +49,14 @@ export default Ember.Component.extend({
@observes('action.custom_category_wizard_field')
toggleCustomCategoryUserField() {
const wizard = this.get('action.custom_category_wizard_field');
if (wizard) this.set('action.custom_category_user_field', false);
if (this.action.custom_category_wizard_field)
this.set('action.custom_category_user_field', false);
},
@observes('action.custom_category_user_field')
toggleCustomCategoryWizardField() {
const user = this.get('action.custom_category_user_field');
if (user) this.set('action.custom_category_wizard_field', false);
if (this.action.custom_category_user_field)
this.set('action.custom_category_wizard_field', false);
},
@computed('wizard.apis')

Datei anzeigen

@ -14,6 +14,8 @@ export default Ember.Component.extend({
choicesTranslation: equal('field.choices_type', 'translation'),
choicesCustom: equal('field.choices_type', 'custom'),
categoryPropertyTypes: generateSelectKitContent(['id', 'slug']),
prefillEnabled: or('isCategory', 'isTag', 'isGroup'),
contentEnabled: or('isCategory', 'isTag', 'isGroup'),
@computed('field.type')
isInput: (type) => type === 'text' || type === 'textarea' || type === 'url',
@ -24,45 +26,40 @@ export default Ember.Component.extend({
@on('didInsertElement')
@observes('isUpload')
setupFileType() {
if (this.get('isUpload') && !this.get('field.file_types')) {
if (this.isUpload && !this.field.file_types) {
this.set('field.file_types', '.jpg,.png');
}
},
@computed('isCategory', 'isGroup', 'isTag')
prefillOptions(isCategory, isGroup, isTag) {
@computed('field.type')
prefillOptions(fieldType) {
if (!this.prefillEnabled) return {};
let options = {
hasOutput: true,
enableConnectors: true,
wizardFieldSelection: true,
userFieldSelection: true
}
if (isCategory || isGroup || isTag) {
options.userFieldSelection = 'key,value';
options[`${this.field.type}Selection`] = 'output';
textSelection: 'key,value',
wizardSelection: true,
userSelection: 'key,value'
}
options[`${fieldType}Selection`] = 'output';
options[`outputDefaultSelection`] = fieldType;
return options;
},
prefillEnabled: or('isCategory', 'isTag', 'isGroup'),
contentEnabled: or('isCategory', 'isTag', 'isGroup'),
@computed('field.type')
contentOptions(fieldType) {
if (!this.contentEnabled) return {};
let options = {
hasOutput: true,
enableConnectors: true,
wizardFieldSelection: 'key,value',
userFieldSelection: 'key,value',
textDisabled: 'output'
wizardSelection: 'key,value',
userSelection: 'key,value',
textSelection: 'key,value'
}
options[`${this.field.type}Selection`] = 'output';
options[`${this.field.type}AllowMultiple`] = true;
options[`${fieldType}Selection`] = 'output';
return options;
},

Datei anzeigen

@ -1,14 +0,0 @@
import { connectors } from '../lib/custom-wizard';
import { gt, or, alias } from "@ember/object/computed";
import { computed, observes} from "@ember/object";
export default Ember.Component.extend({
classNameBindings: [':input-pair', 'hasConnector::no-connector'],
connectors: connectors,
hasConnector: or('options.enableConnectors', 'connectorKey'),
firstPair: gt('pair.index', 0),
showRemove: alias('firstPair'),
showJoin: computed('pair.pairCount', function() {
return this.pair.index < (this.pair.pairCount - 1);
})
})

Datei anzeigen

@ -1,77 +0,0 @@
import {
newPair,
generateSelectKitContent,
defaultInputType
} from '../lib/custom-wizard';
import {
default as discourseComputed,
on
} from 'discourse-common/utils/decorators';
import { computed, set } from "@ember/object";
import { alias } from "@ember/object/computed";
export default Ember.Component.extend({
classNameBindings: [':custom-input', 'type'],
inputType: alias('input.type'),
outputConnector: computed('inputTypes', function() {
const key = this.outputConnectorKey || `admin.wizard.input.${this.type}.output`;
return I18n.t(key).toLowerCase();
}),
@on('init')
setDefaults() {
if (!this.type) this.set('type', defaultInputType(this.options));
},
@discourseComputed('options.allowedInputs')
allowedInputs(option) {
return option || 'conditional,assignment';
},
@discourseComputed('allowedInputs')
inputTypes(allowedInputs) {
return allowedInputs.split(',').map((type) => {
return {
id: type,
name: I18n.t(`admin.wizard.input.${type}.prefix`)
}
});
},
@discourseComputed('options.hasOutput', 'input.type')
hasPairs(hasOutput, inputType) {
return !hasOutput || inputType === 'conditional';
},
@discourseComputed('input.type')
hasOutputConnector(inputType) {
return inputType === 'conditional';
},
actions: {
addPair() {
const pairs = this.get('input.pairs');
const pairCount = pairs.length + 1;
pairs.forEach(p => (set(p, 'pairCount', pairCount)));
pairs.pushObject(
newPair(Object.assign(
{},
this.options,
{
index: pairs.length,
pairCount,
}
))
);
},
removePair(pair) {
const pairs = this.get('input.pairs');
const pairCount = pairs.length - 1;
pairs.forEach(p => (set(p, 'pairCount', pairCount)));
pairs.removeObject(pair);
}
}
});

Datei anzeigen

@ -32,41 +32,17 @@ export default Ember.Component.extend({
actions.forEach(a => {
if (a.type === 'route_to' && a.code) {
content.push(Ember.Object.create({
id: a.code,
label: "code (Route To)"
}));
content.push(
Ember.Object.create({
id: a.code,
label: "code (Route To)"
})
);
}
});
return content;
},
@computed('step.id', 'wizard.save_submissions')
wizardFields(currentStepId, saveSubmissions) {
const allSteps = this.get('wizard.steps');
let steps = allSteps;
let fields = [];
if (!saveSubmissions) {
steps = [allSteps.findBy('id', currentStepId)];
}
steps.forEach((s) => {
if (s.fields && s.fields.length > 0) {
let stepFields = s.fields.map((f) => {
return Ember.Object.create({
id: f.id,
label: `${f.id} (${s.id})`,
type: f.type
});
});
fields.push(...stepFields);
}
});
return fields;
},
actions: {
bannerUploadDone(upload) {

Datei anzeigen

@ -1,24 +0,0 @@
import { getOwner } from 'discourse-common/lib/get-owner';
import { on } from 'discourse-common/utils/decorators';
import { newInput } from '../lib/custom-wizard';
import { default as discourseComputed } from 'discourse-common/utils/decorators';
export default Ember.Component.extend({
classNames: 'field-mapper',
@discourseComputed('inputs.[]', 'options.singular')
canAdd(inputs, singular) {
return !singular || !inputs || inputs.length < 1;
},
actions: {
add() {
if (!this.get('inputs')) this.set('inputs', Ember.A());
this.get('inputs').pushObject(newInput(this.options));
},
remove(input) {
this.get('inputs').removeObject(input);
}
}
});

Datei anzeigen

@ -22,7 +22,7 @@ export default Ember.Component.extend({
},
updateItemOrder(itemId, newIndex) {
const items = this.get('items');
const items = this.items;
const item = items.findBy('id', itemId);
items.removeObject(item);
items.insertAt(newIndex, item);
@ -38,13 +38,13 @@ export default Ember.Component.extend({
return items.map((item) => {
if (item) {
const id = item.get('id');
const type = this.get('type');
const label = type === 'action' ? id : (item.get('label') || item.get('title') || id);
const id = item.id;
const type = this.type;
const label = type === 'action' ? id : (item.label || item.title || id);
let link = { id, label };
let classes = 'btn';
if (current && item.get('id') === current.get('id')) {
if (current && item.id === current.id) {
classes += ' btn-primary';
};
@ -57,8 +57,8 @@ export default Ember.Component.extend({
actions: {
add() {
const items = this.get('items');
const type = this.get('type');
const items = this.items;
const type = this.type;
const newId = `${type}_${items.length + 1}`;
let params = { id: newId, isNew: true };
@ -73,12 +73,11 @@ export default Ember.Component.extend({
},
change(itemId) {
const items = this.get('items');
this.set('current', items.findBy('id', itemId));
this.set('current', this.items.findBy('id', itemId));
},
remove(itemId) {
const items = this.get('items');
const items = this.items;
items.removeObject(items.findBy('id', itemId));
this.set('current', items[items.length - 1]);
}

Datei anzeigen

@ -0,0 +1,48 @@
import { computed, set } from "@ember/object";
import { alias, equal } from "@ember/object/computed";
import {
newPair,
connectorContent,
inputTypesContent
} from '../lib/custom-wizard';
export default Ember.Component.extend({
classNameBindings: [':mapper-input', 'type'],
inputType: alias('input.type'),
isConditional: equal('inputType', 'conditional'),
hasOutput: alias('options.hasOutput'),
hasPairs: computed('hasOutput', 'isConditional', function() { return !this.hasOutput || this.isConditional; }),
connectors: computed(function() { return connectorContent('output', this.input.type, this.options) }),
inputTypes: computed(function() { return inputTypesContent(this.options) }),
actions: {
addPair() {
const pairs = this.input.pairs;
const pairCount = pairs.length + 1;
pairs.forEach(p => (set(p, 'pairCount', pairCount)));
pairs.pushObject(
newPair(
this.input.type,
Object.assign(
{},
this.options,
{
index: pairs.length,
pairCount,
}
)
)
);
},
removePair(pair) {
const pairs = this.input.pairs;
const pairCount = pairs.length - 1;
pairs.forEach(p => (set(p, 'pairCount', pairCount)));
pairs.removeObject(pair);
}
}
});

Datei anzeigen

@ -0,0 +1,15 @@
import { connectorContent } from '../lib/custom-wizard';
import { gt, or, alias } from "@ember/object/computed";
import { computed, observes } from "@ember/object";
export default Ember.Component.extend({
classNameBindings: [':mapper-pair', 'hasConnector::no-connector'],
firstPair: gt('pair.index', 0),
showRemove: alias('firstPair'),
showJoin: computed('pair.pairCount', function() {
return this.pair.index < (this.pair.pairCount - 1);
}),
connectors: computed(function() {
return connectorContent('pair', this.inputType, this.options);
})
});

Datei anzeigen

@ -1,15 +1,14 @@
import { alias, equal } from "@ember/object/computed";
import { alias } from "@ember/object/computed";
import { computed } from "@ember/object";
import {
default as discourseComputed,
observes,
on
} from "discourse-common/utils/decorators";
import { defaultSelectionType } from '../lib/custom-wizard';
import { getOwner } from 'discourse-common/lib/get-owner';
export default Ember.Component.extend({
classNames: 'input-selector',
classNames: 'mapper-selector',
groups: alias('site.groups'),
categories: computed(function() {
return this.site.categories.map(c => ({ id: c.id, name: c.name }));
@ -18,7 +17,13 @@ export default Ember.Component.extend({
@discourseComputed
userFields() {
const controller = getOwner(this).lookup('controller:admin-wizard');
return controller.get('model.userFields');
return controller.model.userFields;
},
@discourseComputed
wizardFields() {
const controller = getOwner(this).lookup('controller:admin-wizard');
return controller.wizardFields;
},
@observes('options.@each')
@ -36,12 +41,11 @@ export default Ember.Component.extend({
return customPlaceholder || 'admin.wizard.text';
},
showText: equal('activeType', 'text'),
showInput(type) {
return this.activeType === type && this[`${type}Enabled`] && !this[`${type}Disabled`];
return this.activeType === type && this[`${type}Enabled`];
},
showText: computed('activeType', function() { return this.showInput('text') }),
showWizard: computed('activeType', function() { return this.showInput('wizard') }),
showUser: computed('activeType', function() { return this.showInput('user') }),
showCategory: computed('activeType', function() { return this.showInput('category') }),
@ -61,9 +65,9 @@ export default Ember.Component.extend({
return option.split(',').filter(o => types.indexOf(o) !== -1).length
},
textDisabled: computed('options.textDisabled', 'inputType', function() { return this.optionEnabled('textDisabled') }),
wizardEnabled: computed('options.wizardFieldSelection', 'inputType', function() { return this.optionEnabled('wizardFieldSelection') }),
userEnabled: computed('options.userFieldSelection', 'inputType', function() { return this.optionEnabled('userFieldSelection') }),
textEnabled: computed('options.textSelection', 'inputType', function() { return this.optionEnabled('textSelection') }),
wizardEnabled: computed('options.wizardSelection', 'inputType', function() { return this.optionEnabled('wizardSelection') }),
userEnabled: computed('options.userSelection', 'inputType', function() { return this.optionEnabled('userSelection') }),
categoryEnabled: computed('options.categorySelection', 'inputType', function() { return this.optionEnabled('categorySelection') }),
tagEnabled: computed('options.tagSelection', 'inputType', function() { return this.optionEnabled('tagSelection') }),
groupEnabled: computed('options.groupSelection', 'inputType', function() { return this.optionEnabled('groupSelection') }),

Datei anzeigen

@ -0,0 +1,43 @@
import { getOwner } from 'discourse-common/lib/get-owner';
import { on } from 'discourse-common/utils/decorators';
import { newInput } from '../lib/custom-wizard';
import { default as discourseComputed } from 'discourse-common/utils/decorators';
export default Ember.Component.extend({
classNames: 'wizard-mapper',
@discourseComputed('inputs.[]', 'options.singular')
canAdd(inputs, singular) {
return !singular || !inputs || inputs.length < 1;
},
@discourseComputed('options')
inputOptions(options) {
return {
hasOutput: options.hasOutput || false,
inputTypes: options.inputTypes || null,
pairConnector: options.pairConnector || null,
outputConnector: options.outputConnector || null,
textSelection: options.textSelection || true,
wizardSelection: options.wizardSelection || false,
userSelection: options.userSelection || false,
categorySelection: options.categorySelection || false,
tagSelection: options.tagSelection || false,
groupSelection: options.groupSelection || false,
keyDefaultSelection: options.keyDefaultSelection || null,
valueDefaultSelection: options.valueDefaultSelection || null,
outputDefaultSelection: options.outputDefaultSelection || null
}
},
actions: {
add() {
if (!this.get('inputs')) this.set('inputs', Ember.A());
this.get('inputs').pushObject(newInput(this.inputOptions));
},
remove(input) {
this.get('inputs').removeObject(input);
}
}
});

Datei anzeigen

@ -1,4 +1,4 @@
import { default as computed, observes } from 'discourse-common/utils/decorators';
import { default as discourseComputed, observes } from 'discourse-common/utils/decorators';
import { notEmpty } from "@ember/object/computed";
import showModal from 'discourse/lib/show-modal';
import { generateId } from '../lib/custom-wizard';
@ -7,24 +7,49 @@ import { dasherize } from "@ember/string";
export default Ember.Controller.extend({
hasName: notEmpty('model.name'),
@computed('model.id')
@observes('model.name')
setId() {
if (!this.model.existingId) this.set('model.id', generateId(this.model.name));
},
@discourseComputed('model.id')
wizardUrl(wizardId) {
return window.location.origin + '/w/' + dasherize(wizardId);
},
@observes('model.name')
setId() {
if (!this.model.existingId) {
this.set('model.id', generateId(this.model.name));
}
},
@computed('model.after_time_scheduled')
@discourseComputed('model.after_time_scheduled')
nextSessionScheduledLabel(scheduled) {
return scheduled ?
moment(scheduled).format('MMMM Do, HH:mm') :
I18n.t('admin.wizard.after_time_time_label');
},
@discourseComputed('currentStep.id', 'model.save_submissions', 'model.steps.@each.fields[]')
wizardFields(currentStepId, saveSubmissions) {
const allSteps = this.get('model.steps');
let steps = allSteps;
let fields = [];
if (!saveSubmissions) {
steps = [allSteps.findBy('id', currentStepId)];
}
steps.forEach((s) => {
if (s.fields && s.fields.length > 0) {
let stepFields = s.fields.map((f) => {
return Ember.Object.create({
id: f.id,
label: `${f.id} (${s.id})`,
type: f.type
});
});
fields.push(...stepFields);
}
});
return fields;
},
actions: {
save() {

Datei anzeigen

@ -8,6 +8,12 @@ function generateName(id) {
.replace(/(^\w|\b\w)/g, (m) => m.toUpperCase())
}
function generateId(name) {
return name.replace(/[^\w ]/g, '')
.replace(/ /g,"_")
.toLowerCase();
}
const profileFields = [
'name',
'username',
@ -21,25 +27,6 @@ const profileFields = [
'trust_level'
];
const connectors = [
{
id: 'eq',
name: '='
},{
id: 'gt',
name: '>'
},{
id: 'lt',
name: '<'
},{
id: 'gte',
name: '>='
},{
id: 'lte',
name: '<='
}
]
const actionTypes = [
'create_topic',
'update_profile',
@ -52,62 +39,130 @@ const actionTypes = [
'open_composer'
];
const selectionTypes = [
'text',
'wizardField',
'userField',
'group',
'category',
'tag'
]
// Inputs
const inputTypes = [
'pair',
const selectableInputTypes = [
'conditional',
'assignment'
]
function defaultInputType(options = {}) {
if (!options.hasOutput) return 'pair';
const allowedInputs = options.allowedInputs;
if (!allowedInputs) return 'conditional';
return allowedInputs.split(',')[0];
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}DefaultType`]) {
return options[`${inputType}DefaultType`];
if (options[`${inputType}DefaultSelection`]) {
return options[`${inputType}DefaultSelection`];
}
const textDisabled = options.textDisabled;
let type = 'text';
if (textDisabled === true ||
((typeof textDisabled == 'string') && textDisabled.indexOf(inputType) > -1)) {
for (let t of selectionTypes) {
let inputTypes = options[`${t}Selection`];
if (inputTypes === true ||
((typeof inputTypes == 'string') && inputTypes.indexOf(inputType) > -1)) {
type = t;
break;
}
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;
}
function newInput(options = {}) {
// items
function newPair(inputType, options = {}) {
let params = {
type: defaultInputType(options),
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(
Object.assign(
{},
inputType,
Object.assign({},
options,
{ index: 0, pairCount: 1 }
)
@ -118,30 +173,13 @@ function newInput(options = {}) {
if (options.hasOutput) {
params['output_type'] = defaultSelectionType('output', options);
params['connector'] = defaultConnector('output', inputType, options);
}
return Ember.Object.create(params);
}
function newPair(options = {}) {
let params = {
index: options.index,
pairCount: options.pairCount,
key: '',
key_type: defaultSelectionType('key', options),
value: '',
value_type: defaultSelectionType('value', options),
connector: 'eq'
}
return Ember.Object.create(params);
}
function generateId(name) {
return name.replace(/[^\w ]/g, '')
.replace(/ /g,"_")
.toLowerCase();
}
//
export {
generateSelectKitContent,
@ -150,7 +188,8 @@ export {
generateName,
defaultInputType,
defaultSelectionType,
connectors,
connectorContent,
inputTypesContent,
newInput,
newPair,
generateId

Datei anzeigen

@ -1,28 +1,18 @@
<div class="admin-wizard settings">
<div class="wizard-header large">
<span>{{model.name}}</span>
{{input
name="name"
value=model.name
placeholderKey="admin.wizard.name_placeholder"}}
{{#if model.name}}
<div class="wizard-url">
<div class="wizard-url">
{{#if model.name}}
<a href="{{wizardUrl}}" target="_blank">{{wizardUrl}}</a>
</div>
{{/if}}
{{/if}}
</div>
</div>
<div class="wizard-basic-details">
<div class="setting">
<div class="setting-label">
<label>{{i18n 'admin.wizard.name'}}</label>
</div>
<div class="setting-value">
{{input
name="name"
value=model.name
placeholderKey="admin.wizard.name_placeholder"}}
</div>
</div>
<div class="wizard-basic-details">
<div class="setting">
<div class="setting-label">
<label>{{i18n 'admin.wizard.background'}}</label>
@ -31,7 +21,8 @@
{{input
name="background"
value=model.background
placeholderKey="admin.wizard.background_placeholder"}}
placeholderKey="admin.wizard.background_placeholder"
class="medium"}}
</div>
</div>
@ -137,14 +128,14 @@
<label>{{i18n 'admin.wizard.permitted'}}</label>
</div>
<div class="setting-value">
{{wizard-field-mapper
{{wizard-mapper
inputs=model.permitted
options=(hash
singular=true
inputTypes='assignment'
hasOutput=true
groupSelection='output'
textDisabled='output'
allowedInputs='assignment'
singular=true
textSelection='key,value'
)}}
</div>
</div>
@ -153,10 +144,10 @@
{{wizard-links type="step" current=currentStep items=model.steps}}
{{#if currentStep}}
{{wizard-custom-step step=currentStep wizard=model}}
{{wizard-custom-step step=currentStep wizard=model wizardFields=wizardFields}}
{{/if}}
<div class='buttons'>
<div class='admin-wizard-buttons'>
<button {{action "save"}} disabled={{disableSave}} class='btn btn-primary'>
{{i18n 'admin.wizard.save'}}
</button>

Datei anzeigen

@ -21,13 +21,12 @@
</div>
<div class="setting-value">
{{wizard-field-mapper
{{wizard-mapper
inputs=action.title
wizardFields=wizardFields
options=(hash
hasOutput=true
wizardFieldSelection=true
userFieldSelection='key,value'
wizardSelection=true
userSelection='key,value'
)}}
</div>
</div>
@ -77,14 +76,13 @@
</div>
<div class="setting-value">
{{wizard-field-mapper
{{wizard-mapper
inputs=action.category
wizardFields=wizardFields
options=(hash
hasOutput=true
categorySelection='output'
wizardFieldSelection=true
userFieldSelection='key,value'
wizardSelection=true
userSelection='key,value'
)}}
</div>
</div>
@ -95,14 +93,13 @@
</div>
<div class="setting-value">
{{wizard-field-mapper
{{wizard-mapper
inputs=action.tags
wizardFields=wizardFields
options=(hash
hasOutput=true
tagSelection='output'
wizardFieldSelection=true
userFieldSelection='key,value'
wizardSelection=true
userSelection='key,value'
)}}
</div>
</div>
@ -130,15 +127,16 @@
<label>{{i18n 'admin.wizard.action.custom_fields.label'}}</label>
</div>
{{wizard-field-mapper
inputs=action.custom_fields
wizardFields=wizardFields
connectorKey='admin.wizard.action.custom_fields.connector'
keyPlaceholder='admin.wizard.action.custom_fields.key'
options=(hash
wizardFieldSelection='value'
userFieldSelection='value'
)}}
<div class="setting-value">
{{wizard-mapper
inputs=action.custom_fields
keyPlaceholder='admin.wizard.action.custom_fields.key'
options=(hash
pairConnector='set'
wizardSelection='value'
userSelection='value'
)}}
</div>
</div>
{{/if}}
@ -149,14 +147,12 @@
</div>
<div class="setting-value">
{{wizard-field-mapper
{{wizard-mapper
inputs=action.required
wizardFields=wizardFields
options=(hash
textDisabled='key'
enableConnectors=true
wizardFieldSelection=true
userFieldSelection=true
textSelection='value'
wizardSelection=true
userSelection=true
groupSelection=true
)}}
</div>
@ -168,14 +164,13 @@
</div>
<div class="setting-value">
{{wizard-field-mapper
{{wizard-mapper
inputs=action.recipient
wizardFields=wizardFields
options=(hash
textDisabled='key'
hasOutput=true
wizardFieldSelection=true
userFieldSelection=true
textSelection='value,output'
wizardSelection=true
userSelection=true
groupSelection='key,value'
)}}
</div>
@ -188,15 +183,14 @@
<label>{{i18n 'admin.wizard.action.update_profile.label'}}</label>
</div>
{{wizard-field-mapper
{{wizard-mapper
inputs=action.profile_updates
wizardFields=wizardFields
connectorKey='admin.wizard.action.update_profile.connector'
keyPlaceholder='admin.wizard.action.update_profile.key'
options=(hash
keyDefaultType='user'
userFieldSelection='key'
wizardFieldSelection='value'
pairConnector='set'
userSelection='key'
wizardSelection='value'
keyDefaultSelection='user'
)}}
</div>
{{/if}}
@ -259,17 +253,15 @@
</div>
<div class="setting-value">
{{wizard-field-mapper
{{wizard-mapper
inputs=action.group
wizardFields=wizardFields
outputConnectorKey='admin.wizard.action.add_to_group.output_connector'
options=(hash
hasOutput=true
enableConnectors=true
textDisabled='key'
wizardFieldSelection='key,value,assignment'
userFieldSelection='key,value,assignment'
textSelection='value,output'
wizardSelection='key,value,assignment'
userSelection='key,value,assignment'
groupSelection='value,output'
outputDefaultSelection='group'
)}}
</div>
</div>

Datei anzeigen

@ -103,9 +103,7 @@
<div class="wizard-header small">
{{i18n 'admin.wizard.field.choices_custom'}}
</div>
{{wizard-field-mapper
inputs=field.choices
wizardFields=wizardFields}}
{{wizard-mapper inputs=field.choices}}
{{/if}}
<div class="wizard-header small">
@ -164,10 +162,7 @@
</div>
<div class="setting-value">
{{wizard-field-mapper
inputs=field.prefill
wizardFields=wizardFields
options=prefillOptions}}
{{wizard-mapper inputs=field.prefill options=prefillOptions}}
</div>
</div>
{{/if}}
@ -179,10 +174,7 @@
</div>
<div class="setting-value">
{{wizard-field-mapper
inputs=field.content
wizardFields=wizardFields
options=contentOptions}}
{{wizard-mapper inputs=field.content options=contentOptions}}
</div>
</div>
{{/if}}

Datei anzeigen

@ -1,46 +0,0 @@
<div class="key input-block">
{{wizard-custom-input-selector
selectorType='key'
inputType=inputType
wizardFields=wizardFields
value=pair.key
activeType=pair.key_type
customPlaceholder=keyPlaceholder
options=options}}
</div>
{{#if hasConnector}}
<div class="connector">
{{#if options.enableConnectors}}
{{combo-box
value=pair.connector
content=connectors
onChange=(action (mut pair.connector))}}
{{/if}}
{{#if connectorKey}}
<span class="key-connector">
{{i18n connectorKey}}
</span>
{{/if}}
</div>
{{/if}}
<div class="value input-block">
{{wizard-custom-input-selector
selectorType='value'
inputType=inputType
wizardFields=wizardFields
value=pair.value
activeType=pair.value_type
customPlaceholder=valuePlaceholder
options=options}}
</div>
{{#if showJoin}}
<span class="join-pair">&</span>
{{/if}}
{{#if showRemove}}
<a {{action removePair pair}} class="remove-pair">{{d-icon 'minus'}}</a>
{{/if}}

Datei anzeigen

@ -51,14 +51,13 @@
<label>{{i18n 'admin.wizard.step.required_data.label'}}</label>
</div>
<div class="setting-value">
{{wizard-field-mapper
{{wizard-mapper
inputs=step.required_data
wizardFields=wizardFields
keyPlaceholder="admin.wizard.submission_key"
options=(hash
enableConnectors=true
wizardFieldSelection='value'
userFieldSelection='value'
pairConnector='equal'
wizardSelection='value'
userSelection='value'
)}}
{{#if step.required_data}}
<div class="required-data-message">
@ -76,12 +75,13 @@
<label>{{i18n 'admin.wizard.step.permitted_params.label'}}</label>
</div>
<div class="setting-value">
{{wizard-field-mapper
{{wizard-mapper
inputs=step.permitted_params
wizardFields=wizardFields
keyPlaceholder='admin.wizard.param_key'
valuePlaceholder='admin.wizard.submission_key'
connectorKey='admin.wizard.step.permitted_params.connector'}}
options=(hash
pairConnector='set'
)}}
</div>
</div>

Datei anzeigen

@ -1,5 +1,5 @@
{{#if options.hasOutput}}
<div class="connector prefix">
{{#if hasOutput}}
<div class="mapper-connector mapper-block">
{{combo-box
value=input.type
content=inputTypes
@ -8,20 +8,19 @@
{{/if}}
{{#if hasPairs}}
<div class="input-pairs">
<div class="mapper-pairs mapper-block">
{{#each input.pairs as |pair|}}
{{wizard-custom-input-pair
{{wizard-mapper-pair
pair=pair
last=pair.last
inputType=inputType
keyPlaceholder=keyPlaceholder
valuePlaceholder=valuePlaceholder
connectorKey=connectorKey
wizardFields=wizardFields
options=options
removePair=(action 'removePair')}}
{{/each}}
{{#if options.hasOutput}}
{{#if hasOutput}}
<a {{action 'addPair'}} class="add-pair">
{{d-icon 'plus'}}
</a>
@ -29,20 +28,20 @@
</div>
{{/if}}
{{#if options.hasOutput}}
{{#if hasOutputConnector}}
<div class="connector">
<span class="output-connector">
{{outputConnector}}
</span>
{{#if hasOutput}}
{{#if hasPairs}}
<div class="mapper-connector mapper-block">
{{combo-box
value=input.connector
content=connectors
onChange=(action (mut input.connector))}}
</div>
{{/if}}
<div class="output input-block">
{{wizard-custom-input-selector
<div class="output mapper-block">
{{wizard-mapper-selector
selectorType='output'
inputType=inputType
wizardFields=wizardFields
value=input.output
activeType=input.output_type
customPlaceholder=outputPlaceholder

Datei anzeigen

@ -0,0 +1,34 @@
<div class="key mapper-block">
{{wizard-mapper-selector
selectorType='key'
inputType=inputType
value=pair.key
activeType=pair.key_type
customPlaceholder=keyPlaceholder
options=options}}
</div>
<div class="mapper-connector mapper-block">
{{combo-box
value=pair.connector
content=connectors
onChange=(action (mut pair.connector))}}
</div>
<div class="value mapper-block">
{{wizard-mapper-selector
selectorType='value'
inputType=inputType
value=pair.value
activeType=pair.value_type
customPlaceholder=valuePlaceholder
options=options}}
</div>
{{#if showJoin}}
<span class="join-pair">&</span>
{{/if}}
{{#if showRemove}}
<a {{action removePair pair}} class="remove-pair">{{d-icon 'minus'}}</a>
{{/if}}

Datei anzeigen

@ -1,10 +1,10 @@
<div class="type-selector">
{{#unless textDisabled}}
{{#if textEnabled}}
{{input-type-toggle
activeType=activeType
type='text'
toggle=(action 'toggleType')}}
{{/unless}}
{{/if}}
{{#if wizardEnabled}}
{{input-type-toggle

Datei anzeigen

@ -1,17 +1,14 @@
{{#each inputs as |input|}}
{{wizard-custom-input
{{wizard-mapper-input
input=input
wizardFields=wizardFields
keyPlaceholder=keyPlaceholder
valuePlaceholder=valuePlaceholder
connectorKey=connectorKey
outputConnectorKey=outputConnectorKey
options=options
options=inputOptions
remove=(action 'remove')}}
{{/each}}
{{#if canAdd}}
<span class="add-custom-input">
<span class="add-mapper-input">
{{d-button action='add' label='admin.wizard.add' icon='plus'}}
</span>
{{/if}}

Datei anzeigen

@ -1,3 +1,7 @@
@import 'wizard-mapper';
@import 'wizard-transfer';
@import 'wizard-api';
$setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
.wizard-list {
@ -32,8 +36,18 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
@extend .wizard-settings-group;
}
.wizard-basic-details {
margin-bottom: 30px;
.admin-wizard.settings .wizard-basic-details {
justify-content: initial;
.setting {
width: auto;
margin-right: 20px;
.setting-label {
width: initial;
min-width: initial;
}
}
}
.new-wizard {
@ -47,6 +61,10 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
font-size: 1.5em;
min-height: 31px;
margin-bottom: 30px;
input {
margin-bottom: 0;
}
}
&.medium {
@ -70,6 +88,10 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
}
}
.admin-wizard-buttons {
margin-top: 20px;
}
.content-list + .content {
overflow: hidden;
}
@ -84,14 +106,6 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
width: 48%;
margin-bottom: 30px;
padding-bottom: 0;
&:last-of-type {
margin-bottom: 0;
}
&.field-mapper-setting {
margin-top: 5px;
}
.setting-label {
width: 80px;
@ -120,6 +134,7 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
input[type="text"], textarea {
width: 100%;
box-sizing: border-box;
margin-bottom: 0;
}
input[disabled] {
@ -131,9 +146,14 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
width: 70px;
}
input.medium {
width: 200px;
}
.uploaded-image-preview {
width: 100%;
max-height: 100px;
margin-bottom: 0;
}
.image-upload-controls {
@ -152,10 +172,6 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
float: left;
margin: 5px 7px 0 0;
}
span {
overflow: hidden;
}
}
&.full, &.full-inline {
@ -201,6 +217,10 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
margin-top: 5px;
}
}
.wizard-custom-action > [class~='setting']:last-of-type {
margin-bottom: 0;
}
.select-box-kit-header {
height: initial;
@ -224,20 +244,6 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
}
}
.field-mapper {
width: 100%;
.multi-select {
.multi-select-header, input {
min-height: 25px;
}
.choices .choice {
height: 24px;
}
}
}
.btn-after-time {
margin-top: 7px;
}
@ -301,7 +307,7 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
}
.wizard-links {
margin-bottom: 20px;
margin: 20px 0;
display: inline-block;
width: 100%;
@ -344,124 +350,6 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
}
}
[class~='custom-input'] {
display: inline-flex;
align-items: flex-start;
position: relative;
padding-bottom: 30px;
&:last-of-type {
padding-bottom: 10px;
}
.input-selector {
width: 100%;
}
.type-selector {
position: absolute;
top: -22px;
width: 100%;
}
.type-selector a {
color: $primary;
margin-right: 4px;
&.active {
color: $tertiary;
text-decoration: underline;
}
&:last-of-type {
margin-right: 0;
}
}
.input-pairs {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
.add-pair {
margin-top: 10px;
}
.remove-pair {
position: absolute;
top: 25px;
right: -25px;
}
.join-pair {
position: absolute;
bottom: -25px;
left: 50%;
transform: translateX(-50%);
}
}
.input-pair {
display: flex;
align-items: flex-end;
position: relative;
&:not(:first-of-type) {
margin-top: 20px;
}
&.no-connector div.input-block:not(:last-of-type) {
margin-right: 10px;
}
}
.d-icon {
text-align: center;
}
input {
margin: 0;
}
input[disabled] {
background-color: $primary-low;
border-color: #ddd;
}
.input-block, .select-kit, input {
width: 160px;
min-width: 160px;
}
a.remove-input {
position: absolute;
right: -25px;
top: 5px;
}
.connector {
margin: 0 10px;
&.prefix {
margin-left: 0;
}
.select-kit {
min-width: 50px;
}
.key-connector {
padding-bottom: 5px;
display: inline-block;
}
.output-connector {
white-space: nowrap;
}
}
}
.required-data-message {
display: inline-block;
margin-top: 20px;
@ -469,6 +357,10 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
.label {
margin-bottom: 5px;
}
input {
margin-bottom: 0;
}
}
.admin-contents .wizard-submissions {
@ -574,186 +466,7 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
}
}
.wizard-step-contents {
height: unset !important;
}
.admin-wizards-api {
margin-bottom: 40px;
.content-list {
margin-right: 20px;
}
.new-api {
margin-top: 20px;
}
.metadata .title input {
width: 400px;
}
.buttons {
text-align: right;
vertical-align: middle;
> .d-icon, > .spinner {
margin-right: 7px;
}
.error {
margin-top: 10px;
color: $danger;
}
}
}
.wizard-api-header {
&.page {
margin-bottom: 20px;
}
.buttons {
float: right;
}
.wizard-header {
overflow: hidden;
}
}
.wizard-api-authentication {
display: flex;
background-color: $primary-very-low;
padding: 20px;
margin-bottom: 20px;
.settings {
width: 50%;
max-width: 50%;
}
.redirect-uri .controls {
word-break: break-all;
}
.auth-type .select-kit {
min-width: 210px;
width: 210px;
margin-bottom: 10px;
}
.status {
border-left: 1px solid $primary;
margin-left: 20px;
padding-left: 20px;
width: 50%;
max-width: 50%;
.wizard-header {
overflow: hidden;
}
.authorization {
float: right;
}
.control-group {
margin-bottom: 15px;
}
}
}
.wizard-api-endpoints {
background-color: $primary-very-low;
padding: 20px;
margin-bottom: 20px;
.endpoint-list {
margin-top: 20px;
ul {
margin: 0;
list-style: none;
}
}
.endpoint {
margin-top: 20px;
.top, .bottom {
display: flex;
}
.top {
margin-bottom: 15px;
}
.combo-box {
margin-right: 10px;
width: 210px;
}
input {
margin: 0;
margin-right: 10px;
}
.endpoint-url {
flex: 1 1 auto;
}
.remove-endpoint {
margin-left: auto;
}
}
}
.wizard-api-log {
background-color: #f8f8f8;
padding: 20px;
margin-bottom: 20px;
}
.wizard-step-contents{
height: unset !important;
}
// Tansfer tab
.admin-wizards-transfer .admin-container .container {
padding-top: 20px;
}
#file-url {
display: block;
margin-bottom: 10px;
}
.wizard-list-select {
list-style-type: none;
}
.wizard-action-buttons {
flex-direction: column;
}
.import-message {
margin: 10px 0;
}
.import-logs {
margin-top: 20px;
.title {
font-weight: 800;
margin-bottom: 10px;
}
ul {
list-style: none;
}
}

Datei anzeigen

@ -0,0 +1,140 @@
.admin-wizards-api {
margin-bottom: 40px;
.content-list {
margin-right: 20px;
}
.new-api {
margin-top: 20px;
}
.metadata .title input {
width: 400px;
}
.buttons {
text-align: right;
vertical-align: middle;
> .d-icon, > .spinner {
margin-right: 7px;
}
.error {
margin-top: 10px;
color: $danger;
}
}
}
.wizard-api-header {
&.page {
margin-bottom: 20px;
}
.buttons {
float: right;
}
.wizard-header {
overflow: hidden;
}
}
.wizard-api-authentication {
display: flex;
background-color: $primary-very-low;
padding: 20px;
margin-bottom: 20px;
.settings {
width: 50%;
max-width: 50%;
}
.redirect-uri .controls {
word-break: break-all;
}
.auth-type .select-kit {
min-width: 210px;
width: 210px;
margin-bottom: 10px;
}
.status {
border-left: 1px solid $primary;
margin-left: 20px;
padding-left: 20px;
width: 50%;
max-width: 50%;
.wizard-header {
overflow: hidden;
}
.authorization {
float: right;
}
.control-group {
margin-bottom: 15px;
}
}
}
.wizard-api-endpoints {
background-color: $primary-very-low;
padding: 20px;
margin-bottom: 20px;
.endpoint-list {
margin-top: 20px;
ul {
margin: 0;
list-style: none;
}
}
.endpoint {
margin-top: 20px;
.top, .bottom {
display: flex;
}
.top {
margin-bottom: 15px;
}
.combo-box {
margin-right: 10px;
width: 210px;
}
input {
margin: 0;
margin-right: 10px;
}
.endpoint-url {
flex: 1 1 auto;
}
.remove-endpoint {
margin-left: auto;
}
}
}
.wizard-api-log {
background-color: #f8f8f8;
padding: 20px;
margin-bottom: 20px;
}
.wizard-step-contents{
height: unset !important;
}

Datei anzeigen

@ -0,0 +1,143 @@
.wizard-mapper {
width: 100%;
.select-kit {
min-width: initial;
width: initial;
.select-kit-header {
min-height: 30px;
}
.choices .choice {
height: 24px;
}
}
div.mapper-block:not(:last-of-type) {
margin-right: 10px;
}
}
[class~='mapper-input'] {
display: flex;
align-items: flex-start;
width: min-content;
position: relative;
padding-bottom: 30px;
&:last-of-type {
padding-bottom: 0;
}
.d-icon {
text-align: center;
}
input {
margin: 0;
}
input[disabled] {
background-color: $primary-low;
border-color: #ddd;
}
a.remove-input {
position: absolute;
right: -25px;
top: 5px;
}
}
.add-mapper-input {
display: block;
.btn {
background-color: $secondary;
border: 1px solid $primary-medium;
}
}
.mapper-input + .add-mapper-input {
padding-top: 10px;
}
.mapper-connector {
width: auto;
min-width: 40px;
.select-kit .select-kit-header {
padding: 0 0.5em;
display: flex;
justify-content: center;
.caret-icon {
display: none;
}
}
}
.mapper-selector {
width: 100%;
max-width: 160px;
min-width: 160px;
input, .select-kit {
width: 160px;
}
.type-selector {
position: absolute;
top: -22px;
width: 100%;
}
.type-selector a {
color: $primary;
margin-right: 4px;
&.active {
color: $tertiary;
text-decoration: underline;
}
&:last-of-type {
margin-right: 0;
}
}
}
.mapper-pairs {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
.add-pair {
margin-top: 5px;
}
.remove-pair {
position: absolute;
top: 5px;
right: -25px;
}
.join-pair {
position: absolute;
bottom: -25px;
left: 50%;
transform: translateX(-50%);
}
}
.mapper-pair {
display: flex;
align-items: flex-end;
position: relative;
&:not(:first-of-type) {
margin-top: 30px;
}
}

Datei anzeigen

@ -0,0 +1,33 @@
.admin-wizards-transfer .admin-container .container {
padding-top: 20px;
}
#file-url {
display: block;
margin-bottom: 10px;
}
.wizard-list-select {
list-style-type: none;
}
.wizard-action-buttons {
flex-direction: column;
}
.import-message {
margin: 10px 0;
}
.import-logs {
margin-top: 20px;
.title {
font-weight: 800;
margin-bottom: 10px;
}
ul {
list-style: none;
}
}

Datei anzeigen

@ -12,9 +12,9 @@ en:
custom_label: "Custom"
submissions_label: "Submissions"
name: "Name"
name_placeholder: "name of the wizard"
name_placeholder: "name"
background: "Background"
background_placeholder: "background: css"
background_placeholder: "#hex"
save_submissions: "Save"
save_submissions_label: "Save wizard submissions."
multiple_submissions: "Multiple"
@ -22,7 +22,7 @@ en:
after_signup: "Signup"
after_signup_label: "Users directed to wizard after signup."
after_time: "Time"
after_time_label: "Users directed to wizard after start time."
after_time_label: "Users directed to wizard after start time:"
after_time_time_label: "Start Time"
after_time_modal:
title: "Wizard Start Time"
@ -69,10 +69,10 @@ en:
input:
conditional:
prefix: 'if'
name: 'if'
output: 'then'
assignment:
prefix: 'set'
name: 'set'
error:
name_required: "Wizards must have a name."
@ -96,7 +96,6 @@ en:
not_permitted_message: "Message shown when required data not present"
permitted_params:
label: "Params"
connector: "save as"
field:
type: "Choose a type"
@ -126,6 +125,15 @@ en:
prefill: "Prefill"
content: "Content"
connector:
then: "then"
set: "set"
equal: '='
greater: '>'
less: '<'
greater_or_equal: '>='
less_or_equal: '<='
action:
header: "Actions"
include: "Include Fields"
@ -135,9 +143,8 @@ en:
interpolate_fields: "Insert wizard fields using the field_id in w{}. Insert user fields using field key in u{}."
custom_fields:
label: "Custom Fields"
label: "Custom"
key: "field"
connector: "set"
skip_redirect:
label: "Redirect"
description: "Don't redirect the user to this {{type}} after the wizard completes"
@ -149,9 +156,8 @@ en:
category: "Category"
tags: "Tags"
update_profile:
label: "Update Profile"
label: "Fields"
key: "field"
connector: "set"
post_builder:
checkbox: "Post Builder"
label: "Builder"
@ -160,7 +166,6 @@ en:
placeholder: "Insert wizard fields using the field_id in w{}. Insert user fields using field key in u{}."
add_to_group:
label: "Add to Group"
output_connector: "add to"
route_to:
label: "Route To"
url: "Url"

Datei anzeigen

@ -3,7 +3,13 @@ class CustomWizard::Mapper
USER_FIELDS = ['name', 'username', 'email', 'date_of_birth', 'title', 'locale', 'trust_level']
PROFILE_FIELDS = ['location', 'website', 'bio_raw']
OPERATORS = { 'eq': '==', 'gt': '>', 'lt': '<', 'gte': '>=', 'lte': '<=' }
OPERATORS = {
equal: '=',
greater: '>',
less: '<',
greater_or_equal: '>=',
less_or_equal: '<='
}
def initialize(params)
@inputs = params[:inputs] || {}
@ -47,14 +53,19 @@ class CustomWizard::Mapper
pairs.each do |pair|
key = map_field(pair['key'], pair['key_type'])
value = map_field(pair['value'], pair['value_type'])
failed = true unless key.public_send(operator(pair['connector']), value)
begin
failed = true unless key.public_send(operator(pair['connector']), value)
rescue => e
byebug
end
end
!failed
end
def operator(connector)
OPERATORS[connector] || '=='
OPERATORS[connector.to_sym] || '=='
end
def map_field(value, type)

Datei anzeigen

@ -4,7 +4,8 @@
# authors: Angus McLeod
# url: https://github.com/angusmcleod/discourse-custom-wizard
register_asset 'stylesheets/wizard_custom_admin.scss'
register_asset 'stylesheets/common/wizard-admin.scss'
register_asset 'stylesheets/common/wizard-mapper.scss'
register_asset 'lib/jquery.timepicker.min.js'
register_asset 'lib/jquery.timepicker.scss'