0
0
Fork 1
Spiegel von https://github.com/paviliondev/discourse-custom-wizard.git synchronisiert 2024-11-26 02:50:28 +01:00
Dieser Commit ist enthalten in:
Angus McLeod 2020-04-02 16:21:57 +11:00
Ursprung 694f5f1898
Commit 87a53a8c85
32 geänderte Dateien mit 600 neuen und 429 gelöschten Zeilen

Datei anzeigen

@ -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');
}
}
})

Datei anzeigen

@ -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) {

Datei anzeigen

@ -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',

Datei anzeigen

@ -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)"
}) })

Datei anzeigen

@ -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);
}, },

Datei anzeigen

@ -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}`);
})
});

Datei anzeigen

@ -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'],

Datei anzeigen

@ -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";

Datei anzeigen

@ -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',

Datei anzeigen

@ -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({

Datei anzeigen

@ -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({

Datei anzeigen

@ -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');
}
} }
}); });

Datei anzeigen

@ -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'],

Datei anzeigen

@ -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,10 +151,9 @@ 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 = {}
@ -183,7 +181,6 @@ function buildObject(json, type) {
EmberObject.create(input) 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);
}); });
} }

Datei anzeigen

@ -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,

Datei anzeigen

@ -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
}; };

Datei anzeigen

@ -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 {

Datei anzeigen

@ -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,

Datei anzeigen

@ -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}}

Datei anzeigen

@ -0,0 +1,4 @@
{{d-button
action="toggleAdvanced"
label='admin.wizard.advanced'
class=toggleClass}}

Datei anzeigen

@ -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,7 +227,69 @@
{{input value=action.url}} {{input value=action.url}}
</div> </div>
</div> </div>
{{/if}}
{{#if hasAdvanced}}
{{wizard-advanced-toggle showAdvanced=action.showAdvanced}}
{{#if action.showAdvanced}}
<div class="advanced-settings">
{{#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}}
<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">
<div class="setting-label"> <div class="setting-label">
<label>{{i18n "admin.wizard.action.route_to.code"}}</label> <label>{{i18n "admin.wizard.action.route_to.code"}}</label>
@ -290,3 +300,6 @@
</div> </div>
</div> </div>
{{/if}} {{/if}}
</div>
{{/if}}
{{/if}}

Datei anzeigen

@ -125,6 +125,23 @@
</div> </div>
{{/if}} {{/if}}
{{#if isCategoryOrTag}}
<div class="setting">
<div class="setting-label">
<label>{{i18n 'admin.wizard.field.limit'}}</label>
</div>
<div class="setting-value">
{{input type="number" value=field.limit}}
</div>
</div>
{{/if}}
{{#if hasAdvanced}}
{{wizard-advanced-toggle showAdvanced=field.showAdvanced}}
{{#if field.showAdvanced}}
<div class="advanced-settings">
{{#if isCategory}} {{#if isCategory}}
<div class="setting"> <div class="setting">
<div class="setting-label"> <div class="setting-label">
@ -143,18 +160,6 @@
</div> </div>
{{/if}} {{/if}}
{{#if isCategoryOrTag}}
<div class="setting">
<div class="setting-label">
<label>{{i18n 'admin.wizard.field.limit'}}</label>
</div>
<div class="setting-value">
{{input type="number" value=field.limit}}
</div>
</div>
{{/if}}
{{#if prefillEnabled}} {{#if prefillEnabled}}
<div class="setting full field-mapper-setting"> <div class="setting full field-mapper-setting">
<div class="setting-label"> <div class="setting-label">
@ -178,3 +183,6 @@
</div> </div>
</div> </div>
{{/if}} {{/if}}
</div>
{{/if}}
{{/if}}

Datei anzeigen

@ -46,6 +46,11 @@
</div> </div>
</div> </div>
{{wizard-advanced-toggle showAdvanced=step.showAdvanced}}
{{#if step.showAdvanced}}
<div class="advanced-settings">
<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.step.required_data.label'}}</label> <label>{{i18n 'admin.wizard.step.required_data.label'}}</label>
@ -55,7 +60,6 @@
inputs=step.required_data inputs=step.required_data
keyPlaceholder="admin.wizard.submission_key" keyPlaceholder="admin.wizard.submission_key"
options=(hash options=(hash
pairConnector='equal'
wizardSelection='value' wizardSelection='value'
userSelection='value' userSelection='value'
)}} )}}
@ -85,6 +89,9 @@
</div> </div>
</div> </div>
</div>
{{/if}}
{{wizard-links type="field" current=currentField items=step.fields}} {{wizard-links type="field" current=currentField items=step.fields}}
{{#if currentField}} {{#if currentField}}

Datei anzeigen

@ -0,0 +1,10 @@
{{#if single}}
<span class="connector-single">
{{connectorLabel}}
</span>
{{else}}
{{combo-box
value=connector
content=connectors
onChange=(action (mut connector))}}
{{/if}}

Datei anzeigen

@ -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">

Datei anzeigen

@ -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

Datei anzeigen

@ -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')
})) }))

Datei anzeigen

@ -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

Datei anzeigen

@ -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;
}

Datei anzeigen

@ -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 {
display: none;
} }
&.single {
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 {

Datei anzeigen

@ -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"

Datei anzeigen

@ -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?
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 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 end
attributes[updater_key.to_sym] = data[key] if updater_key
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
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
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