wip
Dieser Commit ist enthalten in:
Ursprung
694f5f1898
Commit
87a53a8c85
32 geänderte Dateien mit 600 neuen und 429 gelöschten Zeilen
|
@ -0,0 +1,19 @@
|
||||||
|
import { default as discourseComputed } from 'discourse-common/utils/decorators';
|
||||||
|
import Component from '@ember/component';
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
classNames: 'wizard-advanced-toggle',
|
||||||
|
|
||||||
|
@discourseComputed('showAdvanced')
|
||||||
|
toggleClass(showAdvanced) {
|
||||||
|
let classes = 'btn'
|
||||||
|
if (showAdvanced) classes += ' btn-primary';
|
||||||
|
return classes;
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
toggleAdvanced() {
|
||||||
|
this.toggleProperty('showAdvanced');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
|
@ -1,11 +1,11 @@
|
||||||
import { default as discourseComputed, observes } from 'discourse-common/utils/decorators';
|
import { default as discourseComputed, observes, on } from 'discourse-common/utils/decorators';
|
||||||
import { equal, not, empty } from "@ember/object/computed";
|
import { equal, not, empty, or } from "@ember/object/computed";
|
||||||
import {
|
import {
|
||||||
actionTypes,
|
actionTypes,
|
||||||
generateName,
|
generateName,
|
||||||
generateSelectKitContent,
|
generateSelectKitContent,
|
||||||
profileFields
|
profileFields
|
||||||
} from '../lib/custom-wizard';
|
} from '../lib/wizard';
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
classNames: 'wizard-custom-action',
|
classNames: 'wizard-custom-action',
|
||||||
|
@ -19,6 +19,13 @@ export default Ember.Component.extend({
|
||||||
routeTo: equal('action.type', 'route_to'),
|
routeTo: equal('action.type', 'route_to'),
|
||||||
disableId: not('action.isNew'),
|
disableId: not('action.isNew'),
|
||||||
groupPropertyTypes: generateSelectKitContent(['id', 'name']),
|
groupPropertyTypes: generateSelectKitContent(['id', 'name']),
|
||||||
|
hasAdvanced: or('basicTopicFields', 'routeTo'),
|
||||||
|
|
||||||
|
@on('didInsertElement')
|
||||||
|
@observes('action.type')
|
||||||
|
updateId() {
|
||||||
|
if (this.action.type) this.set('action.id', generateName(this.action.type));
|
||||||
|
},
|
||||||
|
|
||||||
@discourseComputed('action.type')
|
@discourseComputed('action.type')
|
||||||
basicTopicFields(actionType) {
|
basicTopicFields(actionType) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { default as discourseComputed, 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 { equal, not, or } from "@ember/object/computed";
|
||||||
import { generateSelectKitContent } from '../lib/custom-wizard';
|
import { generateSelectKitContent } from '../lib/wizard';
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
classNames: 'wizard-custom-field',
|
classNames: 'wizard-custom-field',
|
||||||
|
@ -16,6 +16,7 @@ export default Ember.Component.extend({
|
||||||
categoryPropertyTypes: generateSelectKitContent(['id', 'slug']),
|
categoryPropertyTypes: generateSelectKitContent(['id', 'slug']),
|
||||||
prefillEnabled: or('isCategory', 'isTag', 'isGroup'),
|
prefillEnabled: or('isCategory', 'isTag', 'isGroup'),
|
||||||
contentEnabled: or('isCategory', 'isTag', 'isGroup'),
|
contentEnabled: or('isCategory', 'isTag', 'isGroup'),
|
||||||
|
hasAdvanced: or('isCategory', 'isTag', 'isGroup'),
|
||||||
|
|
||||||
@discourseComputed('field.type')
|
@discourseComputed('field.type')
|
||||||
isInput: (type) => type === 'text' || type === 'textarea' || type === 'url',
|
isInput: (type) => type === 'text' || type === 'textarea' || type === 'url',
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { observes, on, default as discourseComputed } from 'discourse-common/utils/decorators';
|
import { observes, on, default as discourseComputed } from 'discourse-common/utils/decorators';
|
||||||
import { not } from "@ember/object/computed";
|
import { not } from "@ember/object/computed";
|
||||||
|
import EmberObject from "@ember/object";
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
classNames: 'wizard-custom-step',
|
classNames: 'wizard-custom-step',
|
||||||
|
@ -31,7 +32,7 @@ export default Ember.Component.extend({
|
||||||
actions.forEach(a => {
|
actions.forEach(a => {
|
||||||
if (a.type === 'route_to' && a.code) {
|
if (a.type === 'route_to' && a.code) {
|
||||||
content.push(
|
content.push(
|
||||||
Ember.Object.create({
|
EmberObject.create({
|
||||||
id: a.code,
|
id: a.code,
|
||||||
label: "code (Route To)"
|
label: "code (Route To)"
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { default as computed, on, observes } from 'discourse-common/utils/decorators';
|
import { default as computed, on, observes } from 'discourse-common/utils/decorators';
|
||||||
import { notEmpty } from "@ember/object/computed";
|
import { notEmpty } from "@ember/object/computed";
|
||||||
import { scheduleOnce } from "@ember/runloop";
|
import { scheduleOnce } from "@ember/runloop";
|
||||||
|
import EmberObject from "@ember/object";
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
classNameBindings: [':wizard-links', 'type'],
|
classNameBindings: [':wizard-links', 'type'],
|
||||||
|
@ -67,7 +68,7 @@ export default Ember.Component.extend({
|
||||||
params['actions'] = Ember.A();
|
params['actions'] = Ember.A();
|
||||||
};
|
};
|
||||||
|
|
||||||
const newItem = Ember.Object.create(params);
|
const newItem = EmberObject.create(params);
|
||||||
items.pushObject(newItem);
|
items.pushObject(newItem);
|
||||||
this.set('current', newItem);
|
this.set('current', newItem);
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import Component from "@ember/component";
|
||||||
|
import { lt } from '@ember/object/computed';
|
||||||
|
import { computed } from "@ember/object";
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
classNameBindings: [':mapper-connector', ':mapper-block', 'single'],
|
||||||
|
single: lt('connectors.length', 2),
|
||||||
|
connectorLabel: computed(function() {
|
||||||
|
let key = this.connector;
|
||||||
|
let path = this.inputTypes ? `input.${key}.name` : `connector.${key}`;
|
||||||
|
return I18n.t(`admin.wizard.${path}`);
|
||||||
|
})
|
||||||
|
});
|
|
@ -1,6 +1,6 @@
|
||||||
import { computed, set } from "@ember/object";
|
import { computed, set } from "@ember/object";
|
||||||
import { alias, equal } from "@ember/object/computed";
|
import { alias, equal } from "@ember/object/computed";
|
||||||
import { newPair, connectorContent, inputTypesContent } from '../lib/mapper';
|
import { newPair, connectorContent, inputTypesContent } from '../lib/wizard-mapper';
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
classNameBindings: [':mapper-input', 'type'],
|
classNameBindings: [':mapper-input', 'type'],
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { connectorContent } from '../lib/mapper';
|
import { connectorContent } from '../lib/wizard-mapper';
|
||||||
import { gt, or, alias } from "@ember/object/computed";
|
import { gt, or, alias } from "@ember/object/computed";
|
||||||
import { computed, observes } from "@ember/object";
|
import { computed, observes } from "@ember/object";
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { alias } from "@ember/object/computed";
|
||||||
import { computed } from "@ember/object";
|
import { computed } from "@ember/object";
|
||||||
import { default as discourseComputed, observes } from "discourse-common/utils/decorators";
|
import { default as discourseComputed, observes } from "discourse-common/utils/decorators";
|
||||||
import { getOwner } from 'discourse-common/lib/get-owner';
|
import { getOwner } from 'discourse-common/lib/get-owner';
|
||||||
import { defaultSelectionType } from '../lib/mapper';
|
import { defaultSelectionType } from '../lib/wizard-mapper';
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
classNames: 'mapper-selector',
|
classNames: 'mapper-selector',
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { getOwner } from 'discourse-common/lib/get-owner';
|
import { getOwner } from 'discourse-common/lib/get-owner';
|
||||||
import { on } from 'discourse-common/utils/decorators';
|
import { on } from 'discourse-common/utils/decorators';
|
||||||
import { newInput } from '../lib/mapper';
|
import { newInput } from '../lib/wizard-mapper';
|
||||||
import { default as discourseComputed } from 'discourse-common/utils/decorators';
|
import { default as discourseComputed } from 'discourse-common/utils/decorators';
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
|
|
|
@ -2,7 +2,7 @@ import {
|
||||||
default as discourseComputed,
|
default as discourseComputed,
|
||||||
on
|
on
|
||||||
} from 'discourse-common/utils/decorators';
|
} from 'discourse-common/utils/decorators';
|
||||||
import { profileFields } from '../lib/custom-wizard';
|
import { profileFields } from '../lib/wizard';
|
||||||
import { scheduleOnce } from "@ember/runloop";
|
import { scheduleOnce } from "@ember/runloop";
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
import { default as discourseComputed, observes } from 'discourse-common/utils/decorators';
|
import { default as discourseComputed, observes } from 'discourse-common/utils/decorators';
|
||||||
import { notEmpty } from "@ember/object/computed";
|
import { notEmpty } from "@ember/object/computed";
|
||||||
import showModal from 'discourse/lib/show-modal';
|
import showModal from 'discourse/lib/show-modal';
|
||||||
import { generateId } from '../lib/custom-wizard';
|
import { generateId } from '../lib/wizard';
|
||||||
import { buildProperties } from '../lib/json';
|
import { buildProperties } from '../lib/wizard-json';
|
||||||
import { dasherize } from "@ember/string";
|
import { dasherize } from "@ember/string";
|
||||||
|
import EmberObject from "@ember/object";
|
||||||
|
import { scheduleOnce } from "@ember/runloop";
|
||||||
|
|
||||||
export default Ember.Controller.extend({
|
export default Ember.Controller.extend({
|
||||||
hasName: notEmpty('model.name'),
|
hasName: notEmpty('model.name'),
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this._super();
|
||||||
|
scheduleOnce('afterRender', () => ($("body").addClass('admin-wizard')));
|
||||||
|
},
|
||||||
|
|
||||||
@observes('model.name')
|
@observes('model.name')
|
||||||
setId() {
|
setId() {
|
||||||
if (!this.model.existingId) this.set('model.id', generateId(this.model.name));
|
if (!this.model.existingId) this.set('model.id', generateId(this.model.name));
|
||||||
|
@ -38,7 +45,7 @@ export default Ember.Controller.extend({
|
||||||
steps.forEach((s) => {
|
steps.forEach((s) => {
|
||||||
if (s.fields && s.fields.length > 0) {
|
if (s.fields && s.fields.length > 0) {
|
||||||
let stepFields = s.fields.map((f) => {
|
let stepFields = s.fields.map((f) => {
|
||||||
return Ember.Object.create({
|
return EmberObject.create({
|
||||||
id: f.id,
|
id: f.id,
|
||||||
label: `${f.id} (${s.id})`,
|
label: `${f.id} (${s.id})`,
|
||||||
type: f.type
|
type: f.type
|
||||||
|
@ -98,5 +105,9 @@ export default Ember.Controller.extend({
|
||||||
|
|
||||||
controller.setup();
|
controller.setup();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
toggleAdvanced() {
|
||||||
|
this.toggleProperty('model.showAdvanced');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { ajax } from 'discourse/lib/ajax';
|
||||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||||
import CustomWizardApi from '../models/custom-wizard-api';
|
import CustomWizardApi from '../models/custom-wizard-api';
|
||||||
import { default as computed } from 'discourse-common/utils/decorators';
|
import { default as computed } from 'discourse-common/utils/decorators';
|
||||||
import { generateSelectKitContent } from '../lib/custom-wizard';
|
import { generateSelectKitContent } from '../lib/wizard';
|
||||||
|
|
||||||
export default Ember.Controller.extend({
|
export default Ember.Controller.extend({
|
||||||
queryParams: ['refresh_list'],
|
queryParams: ['refresh_list'],
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { properties } from '../lib/custom-wizard';
|
import { properties, mappedProperties } from '../lib/wizard';
|
||||||
import { mappedProperties } from '../lib/mapper';
|
|
||||||
import EmberObject from '@ember/object';
|
import EmberObject from '@ember/object';
|
||||||
|
|
||||||
function present(val) {
|
function present(val) {
|
||||||
|
@ -152,38 +151,36 @@ function buildObject(json, type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(json).forEach(prop => {
|
Object.keys(json).forEach(prop => {
|
||||||
if (mapped(prop, type)) {
|
if (mapped(prop, type) && present(json[prop])) {
|
||||||
let inputs = [];
|
let inputs = [];
|
||||||
|
|
||||||
if (present(json[prop])) {
|
json[prop].forEach(inputJson => {
|
||||||
json[prop].forEach(inputJson => {
|
let input = {}
|
||||||
let input = {}
|
|
||||||
|
|
||||||
Object.keys(inputJson).forEach(inputKey => {
|
Object.keys(inputJson).forEach(inputKey => {
|
||||||
if (inputKey === 'pairs') {
|
if (inputKey === 'pairs') {
|
||||||
let pairs = [];
|
let pairs = [];
|
||||||
let pairCount = inputJson.pairs.length;
|
let pairCount = inputJson.pairs.length;
|
||||||
|
|
||||||
inputJson.pairs.forEach(pairJson => {
|
inputJson.pairs.forEach(pairJson => {
|
||||||
let pair = pairJson;
|
let pair = pairJson;
|
||||||
pair.pairCount = pairCount;
|
pair.pairCount = pairCount;
|
||||||
|
|
||||||
pairs.push(
|
pairs.push(
|
||||||
EmberObject.create(pair)
|
EmberObject.create(pair)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
input.pairs = pairs;
|
input.pairs = pairs;
|
||||||
} else {
|
} else {
|
||||||
input[inputKey] = inputJson[inputKey];
|
input[inputKey] = inputJson[inputKey];
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
inputs.push(
|
|
||||||
EmberObject.create(input)
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
inputs.push(
|
||||||
|
EmberObject.create(input)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
params[prop] = Ember.A(inputs);
|
params[prop] = Ember.A(inputs);
|
||||||
} else {
|
} else {
|
||||||
|
@ -194,6 +191,30 @@ function buildObject(json, type) {
|
||||||
return EmberObject.create(params);
|
return EmberObject.create(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isAdvancedWizard(property, value) {
|
||||||
|
if (property === 'save_submissions' && value == false) return true;
|
||||||
|
if (property === 'restart_on_revisit' && value == true) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAdvancedStep(property, value) {
|
||||||
|
return mapped(property, 'step') && present(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAdvancedField(params) {
|
||||||
|
if (present(params.property)) return true;
|
||||||
|
if (present(params.prefill)) return true;
|
||||||
|
if (present(params.content)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAdvancedAction(params) {
|
||||||
|
if (present(params.code)) return true;
|
||||||
|
if (present(params.custom_fields)) return true;
|
||||||
|
if (present(params.skip_redirect)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function buildProperties(json) {
|
function buildProperties(json) {
|
||||||
let steps = Ember.A();
|
let steps = Ember.A();
|
||||||
let props = {
|
let props = {
|
||||||
|
@ -206,6 +227,10 @@ function buildProperties(json) {
|
||||||
|
|
||||||
properties.wizard.forEach((p) => {
|
properties.wizard.forEach((p) => {
|
||||||
props[p] = json[p];
|
props[p] = json[p];
|
||||||
|
|
||||||
|
if (isAdvancedWizard(p, json[p])) {
|
||||||
|
props.showAdvanced = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (present(json.steps)) {
|
if (present(json.steps)) {
|
||||||
|
@ -216,15 +241,23 @@ function buildProperties(json) {
|
||||||
|
|
||||||
properties.step.forEach((p) => {
|
properties.step.forEach((p) => {
|
||||||
stepParams[p] = stepJson[p];
|
stepParams[p] = stepJson[p];
|
||||||
|
|
||||||
|
if (isAdvancedStep(p, stepJson[p])) {
|
||||||
|
stepParams.showAdvanced = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
stepParams.fields = Ember.A();
|
stepParams.fields = Ember.A();
|
||||||
|
|
||||||
if (present(stepJson.fields)) {
|
if (present(stepJson.fields)) {
|
||||||
stepJson.fields.forEach((f) => {
|
stepJson.fields.forEach((f) => {
|
||||||
stepParams.fields.pushObject(
|
let params = buildObject(f, 'field');
|
||||||
buildObject(f, 'field')
|
|
||||||
);
|
if (isAdvancedField(params)) {
|
||||||
|
params.showAdvanced = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
stepParams.fields.pushObject(params);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,9 +265,13 @@ function buildProperties(json) {
|
||||||
|
|
||||||
if (present(stepJson.actions)) {
|
if (present(stepJson.actions)) {
|
||||||
stepJson.actions.forEach((a) => {
|
stepJson.actions.forEach((a) => {
|
||||||
stepParams.actions.pushObject(
|
let params = buildObject(a, 'action');
|
||||||
buildObject(a, 'action')
|
|
||||||
);
|
if (isAdvancedAction(params)) {
|
||||||
|
params.showAdvanced = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
stepParams.actions.pushObject(params);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,4 @@
|
||||||
const mappedProperties = {
|
import EmberObject from "@ember/object";
|
||||||
wizard: [
|
|
||||||
'permitted'
|
|
||||||
],
|
|
||||||
step: [
|
|
||||||
'required_data',
|
|
||||||
'permitted_params'
|
|
||||||
],
|
|
||||||
field: [
|
|
||||||
'choices',
|
|
||||||
'prefill',
|
|
||||||
'content'
|
|
||||||
],
|
|
||||||
action: [
|
|
||||||
'title',
|
|
||||||
'category',
|
|
||||||
'tags',
|
|
||||||
'custom_fields',
|
|
||||||
'required',
|
|
||||||
'recipient',
|
|
||||||
'profile_updates',
|
|
||||||
'group'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inputs
|
// Inputs
|
||||||
|
|
||||||
|
@ -67,25 +44,28 @@ const connectors = {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectorItem(connector) {
|
|
||||||
return {
|
|
||||||
id: connector,
|
|
||||||
name: I18n.t(`admin.wizard.connector.${connector}`)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function defaultConnector(connectorType, inputType, opts = {}) {
|
function defaultConnector(connectorType, inputType, opts = {}) {
|
||||||
if (opts[`${connectorType}Connector`]) return opts[`${connectorType}Connector`];
|
if (opts[`${connectorType}Connector`]) return opts[`${connectorType}Connector`];
|
||||||
if (inputType === 'assignment') return 'set';
|
if (inputType === 'assignment' && connectorType === 'output') return 'set';
|
||||||
return connectorType === 'output' ? 'then' : 'equal';
|
if (inputType === 'conditional' && connectorType === 'output') return 'then';
|
||||||
|
if (inputType === 'conditional' && connectorType === 'pair') return 'equal';
|
||||||
|
if (inputType === 'pair') return 'equal';
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectorContent(connectorType, inputType, opts) {
|
function connectorContent(connectorType, inputType, opts) {
|
||||||
let connector = opts[`${connectorType}Connector`] || defaultConnector(connectorType, inputType, opts);
|
let connector = opts[`${connectorType}Connector`];
|
||||||
if (connector) return [connectorItem(connector)];
|
|
||||||
|
|
||||||
return connectors[connectorType].map(function(connector) {
|
if (!connector && connectorType === 'output') {
|
||||||
return connectorItem(connector);
|
connector = defaultConnector(connectorType, inputType, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = connector ? [connector] : connectors[connectorType];
|
||||||
|
|
||||||
|
return content.map(function(item) {
|
||||||
|
return {
|
||||||
|
id: item,
|
||||||
|
name: I18n.t(`admin.wizard.connector.${item}`)
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +93,7 @@ function defaultSelectionType(inputType, options = {}) {
|
||||||
if (inputTypes === true ||
|
if (inputTypes === true ||
|
||||||
((typeof inputTypes === 'string') &&
|
((typeof inputTypes === 'string') &&
|
||||||
inputTypes.split(',').indexOf(inputType) > -1)) {
|
inputTypes.split(',').indexOf(inputType) > -1)) {
|
||||||
|
|
||||||
type = t;
|
type = t;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -134,7 +115,7 @@ function newPair(inputType, options = {}) {
|
||||||
connector: defaultConnector('pair', inputType, options)
|
connector: defaultConnector('pair', inputType, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ember.Object.create(params);
|
return EmberObject.create(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
function newInput(options = {}) {
|
function newInput(options = {}) {
|
||||||
|
@ -160,11 +141,10 @@ function newInput(options = {}) {
|
||||||
params['connector'] = defaultConnector('output', inputType, options);
|
params['connector'] = defaultConnector('output', inputType, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ember.Object.create(params);
|
return EmberObject.create(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
mappedProperties,
|
|
||||||
defaultInputType,
|
defaultInputType,
|
||||||
defaultSelectionType,
|
defaultSelectionType,
|
||||||
connectorContent,
|
connectorContent,
|
|
@ -75,6 +75,7 @@ const actionProperties = [
|
||||||
'type',
|
'type',
|
||||||
'title',
|
'title',
|
||||||
'post',
|
'post',
|
||||||
|
'post_builder',
|
||||||
'post_template',
|
'post_template',
|
||||||
'category',
|
'category',
|
||||||
'tags',
|
'tags',
|
||||||
|
@ -98,6 +99,31 @@ const properties = {
|
||||||
action: actionProperties
|
action: actionProperties
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mappedProperties = {
|
||||||
|
wizard: [
|
||||||
|
'permitted'
|
||||||
|
],
|
||||||
|
step: [
|
||||||
|
'required_data',
|
||||||
|
'permitted_params'
|
||||||
|
],
|
||||||
|
field: [
|
||||||
|
'choices',
|
||||||
|
'prefill',
|
||||||
|
'content'
|
||||||
|
],
|
||||||
|
action: [
|
||||||
|
'title',
|
||||||
|
'category',
|
||||||
|
'tags',
|
||||||
|
'custom_fields',
|
||||||
|
'required',
|
||||||
|
'recipient',
|
||||||
|
'profile_updates',
|
||||||
|
'group'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
const actionTypes = [
|
const actionTypes = [
|
||||||
'create_topic',
|
'create_topic',
|
||||||
'update_profile',
|
'update_profile',
|
||||||
|
@ -116,6 +142,7 @@ export {
|
||||||
generateId,
|
generateId,
|
||||||
properties,
|
properties,
|
||||||
wizardProperties,
|
wizardProperties,
|
||||||
|
mappedProperties,
|
||||||
profileFields,
|
profileFields,
|
||||||
actionTypes
|
actionTypes
|
||||||
};
|
};
|
|
@ -1,6 +1,6 @@
|
||||||
import { ajax } from 'discourse/lib/ajax';
|
import { ajax } from 'discourse/lib/ajax';
|
||||||
import EmberObject from "@ember/object";
|
import EmberObject from "@ember/object";
|
||||||
import { buildStepJson, buildJson, buildProperties } from '../lib/json';
|
import { buildStepJson, buildJson, buildProperties } from '../lib/wizard-json';
|
||||||
|
|
||||||
const CustomWizard = EmberObject.extend({
|
const CustomWizard = EmberObject.extend({
|
||||||
save() {
|
save() {
|
||||||
|
@ -34,7 +34,6 @@ const CustomWizard = EmberObject.extend({
|
||||||
wizard: JSON.stringify(wizardJson)
|
wizard: JSON.stringify(wizardJson)
|
||||||
}
|
}
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
console.log('result: ', result);
|
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
reject(result);
|
reject(result);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {
|
||||||
generateSelectKitContent,
|
generateSelectKitContent,
|
||||||
profileFields,
|
profileFields,
|
||||||
generateName
|
generateName
|
||||||
} from '../lib/custom-wizard';
|
} from '../lib/wizard';
|
||||||
import DiscourseRoute from "discourse/routes/discourse";
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
|
|
||||||
export default DiscourseRoute.extend({
|
export default DiscourseRoute.extend({
|
||||||
|
@ -95,6 +95,7 @@ export default DiscourseRoute.extend({
|
||||||
setupController(controller, model) {
|
setupController(controller, model) {
|
||||||
const newWizard = this.get('newWizard');
|
const newWizard = this.get('newWizard');
|
||||||
const steps = model.get('steps') || [];
|
const steps = model.get('steps') || [];
|
||||||
|
|
||||||
controller.setProperties({
|
controller.setProperties({
|
||||||
newWizard,
|
newWizard,
|
||||||
model,
|
model,
|
||||||
|
|
|
@ -48,25 +48,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="wizard-settings">
|
<div class="wizard-settings">
|
||||||
<div class="setting">
|
|
||||||
<div class="setting-label">
|
|
||||||
<label>{{i18n 'admin.wizard.save_submissions'}}</label>
|
|
||||||
</div>
|
|
||||||
<div class="setting-value">
|
|
||||||
{{input type='checkbox' checked=model.save_submissions}}
|
|
||||||
<span>{{i18n 'admin.wizard.save_submissions_label'}}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting">
|
|
||||||
<div class="setting-label">
|
|
||||||
<label>{{i18n 'admin.wizard.multiple_submissions'}}</label>
|
|
||||||
</div>
|
|
||||||
<div class="setting-value">
|
|
||||||
{{input type='checkbox' checked=model.multiple_submissions}}
|
|
||||||
<span>{{i18n 'admin.wizard.multiple_submissions_label'}}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting">
|
<div class="setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
|
@ -90,11 +71,11 @@
|
||||||
|
|
||||||
<div class="setting">
|
<div class="setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<label>{{i18n 'admin.wizard.restart_on_revisit'}}</label>
|
<label>{{i18n 'admin.wizard.multiple_submissions'}}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-value">
|
<div class="setting-value">
|
||||||
{{input type='checkbox' checked=model.restart_on_revisit}}
|
{{input type='checkbox' checked=model.multiple_submissions}}
|
||||||
<span>{{i18n 'admin.wizard.restart_on_revisit_label'}}</span>
|
<span>{{i18n 'admin.wizard.multiple_submissions_label'}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -138,7 +119,36 @@
|
||||||
textSelection='key,value'
|
textSelection='key,value'
|
||||||
)}}
|
)}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{wizard-advanced-toggle showAdvanced=model.showAdvanced}}
|
||||||
|
|
||||||
|
{{#if model.showAdvanced}}
|
||||||
|
<div class="advanced-settings">
|
||||||
|
|
||||||
|
<div class="setting">
|
||||||
|
<div class="setting-label">
|
||||||
|
<label>{{i18n 'admin.wizard.save_submissions'}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-value">
|
||||||
|
{{input type='checkbox' checked=model.save_submissions}}
|
||||||
|
<span>{{i18n 'admin.wizard.save_submissions_label'}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting">
|
||||||
|
<div class="setting-label">
|
||||||
|
<label>{{i18n 'admin.wizard.restart_on_revisit'}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-value">
|
||||||
|
{{input type='checkbox' checked=model.restart_on_revisit}}
|
||||||
|
<span>{{i18n 'admin.wizard.restart_on_revisit_label'}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{wizard-links type="step" current=currentStep items=model.steps}}
|
{{wizard-links type="step" current=currentStep items=model.steps}}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{{d-button
|
||||||
|
action="toggleAdvanced"
|
||||||
|
label='admin.wizard.advanced'
|
||||||
|
class=toggleClass}}
|
|
@ -107,59 +107,7 @@
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if newTopicFields}}
|
|
||||||
<div class="setting full">
|
|
||||||
<div class="setting-label">
|
|
||||||
<label>{{i18n "admin.wizard.action.skip_redirect.label"}}</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-value">
|
|
||||||
{{input type='checkbox' checked=action.skip_redirect}}
|
|
||||||
|
|
||||||
<span>
|
|
||||||
{{i18n 'admin.wizard.action.skip_redirect.description' type='topic'}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if basicTopicFields}}
|
|
||||||
<div class="setting full field-mapper-setting">
|
|
||||||
<div class="setting-label">
|
|
||||||
<label>{{i18n 'admin.wizard.action.custom_fields.label'}}</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<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}}
|
|
||||||
|
|
||||||
{{#if sendMessage}}
|
{{#if sendMessage}}
|
||||||
<div class="setting full field-mapper-setting">
|
|
||||||
<div class="setting-label">
|
|
||||||
<label>{{i18n 'admin.wizard.required'}}</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-value">
|
|
||||||
{{wizard-mapper
|
|
||||||
inputs=action.required
|
|
||||||
options=(hash
|
|
||||||
textSelection='value'
|
|
||||||
wizardSelection=true
|
|
||||||
userSelection=true
|
|
||||||
groupSelection=true
|
|
||||||
)}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting full field-mapper-setting">
|
<div class="setting full field-mapper-setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<label>{{i18n "admin.wizard.action.send_message.recipient"}}</label>
|
<label>{{i18n "admin.wizard.action.send_message.recipient"}}</label>
|
||||||
|
@ -172,7 +120,7 @@
|
||||||
hasOutput=true
|
hasOutput=true
|
||||||
textSelection='value,output'
|
textSelection='value,output'
|
||||||
wizardSelection=true
|
wizardSelection=true
|
||||||
userSelection=true
|
userSelection='key,value'
|
||||||
groupSelection='key,value'
|
groupSelection='key,value'
|
||||||
)}}
|
)}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -279,14 +227,79 @@
|
||||||
{{input value=action.url}}
|
{{input value=action.url}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{{/if}}
|
||||||
<div class="setting">
|
|
||||||
<div class="setting-label">
|
{{#if hasAdvanced}}
|
||||||
<label>{{i18n "admin.wizard.action.route_to.code"}}</label>
|
{{wizard-advanced-toggle showAdvanced=action.showAdvanced}}
|
||||||
</div>
|
|
||||||
|
{{#if action.showAdvanced}}
|
||||||
<div class="setting-value">
|
<div class="advanced-settings">
|
||||||
{{input value=action.code}}
|
|
||||||
</div>
|
{{#if basicTopicFields}}
|
||||||
</div>
|
<div class="setting full field-mapper-setting">
|
||||||
|
<div class="setting-label">
|
||||||
|
<label>{{i18n 'admin.wizard.action.custom_fields.label'}}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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}}
|
||||||
|
|
||||||
|
{{#if sendMessage}}
|
||||||
|
<div class="setting full field-mapper-setting">
|
||||||
|
<div class="setting-label">
|
||||||
|
<label>{{i18n 'admin.wizard.required'}}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-value">
|
||||||
|
{{wizard-mapper
|
||||||
|
inputs=action.required
|
||||||
|
options=(hash
|
||||||
|
textSelection='value'
|
||||||
|
wizardSelection=true
|
||||||
|
userSelection=true
|
||||||
|
groupSelection=true
|
||||||
|
)}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if newTopicFields}}
|
||||||
|
<div class="setting full">
|
||||||
|
<div class="setting-label">
|
||||||
|
<label>{{i18n "admin.wizard.action.skip_redirect.label"}}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-value">
|
||||||
|
{{input type='checkbox' checked=action.skip_redirect}}
|
||||||
|
|
||||||
|
<span>
|
||||||
|
{{i18n 'admin.wizard.action.skip_redirect.description' type='topic'}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if routeTo}}
|
||||||
|
<div class="setting">
|
||||||
|
<div class="setting-label">
|
||||||
|
<label>{{i18n "admin.wizard.action.route_to.code"}}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-value">
|
||||||
|
{{input value=action.code}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -125,24 +125,6 @@
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if isCategory}}
|
|
||||||
<div class="setting">
|
|
||||||
<div class="setting-label">
|
|
||||||
<label>{{i18n 'admin.wizard.field.property'}}</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-value">
|
|
||||||
{{combo-box
|
|
||||||
value=field.property
|
|
||||||
content=categoryPropertyTypes
|
|
||||||
onChange=(action (mut field.property))
|
|
||||||
options=(hash
|
|
||||||
none='admin.wizard.select_property'
|
|
||||||
)}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if isCategoryOrTag}}
|
{{#if isCategoryOrTag}}
|
||||||
<div class="setting">
|
<div class="setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
|
@ -155,26 +137,52 @@
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if prefillEnabled}}
|
{{#if hasAdvanced}}
|
||||||
<div class="setting full field-mapper-setting">
|
{{wizard-advanced-toggle showAdvanced=field.showAdvanced}}
|
||||||
<div class="setting-label">
|
|
||||||
<label>{{i18n 'admin.wizard.field.prefill'}}</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-value">
|
{{#if field.showAdvanced}}
|
||||||
{{wizard-mapper inputs=field.prefill options=prefillOptions}}
|
<div class="advanced-settings">
|
||||||
|
{{#if isCategory}}
|
||||||
|
<div class="setting">
|
||||||
|
<div class="setting-label">
|
||||||
|
<label>{{i18n 'admin.wizard.field.property'}}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-value">
|
||||||
|
{{combo-box
|
||||||
|
value=field.property
|
||||||
|
content=categoryPropertyTypes
|
||||||
|
onChange=(action (mut field.property))
|
||||||
|
options=(hash
|
||||||
|
none='admin.wizard.select_property'
|
||||||
|
)}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if prefillEnabled}}
|
||||||
|
<div class="setting full field-mapper-setting">
|
||||||
|
<div class="setting-label">
|
||||||
|
<label>{{i18n 'admin.wizard.field.prefill'}}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-value">
|
||||||
|
{{wizard-mapper inputs=field.prefill options=prefillOptions}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if contentEnabled}}
|
||||||
|
<div class="setting full field-mapper-setting">
|
||||||
|
<div class="setting-label">
|
||||||
|
<label>{{i18n 'admin.wizard.field.content'}}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-value">
|
||||||
|
{{wizard-mapper inputs=field.content options=contentOptions}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{{/if}}
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if contentEnabled}}
|
|
||||||
<div class="setting full field-mapper-setting">
|
|
||||||
<div class="setting-label">
|
|
||||||
<label>{{i18n 'admin.wizard.field.content'}}</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-value">
|
|
||||||
{{wizard-mapper inputs=field.content options=contentOptions}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -46,44 +46,51 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="setting full field-mapper-setting">
|
{{wizard-advanced-toggle showAdvanced=step.showAdvanced}}
|
||||||
<div class="setting-label">
|
|
||||||
<label>{{i18n 'admin.wizard.step.required_data.label'}}</label>
|
{{#if step.showAdvanced}}
|
||||||
</div>
|
<div class="advanced-settings">
|
||||||
<div class="setting-value">
|
|
||||||
{{wizard-mapper
|
<div class="setting full field-mapper-setting">
|
||||||
inputs=step.required_data
|
<div class="setting-label">
|
||||||
keyPlaceholder="admin.wizard.submission_key"
|
<label>{{i18n 'admin.wizard.step.required_data.label'}}</label>
|
||||||
options=(hash
|
</div>
|
||||||
pairConnector='equal'
|
<div class="setting-value">
|
||||||
wizardSelection='value'
|
{{wizard-mapper
|
||||||
userSelection='value'
|
inputs=step.required_data
|
||||||
)}}
|
keyPlaceholder="admin.wizard.submission_key"
|
||||||
{{#if step.required_data}}
|
options=(hash
|
||||||
<div class="required-data-message">
|
wizardSelection='value'
|
||||||
<div class="label">
|
userSelection='value'
|
||||||
{{i18n 'admin.wizard.step.required_data.not_permitted_message'}}
|
)}}
|
||||||
</div>
|
{{#if step.required_data}}
|
||||||
{{input value=step.required_data_message}}
|
<div class="required-data-message">
|
||||||
</div>
|
<div class="label">
|
||||||
{{/if}}
|
{{i18n 'admin.wizard.step.required_data.not_permitted_message'}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{{input value=step.required_data_message}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting full field-mapper-setting">
|
||||||
|
<div class="setting-label">
|
||||||
|
<label>{{i18n 'admin.wizard.step.permitted_params.label'}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-value">
|
||||||
|
{{wizard-mapper
|
||||||
|
inputs=step.permitted_params
|
||||||
|
keyPlaceholder='admin.wizard.param_key'
|
||||||
|
valuePlaceholder='admin.wizard.submission_key'
|
||||||
|
options=(hash
|
||||||
|
pairConnector='set'
|
||||||
|
)}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="setting full field-mapper-setting">
|
|
||||||
<div class="setting-label">
|
|
||||||
<label>{{i18n 'admin.wizard.step.permitted_params.label'}}</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-value">
|
{{/if}}
|
||||||
{{wizard-mapper
|
|
||||||
inputs=step.permitted_params
|
|
||||||
keyPlaceholder='admin.wizard.param_key'
|
|
||||||
valuePlaceholder='admin.wizard.submission_key'
|
|
||||||
options=(hash
|
|
||||||
pairConnector='set'
|
|
||||||
)}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{wizard-links type="field" current=currentField items=step.fields}}
|
{{wizard-links type="field" current=currentField items=step.fields}}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
{{#if single}}
|
||||||
|
<span class="connector-single">
|
||||||
|
{{connectorLabel}}
|
||||||
|
</span>
|
||||||
|
{{else}}
|
||||||
|
{{combo-box
|
||||||
|
value=connector
|
||||||
|
content=connectors
|
||||||
|
onChange=(action (mut connector))}}
|
||||||
|
{{/if}}
|
|
@ -1,10 +1,8 @@
|
||||||
{{#if hasOutput}}
|
{{#if hasOutput}}
|
||||||
<div class="mapper-connector mapper-block">
|
{{wizard-mapper-connector
|
||||||
{{combo-box
|
connector=input.type
|
||||||
value=input.type
|
connectors=inputTypes
|
||||||
content=inputTypes
|
inputTypes=true}}
|
||||||
onChange=(action (mut input.type))}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if hasPairs}}
|
{{#if hasPairs}}
|
||||||
|
@ -30,12 +28,9 @@
|
||||||
|
|
||||||
{{#if hasOutput}}
|
{{#if hasOutput}}
|
||||||
{{#if hasPairs}}
|
{{#if hasPairs}}
|
||||||
<div class="mapper-connector mapper-block">
|
{{wizard-mapper-connector
|
||||||
{{combo-box
|
connector=input.connector
|
||||||
value=input.connector
|
connectors=connectors}}
|
||||||
content=connectors
|
|
||||||
onChange=(action (mut input.connector))}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div class="output mapper-block">
|
<div class="output mapper-block">
|
||||||
|
|
|
@ -8,12 +8,9 @@
|
||||||
options=options}}
|
options=options}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mapper-connector mapper-block">
|
{{wizard-mapper-connector
|
||||||
{{combo-box
|
connector=pair.connector
|
||||||
value=pair.connector
|
connectors=connectors}}
|
||||||
content=connectors
|
|
||||||
onChange=(action (mut pair.connector))}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="value mapper-block">
|
<div class="value mapper-block">
|
||||||
{{wizard-mapper-selector
|
{{wizard-mapper-selector
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { default as computed, observes } from 'discourse-common/utils/decorators';
|
import { default as computed, observes } from 'discourse-common/utils/decorators';
|
||||||
|
import EmberObject from "@ember/object";
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
showPreview: false,
|
showPreview: false,
|
||||||
|
@ -6,7 +7,7 @@ export default Ember.Component.extend({
|
||||||
classNameBindings: ["showPreview:show-preview:hide-preview"],
|
classNameBindings: ["showPreview:show-preview:hide-preview"],
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
this.set('composer', Ember.Object.create({
|
this.set('composer', EmberObject.create({
|
||||||
loading: false,
|
loading: false,
|
||||||
reply: this.get('field.value')
|
reply: this.get('field.value')
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -3,15 +3,15 @@ import getUrl from 'discourse-common/lib/get-url';
|
||||||
import WizardField from 'wizard/models/wizard-field';
|
import WizardField from 'wizard/models/wizard-field';
|
||||||
import { ajax } from 'wizard/lib/ajax';
|
import { ajax } from 'wizard/lib/ajax';
|
||||||
import Step from 'wizard/models/step';
|
import Step from 'wizard/models/step';
|
||||||
|
import EmberObject from "@ember/object";
|
||||||
|
|
||||||
const CustomWizard = Ember.Object.extend({
|
const CustomWizard = EmberObject.extend({
|
||||||
@computed('steps.length')
|
@computed('steps.length')
|
||||||
totalSteps: length => length,
|
totalSteps: length => length,
|
||||||
|
|
||||||
skip() {
|
skip() {
|
||||||
if (this.get('required') && (!this.get('completed') && this.get('permitted'))) return;
|
if (this.required && (!this.completed && this.permitted)) return;
|
||||||
const id = this.get('id');
|
CustomWizard.skip(this.id);
|
||||||
CustomWizard.skip(id);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ export function findCustomWizard(wizardId, params = {}) {
|
||||||
subcatMap[c.parent_category_id] || [];
|
subcatMap[c.parent_category_id] || [];
|
||||||
subcatMap[c.parent_category_id].push(c.id);
|
subcatMap[c.parent_category_id].push(c.id);
|
||||||
}
|
}
|
||||||
return (categoriesById[c.id] = Ember.Object.create(c));
|
return (categoriesById[c.id] = EmberObject.create(c));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Associate the categories with their parents
|
// Associate the categories with their parents
|
||||||
|
|
|
@ -4,6 +4,12 @@
|
||||||
|
|
||||||
$setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
|
$setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
|
||||||
|
|
||||||
|
body.admin-wizard {
|
||||||
|
.boxed.white {
|
||||||
|
background-color: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.wizard-list {
|
.wizard-list {
|
||||||
float: left;
|
float: left;
|
||||||
width: 250px;
|
width: 250px;
|
||||||
|
@ -12,8 +18,8 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
|
||||||
|
|
||||||
.wizard-settings-parent {
|
.wizard-settings-parent {
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
padding: 30px;
|
padding: 20px;
|
||||||
background-color: $setting-background;
|
border: 1px solid $primary-medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wizard-settings-group {
|
.wizard-settings-group {
|
||||||
|
@ -32,10 +38,29 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
|
||||||
|
|
||||||
.wizard-basic-details,
|
.wizard-basic-details,
|
||||||
.wizard-custom-field,
|
.wizard-custom-field,
|
||||||
.wizard-custom-action {
|
.wizard-custom-action,
|
||||||
|
.advanced-settings {
|
||||||
@extend .wizard-settings-group;
|
@extend .wizard-settings-group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wizard-custom-field,
|
||||||
|
.wizard-custom-action {
|
||||||
|
position: relative;
|
||||||
|
background: transparent;
|
||||||
|
background-color: $setting-background;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard-links {
|
||||||
|
&.action, &.field {
|
||||||
|
margin-top: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard-settings > .advanced-settings > div.setting {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.admin-wizard.settings .wizard-basic-details {
|
.admin-wizard.settings .wizard-basic-details {
|
||||||
justify-content: initial;
|
justify-content: initial;
|
||||||
|
|
||||||
|
@ -106,6 +131,11 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
|
||||||
width: 48%;
|
width: 48%;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.setting-label {
|
.setting-label {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
|
@ -144,6 +174,7 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
|
||||||
|
|
||||||
input[type="number"] {
|
input[type="number"] {
|
||||||
width: 70px;
|
width: 70px;
|
||||||
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
input.medium {
|
input.medium {
|
||||||
|
@ -198,9 +229,8 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
input[type='checkbox'],
|
input {
|
||||||
span {
|
margin: 0 7px 0 0;
|
||||||
margin: 0 10px 0 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
|
@ -218,6 +248,15 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.advanced-settings {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 30px;
|
||||||
|
|
||||||
|
[class~='setting']:first-of-type {
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.wizard-custom-action > [class~='setting']:last-of-type {
|
.wizard-custom-action > [class~='setting']:last-of-type {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
@ -365,11 +404,11 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
|
||||||
|
|
||||||
.admin-contents .wizard-submissions {
|
.admin-contents .wizard-submissions {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: inline-block;
|
margin-top: 10px;
|
||||||
|
margin-left: 30px;
|
||||||
|
|
||||||
table {
|
table {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
position: absolute;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -470,3 +509,11 @@ $setting-background: dark-light-diff($primary, $secondary, 96%, -65%);
|
||||||
height: unset !important;
|
height: unset !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wizard-advanced-toggle {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-mapper-input .btn, .btn-after-time, .wizard-editor-gutter .btn {
|
||||||
|
background-color: $secondary;
|
||||||
|
border: 1px solid $primary-medium;
|
||||||
|
}
|
||||||
|
|
|
@ -52,11 +52,6 @@
|
||||||
|
|
||||||
.add-mapper-input {
|
.add-mapper-input {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
.btn {
|
|
||||||
background-color: $secondary;
|
|
||||||
border: 1px solid $primary-medium;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapper-input + .add-mapper-input {
|
.mapper-input + .add-mapper-input {
|
||||||
|
@ -68,23 +63,27 @@
|
||||||
min-width: 40px;
|
min-width: 40px;
|
||||||
|
|
||||||
.select-kit .select-kit-header {
|
.select-kit .select-kit-header {
|
||||||
padding: 0 0.5em;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
.caret-icon {
|
&.single {
|
||||||
display: none;
|
height: 28px;
|
||||||
}
|
background: $secondary;
|
||||||
|
border: 1px solid $primary-medium;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapper-selector {
|
.mapper-selector {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 160px;
|
max-width: 150px;
|
||||||
min-width: 160px;
|
min-width: 150px;
|
||||||
|
|
||||||
input, .select-kit {
|
input, .select-kit {
|
||||||
width: 160px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.type-selector {
|
.type-selector {
|
||||||
|
|
|
@ -60,6 +60,7 @@ en:
|
||||||
param_key: 'param'
|
param_key: 'param'
|
||||||
group: "Group"
|
group: "Group"
|
||||||
permitted: "Permitted"
|
permitted: "Permitted"
|
||||||
|
advanced: "Advanced"
|
||||||
|
|
||||||
editor:
|
editor:
|
||||||
show: "Show"
|
show: "Show"
|
||||||
|
|
|
@ -20,144 +20,69 @@ class CustomWizard::Action
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_topic
|
def create_topic
|
||||||
if action['custom_title_enabled']
|
params = basic_topic_params
|
||||||
title = mapper.interpolate(action['custom_title'])
|
|
||||||
else
|
|
||||||
title = data[action['title']]
|
|
||||||
end
|
|
||||||
|
|
||||||
if action['post_builder']
|
byebug
|
||||||
post = mapper.interpolate(action['post_template'])
|
|
||||||
else
|
|
||||||
post = data[action['post']]
|
|
||||||
end
|
|
||||||
|
|
||||||
if title
|
if params[:title] && params[:raw]
|
||||||
params = {
|
params[:category] = action_category
|
||||||
title: title,
|
params[:tags] = action_tags
|
||||||
raw: post,
|
|
||||||
skip_validations: true
|
|
||||||
}
|
|
||||||
|
|
||||||
params[:category] = action_category_id(action, data)
|
byebug
|
||||||
tags = action_tags(action, data)
|
|
||||||
params[:tags] = tags
|
|
||||||
|
|
||||||
if action['add_fields']
|
|
||||||
action['add_fields'].each do |field|
|
|
||||||
value = field['value_custom'].present? ? field['value_custom'] : data[field['value']]
|
|
||||||
key = field['key']
|
|
||||||
|
|
||||||
if key && (value.present? || value === false)
|
|
||||||
if key.include?('custom_fields')
|
|
||||||
keyArr = key.split('.')
|
|
||||||
|
|
||||||
if keyArr.length === 3
|
|
||||||
custom_key = keyArr.last
|
|
||||||
type = keyArr.first
|
|
||||||
|
|
||||||
if type === 'topic'
|
|
||||||
params[:topic_opts] ||= {}
|
|
||||||
params[:topic_opts][:custom_fields] ||= {}
|
|
||||||
params[:topic_opts][:custom_fields][custom_key] = value
|
|
||||||
elsif type === 'post'
|
|
||||||
params[:custom_fields] ||= {}
|
|
||||||
params[:custom_fields][custom_key.to_sym] = value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
value = [*value] + [*tags] if key === 'tags'
|
|
||||||
params[key.to_sym] = value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
creator = PostCreator.new(user, params)
|
creator = PostCreator.new(user, params)
|
||||||
post = creator.create
|
post = creator.create
|
||||||
|
|
||||||
if creator.errors.present?
|
if creator.errors.present?
|
||||||
updater.errors.add(:create_topic, creator.errors.full_messages.join(" "))
|
updater.errors.add(:create_topic, creator.errors.full_messages.join(" "))
|
||||||
else
|
elsif action['skip_redirect'].blank?
|
||||||
|
data['redirect_on_complete'] = post.topic.url
|
||||||
unless action['skip_redirect']
|
|
||||||
data['redirect_on_complete'] = post.topic.url
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def send_message
|
def send_message
|
||||||
if action['required'].present? && data[action['required']].blank?
|
return if action['required'].present? && data[action['required']].blank?
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if action['custom_title_enabled']
|
params = basic_topic_params
|
||||||
title = mapper.interpolate(action['custom_title'])
|
params[:target_usernames] = CustomWizard::Mapper.new(
|
||||||
else
|
inputs: action['recipient'],
|
||||||
title = data[action['title']]
|
data: data,
|
||||||
end
|
user: user,
|
||||||
|
opts: {
|
||||||
|
multiple: true
|
||||||
|
}
|
||||||
|
).output
|
||||||
|
|
||||||
if action['post_builder']
|
if params[:title] && params[:raw]
|
||||||
post = mapper.interpolate(action['post_template'])
|
params[:archetype] = Archetype.private_message
|
||||||
else
|
|
||||||
post = data[action['post']]
|
|
||||||
end
|
|
||||||
|
|
||||||
if title && post
|
|
||||||
creator = PostCreator.new(user,
|
|
||||||
title: title,
|
|
||||||
raw: post,
|
|
||||||
archetype: Archetype.private_message,
|
|
||||||
target_usernames: action['username']
|
|
||||||
)
|
|
||||||
|
|
||||||
|
creator = PostCreator.new(user, params)
|
||||||
post = creator.create
|
post = creator.create
|
||||||
|
|
||||||
if creator.errors.present?
|
if creator.errors.present?
|
||||||
updater.errors.add(:send_message, creator.errors.full_messages.join(" "))
|
updater.errors.add(:send_message, creator.errors.full_messages.join(" "))
|
||||||
else
|
elsif action['skip_redirect'].blank?
|
||||||
unless action['skip_redirect']
|
data['redirect_on_complete'] = post.topic.url
|
||||||
data['redirect_on_complete'] = post.topic.url
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_profile
|
def update_profile
|
||||||
return unless action['profile_updates'].length
|
return unless (profile_updates = action['profile_updates']).length
|
||||||
|
|
||||||
attributes = {}
|
attributes = { custom_fields: {} }
|
||||||
custom_fields = {}
|
|
||||||
|
|
||||||
action['profile_updates'].each do |pu|
|
profile_updates.each do |pu|
|
||||||
value = pu['value']
|
pair = field['pairs'].first
|
||||||
key = pu['key']
|
field = mapper.map_field(pair['key'], pair['key_type'])
|
||||||
|
value = mapper.map_field(pair['value'], pair['value_type'])
|
||||||
|
|
||||||
return if data[key].blank?
|
if field.include?("custom_field")
|
||||||
|
attributes[:custom_fields][field] = value
|
||||||
if user_field || custom_field
|
|
||||||
custom_fields[user_field || custom_field] = data[key]
|
|
||||||
else
|
else
|
||||||
updater_key = value
|
attributes[field.to_sym] = value
|
||||||
if ['profile_background', 'card_background'].include?(value)
|
|
||||||
updater_key = "#{value}_upload_url"
|
|
||||||
end
|
|
||||||
attributes[updater_key.to_sym] = data[key] if updater_key
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if ['user_avatar'].include?(value)
|
|
||||||
this_upload_id = data[key][:id]
|
|
||||||
user.create_user_avatar unless user.user_avatar
|
|
||||||
user.user_avatar.custom_upload_id = this_upload_id
|
|
||||||
user.uploaded_avatar_id = this_upload_id
|
|
||||||
user.save!
|
|
||||||
user.user_avatar.save!
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if custom_fields.present?
|
|
||||||
attributes[:custom_fields] = custom_fields
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if attributes.present?
|
if attributes.present?
|
||||||
|
@ -205,7 +130,7 @@ class CustomWizard::Action
|
||||||
|
|
||||||
url += "&body=#{post}"
|
url += "&body=#{post}"
|
||||||
|
|
||||||
if category_id = action_category_id
|
if category_id = action_category
|
||||||
if category = Category.find(category_id)
|
if category = Category.find(category_id)
|
||||||
url += "&category=#{category.full_slug('/')}"
|
url += "&category=#{category.full_slug('/')}"
|
||||||
end
|
end
|
||||||
|
@ -249,35 +174,92 @@ class CustomWizard::Action
|
||||||
|
|
||||||
def route_to
|
def route_to
|
||||||
url = mapper.interpolate(action['url'])
|
url = mapper.interpolate(action['url'])
|
||||||
|
|
||||||
if action['code']
|
if action['code']
|
||||||
data[action['code']] = SecureRandom.hex(8)
|
data[action['code']] = SecureRandom.hex(8)
|
||||||
url += "&#{action['code']}=#{data[action['code']]}"
|
url += "&#{action['code']}=#{data[action['code']]}"
|
||||||
end
|
end
|
||||||
|
|
||||||
data['route_to'] = URI.encode(url)
|
data['route_to'] = URI.encode(url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def action_category_id
|
private
|
||||||
if action['custom_category_enabled']
|
|
||||||
if action['custom_category_wizard_field']
|
def action_category
|
||||||
data[action['category_id']]
|
output = CustomWizard::Mapper.new(
|
||||||
elsif action['custom_category_user_field_key']
|
inputs: action['category'],
|
||||||
if action['custom_category_user_field_key'].include?('custom_fields')
|
data: data,
|
||||||
field = action['custom_category_user_field_key'].split('.').last
|
user: user
|
||||||
user.custom_fields[field]
|
).output
|
||||||
else
|
|
||||||
user.send(action['custom_category_user_field_key'])
|
if output.is_a?(Array)
|
||||||
end
|
output.first
|
||||||
end
|
elsif output.is_a?(Integer)
|
||||||
else
|
output
|
||||||
action['category_id']
|
elsif output.is_a?(String)
|
||||||
|
output.to_i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def action_tags
|
def action_tags
|
||||||
if action['custom_tag_enabled']
|
output = CustomWizard::Mapper.new(
|
||||||
data[action['custom_tag_field']]
|
inputs: action['tags'],
|
||||||
else
|
data: data,
|
||||||
action['tags']
|
user: user,
|
||||||
|
).output
|
||||||
|
|
||||||
|
if output.is_a?(Array)
|
||||||
|
output.flatten
|
||||||
|
elsif output.is_a?(Integer)
|
||||||
|
[*output]
|
||||||
|
elsif output.is_a?(String)
|
||||||
|
[*output.to_i]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def add_custom_fields(params = {})
|
||||||
|
if (custom_fields = action['custom_fields']).present?
|
||||||
|
custom_fields.each do |field|
|
||||||
|
pair = field['pairs'].first
|
||||||
|
value = mapper.map_field(pair['key'], pair['key_type'])
|
||||||
|
key = mapper.map_field(pair['value'], pair['value_type'])
|
||||||
|
|
||||||
|
if key &&
|
||||||
|
value.present? &&
|
||||||
|
(keyArr = key.split('.')).length === 2
|
||||||
|
|
||||||
|
if keyArr.first === 'topic'
|
||||||
|
params[:topic_opts] ||= {}
|
||||||
|
params[:topic_opts][:custom_fields] ||= {}
|
||||||
|
params[:topic_opts][:custom_fields][keyArr.last] = value
|
||||||
|
elsif keyArr.first === 'post'
|
||||||
|
params[:custom_fields] ||= {}
|
||||||
|
params[:custom_fields][keyArr.last.to_sym] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
params
|
||||||
|
end
|
||||||
|
|
||||||
|
def basic_topic_params
|
||||||
|
params = {
|
||||||
|
skip_validations: true
|
||||||
|
}
|
||||||
|
|
||||||
|
params[:title] = CustomWizard::Mapper.new(
|
||||||
|
inputs: action['title'],
|
||||||
|
data: data,
|
||||||
|
user: user
|
||||||
|
).output
|
||||||
|
|
||||||
|
params[:raw] = action['post_builder'] ?
|
||||||
|
mapper.interpolate(action['post_template']) :
|
||||||
|
data[action['post']]
|
||||||
|
|
||||||
|
params = add_custom_fields(params)
|
||||||
|
|
||||||
|
params
|
||||||
|
end
|
||||||
end
|
end
|
Laden …
In neuem Issue referenzieren