diff --git a/assets/javascripts/discourse/components/wizard-advanced-toggle.js.es6 b/assets/javascripts/discourse/components/wizard-advanced-toggle.js.es6
new file mode 100644
index 00000000..51b9948c
--- /dev/null
+++ b/assets/javascripts/discourse/components/wizard-advanced-toggle.js.es6
@@ -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');
+ }
+ }
+})
\ No newline at end of file
diff --git a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 b/assets/javascripts/discourse/components/wizard-custom-action.js.es6
index f842a8a6..8f5a6103 100644
--- a/assets/javascripts/discourse/components/wizard-custom-action.js.es6
+++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6
@@ -1,92 +1,44 @@
-import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
+import { default as discourseComputed, observes, on } from 'discourse-common/utils/decorators';
+import { equal, empty, or } from "@ember/object/computed";
+import { generateName, selectKitContent, schema } from '../lib/wizard';
+import Component from "@ember/component";
-const ACTION_TYPES = [
- { id: 'create_topic', name: 'Create Topic' },
- { id: 'update_profile', name: 'Update Profile' },
- { id: 'send_message', name: 'Send Message' },
- { id: 'send_to_api', name: 'Send to API' },
- { id: 'add_to_group', name: 'Add to Group' },
- { id: 'route_to', name: 'Route To' },
- { id: 'open_composer', name: 'Open Composer' }
-];
-
-const PROFILE_FIELDS = [
- 'name',
- 'user_avatar',
- 'date_of_birth',
- 'title',
- 'locale',
- 'location',
- 'website',
- 'bio_raw',
- 'profile_background',
- 'card_background',
- 'theme_id'
-];
-
-export default Ember.Component.extend({
+export default Component.extend({
classNames: 'wizard-custom-action',
- types: ACTION_TYPES,
- profileFields: PROFILE_FIELDS,
- createTopic: Ember.computed.equal('action.type', 'create_topic'),
- updateProfile: Ember.computed.equal('action.type', 'update_profile'),
- sendMessage: Ember.computed.equal('action.type', 'send_message'),
- sendToApi: Ember.computed.equal('action.type', 'send_to_api'),
- apiEmpty: Ember.computed.empty('action.api'),
- addToGroup: Ember.computed.equal('action.type', 'add_to_group'),
- routeTo: Ember.computed.equal('action.type', 'route_to'),
- disableId: Ember.computed.not('action.isNew'),
-
- @computed('action.type')
- basicTopicFields(actionType) {
- return ['create_topic', 'send_message', 'open_composer'].indexOf(actionType) > -1;
- },
-
- @computed('action.type')
- publicTopicFields(actionType) {
- return ['create_topic', 'open_composer'].indexOf(actionType) > -1;
- },
-
- @computed('action.type')
- newTopicFields(actionType) {
- return ['create_topic', 'send_message'].indexOf(actionType) > -1;
- },
-
- @computed('availableFields')
- builderWizardFields(fields) {
- return fields.map((f) => ` w{${f.id}}`);
- },
+ actionTypes: Object.keys(schema.action.types).map(t => ({ id: t, name: generateName(t) })),
+ createTopic: equal('action.type', 'create_topic'),
+ updateProfile: equal('action.type', 'update_profile'),
+ sendMessage: equal('action.type', 'send_message'),
+ openComposer: equal('action.type', 'open_composer'),
+ sendToApi: equal('action.type', 'send_to_api'),
+ addToGroup: equal('action.type', 'add_to_group'),
+ routeTo: equal('action.type', 'route_to'),
+ apiEmpty: empty('action.api'),
+ groupPropertyTypes: selectKitContent(['id', 'name']),
+ hasAdvanced: or('hasCustomFields', 'routeTo'),
+ hasCustomFields: or('basicTopicFields', 'updateProfile'),
+ basicTopicFields: or('createTopic', 'sendMessage', 'openComposer'),
+ publicTopicFields: or('createTopic', 'openComposer'),
+ showSkipRedirect: or('createTopic', 'sendMessage'),
- @computed('availableFields')
- categoryFields(fields) {
- return fields.filter(f => f.type == 'category');
- },
-
- @computed('availableFields')
- tagFields(fields) {
- return fields.filter(f => f.type == 'tag');
+ @discourseComputed('wizard.steps')
+ runAfterContent(steps) {
+ let content = steps.map(function(step) {
+ return {
+ id: step.id,
+ name: step.title || step.id
+ };
+ });
+
+ content.unshift({
+ id: 'wizard_completion',
+ name: I18n.t('admin.wizard.action.run_after.wizard_completion')
+ });
+
+ return content;
},
- @computed()
- builderUserFields() {
- const noTheme = PROFILE_FIELDS.filter((f) => f !== 'theme_id');
- const fields = noTheme.concat(['email', 'username']);
- return fields.map((f) => ` u{${f}}`);
- },
-
- @observes('action.custom_category_wizard_field')
- toggleCustomCategoryUserField() {
- const wizard = this.get('action.custom_category_wizard_field');
- if (wizard) this.set('action.custom_category_user_field', false);
- },
-
- @observes('action.custom_category_user_field')
- toggleCustomCategoryWizardField() {
- const user = this.get('action.custom_category_user_field');
- if (user) this.set('action.custom_category_wizard_field', false);
- },
-
- @computed('wizard.apis')
+ @discourseComputed('wizard.apis')
availableApis(apis) {
return apis.map(a => {
return {
@@ -96,7 +48,7 @@ export default Ember.Component.extend({
});
},
- @computed('wizard.apis', 'action.api')
+ @discourseComputed('wizard.apis', 'action.api')
availableEndpoints(apis, api) {
if (!api) return [];
return apis.find(a => a.name === api).endpoints;
diff --git a/assets/javascripts/discourse/components/wizard-custom-field.js.es6 b/assets/javascripts/discourse/components/wizard-custom-field.js.es6
index 66bcd014..e5456806 100644
--- a/assets/javascripts/discourse/components/wizard-custom-field.js.es6
+++ b/assets/javascripts/discourse/components/wizard-custom-field.js.es6
@@ -1,47 +1,97 @@
-import { default as computed, observes, on } from 'ember-addons/ember-computed-decorators';
-import { generateSelectKitContent } from '../lib/custom-wizard';
+import { default as discourseComputed, observes } from 'discourse-common/utils/decorators';
+import { equal, or } from "@ember/object/computed";
+import { selectKitContent, schema } from '../lib/wizard';
+import Component from "@ember/component";
-export default Ember.Component.extend({
+export default Component.extend({
classNames: 'wizard-custom-field',
- isDropdown: Ember.computed.equal('field.type', 'dropdown'),
- isUpload: Ember.computed.equal('field.type', 'upload'),
- isCategory: Ember.computed.equal('field.type', 'category'),
- disableId: Ember.computed.not('field.isNew'),
- choicesTypes: generateSelectKitContent(['translation', 'preset', 'custom']),
- choicesTranslation: Ember.computed.equal('field.choices_type', 'translation'),
- choicesPreset: Ember.computed.equal('field.choices_type', 'preset'),
- choicesCustom: Ember.computed.equal('field.choices_type', 'custom'),
- categoryPropertyTypes: generateSelectKitContent(['id', 'slug']),
-
- @computed('field.type')
- isInput: (type) => type === 'text' || type === 'textarea' || type === 'url',
-
- @computed('field.type')
- isCategoryOrTag: (type) => type === 'tag' || type === 'category',
-
- @computed()
- presetChoices() {
- let presets = [
- {
- id: 'categories',
- name: I18n.t('admin.wizard.field.choices_preset.categories')
- },{
- id: 'groups',
- name: I18n.t('admin.wizard.field.choices_preset.groups')
- },{
- id: 'tags',
- name: I18n.t('admin.wizard.field.choices_preset.tags')
- }
- ];
+ isDropdown: equal('field.type', 'dropdown'),
+ isUpload: equal('field.type', 'upload'),
+ isCategory: equal('field.type', 'category'),
+ isGroup: equal('field.type', 'group'),
+ isTag: equal('field.type', 'tag'),
+ isText: equal('field.type', 'text'),
+ isTextarea: equal('field.type', 'textarea'),
+ isUrl: equal('field.type', 'url'),
+ showPrefill: or('isCategory', 'isTag', 'isGroup', 'isDropdown'),
+ showContent: or('isCategory', 'isTag', 'isGroup', 'isDropdown'),
+ showLimit: or('isCategory', 'isTag'),
+ showMinLength: or('isText', 'isTextarea', 'isUrl', 'isComposer'),
+ categoryPropertyTypes: selectKitContent(['id', 'slug']),
+
+ // clearMapped only clears mapped fields if the field type of a specific field
+ // changes, and not when switching between fields. Switching between fields also
+ // changes the field.type property in this component
+
+ @observes('field.id', 'field.type')
+ clearMapped(ctx, changed) {
+ if (this.field.id === this.bufferedFieldId) {
+ schema.field.mapped.forEach(property => {
+ this.set(`field.${property}`, null);
+ });
+ }
- return presets;
+ if (changed === 'field.type') {
+ this.set('bufferedFieldId', this.field.id);
+ }
},
+
+ setupTypeOutput(fieldType, options) {
+ const selectionType = {
+ category: 'category',
+ tag: 'tag',
+ group: 'group'
+ }[fieldType];
+
+ if (selectionType) {
+ options[`${selectionType}Selection`] = 'output';
+ options.outputDefaultSelection = selectionType;
+ }
- @on('didInsertElement')
- @observes('isUpload')
- setupFileType() {
- if (this.get('isUpload') && !this.get('field.file_types')) {
- this.set('field.file_types', '.jpg,.png');
+ return options;
+ },
+
+ @discourseComputed('field.type')
+ contentOptions(fieldType) {
+ let options = {
+ wizardFieldSelection: true,
+ textSelection: 'key,value',
+ userFieldSelection: 'key,value',
+ context: 'field'
+ }
+
+ options = this.setupTypeOutput(fieldType, options);
+
+ if (this.isDropdown) {
+ options.wizardFieldSelection = 'key,value';
+ options.inputTypes = 'association';
+ options.pairConnector = 'association';
+ options.keyPlaceholder = 'admin.wizard.key';
+ options.valuePlaceholder = 'admin.wizard.value';
+ }
+
+ return options;
+ },
+
+ @discourseComputed('field.type')
+ prefillOptions(fieldType) {
+ let options = {
+ wizardFieldSelection: true,
+ textSelection: true,
+ userFieldSelection: 'key,value',
+ context: 'field'
+ }
+
+ return this.setupTypeOutput(fieldType, options);
+ },
+
+ actions: {
+ imageUploadDone(upload) {
+ this.set("field.image", upload.url);
+ },
+
+ imageUploadDeleted() {
+ this.set("field.image", null);
}
}
});
diff --git a/assets/javascripts/discourse/components/wizard-custom-input.js.es6 b/assets/javascripts/discourse/components/wizard-custom-input.js.es6
deleted file mode 100644
index 21e27146..00000000
--- a/assets/javascripts/discourse/components/wizard-custom-input.js.es6
+++ /dev/null
@@ -1,34 +0,0 @@
-import { default as computed, on } from 'ember-addons/ember-computed-decorators';
-import { getOwner } from 'discourse-common/lib/get-owner';
-
-export default Ember.Component.extend({
- classNames: 'custom-input',
- noneKey: 'admin.wizard.select_field',
- noneValue: 'admin.wizard.none',
- connectorNone: 'admin.wizard.none',
- inputKey: 'admin.wizard.key',
- customDisabled: Ember.computed.alias('input.user_field'),
-
- @computed('input.value_custom', 'input.user_field')
- valueDisabled(custom, user) {
- return Boolean(custom || user);
- },
-
- @on('init')
- setupUserFields() {
- const allowUserField = this.get('allowUserField');
- if (allowUserField) {
- const store = getOwner(this).lookup('store:main');
- store.findAll('user-field').then((result) => {
- if (result && result.content && result.content.length) {
- this.set('userFields', result.content.map((f) => {
- return {
- id: `user_field_${f.id}`,
- name: f.name
- };
- }));
- }
- });
- }
- }
-});
diff --git a/assets/javascripts/discourse/components/wizard-custom-inputs.js.es6 b/assets/javascripts/discourse/components/wizard-custom-inputs.js.es6
deleted file mode 100644
index 6dca9d65..00000000
--- a/assets/javascripts/discourse/components/wizard-custom-inputs.js.es6
+++ /dev/null
@@ -1,17 +0,0 @@
-export default Ember.Component.extend({
- classNames: 'custom-inputs',
- valuePlaceholder: 'admin.wizard.value',
-
- actions: {
- add() {
- if (!this.get('inputs')) {
- this.set('inputs', Ember.A());
- }
- this.get('inputs').pushObject(Ember.Object.create());
- },
-
- remove(input) {
- this.get('inputs').removeObject(input);
- }
- }
-});
diff --git a/assets/javascripts/discourse/components/wizard-custom-step.js.es6 b/assets/javascripts/discourse/components/wizard-custom-step.js.es6
index e8ade63a..a18743dd 100644
--- a/assets/javascripts/discourse/components/wizard-custom-step.js.es6
+++ b/assets/javascripts/discourse/components/wizard-custom-step.js.es6
@@ -1,76 +1,22 @@
-import { observes, default as computed } from 'ember-addons/ember-computed-decorators';
+import Component from "@ember/component";
+import { default as discourseComputed } from 'discourse-common/utils/decorators';
+import { wizardFieldList } from '../lib/wizard';
-export default Ember.Component.extend({
+export default Component.extend({
classNames: 'wizard-custom-step',
- currentField: null,
- currentAction: null,
- disableId: Ember.computed.not('step.isNew'),
-
- @observes('step')
- resetCurrentObjects() {
- const fields = this.get('step.fields');
- const actions = this.get('step.actions');
- this.setProperties({
- currentField: fields.length ? fields[0] : null,
- currentAction: actions.length ? actions[0] : null
- });
+
+ @discourseComputed('wizard.steps', 'step.id')
+ descriptionWizardFields(steps, stepId) {
+ return wizardFieldList(steps, { upTo: stepId });
},
-
- @computed('availableFields', 'wizard.steps')
- requiredContent(availableFields, steps) {
- let content = availableFields;
- let actions = [];
-
- steps.forEach(s => {
- actions.push(...s.actions);
- });
-
- actions.forEach(a => {
- if (a.type === 'route_to' && a.code) {
- content.push(Ember.Object.create({
- id: a.code,
- label: "code (Route To)"
- }));
- }
- });
-
- return content;
- },
-
- @computed
- requiredConnectorContent() {
- const label = (id) => I18n.t(`admin.wizard.step.required_data.connector.${id}`);
- return [
- {
- id: 'equals',
- label: label('equals')
- }
- ];
- },
-
- @computed('step.id', 'wizard.save_submissions')
- availableFields(currentStepId, saveSubmissions) {
- const allSteps = this.get('wizard.steps');
- let steps = allSteps;
- let fields = [];
-
- if (!saveSubmissions) {
- steps = [allSteps.findBy('id', currentStepId)];
+
+ actions: {
+ bannerUploadDone(upload) {
+ this.set("step.banner", upload.url);
+ },
+
+ bannerUploadDeleted() {
+ this.set("step.banner", null);
}
-
- steps.forEach((s) => {
- if (s.fields && s.fields.length > 0) {
- let stepFields = s.fields.map((f) => {
- return Ember.Object.create({
- id: f.id,
- label: `${f.id} (${s.id})`,
- type: f.type
- });
- });
- fields.push(...stepFields);
- }
- });
-
- return fields;
- },
+ }
});
diff --git a/assets/javascripts/discourse/components/wizard-export.js.es6 b/assets/javascripts/discourse/components/wizard-export.js.es6
index a2505d24..2537511d 100644
--- a/assets/javascripts/discourse/components/wizard-export.js.es6
+++ b/assets/javascripts/discourse/components/wizard-export.js.es6
@@ -1,6 +1,9 @@
-export default Ember.Component.extend({
+import Component from "@ember/component";
+import { A } from "@ember/array";
+
+export default Component.extend({
classNames: ['container', 'export'],
- selected: Ember.A(),
+ selected: A(),
actions: {
checkChanged(event) {
diff --git a/assets/javascripts/discourse/components/wizard-import.js.es6 b/assets/javascripts/discourse/components/wizard-import.js.es6
index 1cf6618d..d844d5a6 100644
--- a/assets/javascripts/discourse/components/wizard-import.js.es6
+++ b/assets/javascripts/discourse/components/wizard-import.js.es6
@@ -1,11 +1,13 @@
import { ajax } from 'discourse/lib/ajax';
-import { default as computed } from 'ember-addons/ember-computed-decorators';
+import { default as discourseComputed } from 'discourse-common/utils/decorators';
+import { notEmpty } from "@ember/object/computed";
+import Component from "@ember/component";
-export default Ember.Component.extend({
+export default Component.extend({
classNames: ['container', 'import'],
- hasLogs: Ember.computed.notEmpty('logs'),
+ hasLogs: notEmpty('logs'),
- @computed('successIds', 'failureIds')
+ @discourseComputed('successIds', 'failureIds')
logs(successIds, failureIds) {
let logs = [];
diff --git a/assets/javascripts/discourse/components/wizard-links.js.es6 b/assets/javascripts/discourse/components/wizard-links.js.es6
index 291f617c..5f7be87b 100644
--- a/assets/javascripts/discourse/components/wizard-links.js.es6
+++ b/assets/javascripts/discourse/components/wizard-links.js.es6
@@ -1,86 +1,140 @@
-import { default as computed, on, observes } from 'ember-addons/ember-computed-decorators';
+import { default as discourseComputed, on, observes } from 'discourse-common/utils/decorators';
+import { generateName, schema } from '../lib/wizard';
+import { notEmpty } from "@ember/object/computed";
+import { scheduleOnce, bind } from "@ember/runloop";
+import EmberObject from "@ember/object";
+import Component from "@ember/component";
+import { A } from "@ember/array";
-export default Ember.Component.extend({
- classNames: 'wizard-links',
- items: Ember.A(),
+export default Component.extend({
+ classNameBindings: [':wizard-links', 'itemType'],
+ items: A(),
+ anyLinks: notEmpty('links'),
@on('didInsertElement')
@observes('links.@each')
didInsertElement() {
- Ember.run.scheduleOnce('afterRender', () => {
- this.applySortable();
- });
+ scheduleOnce('afterRender', () => (this.applySortable()));
},
applySortable() {
- this.$("ul").sortable({tolerance: 'pointer'}).on('sortupdate', (e, ui) => {
- const itemId = ui.item.data('id');
- const index = ui.item.index();
- Ember.run.bind(this, this.updateItemOrder(itemId, index));
- });
+ $(this.element).find("ul")
+ .sortable({ tolerance: 'pointer' })
+ .on('sortupdate', (e, ui) => {
+ this.updateItemOrder(ui.item.data('id'), ui.item.index());
+ });
},
updateItemOrder(itemId, newIndex) {
- const items = this.get('items');
+ const items = this.items;
const item = items.findBy('id', itemId);
items.removeObject(item);
items.insertAt(newIndex, item);
- Ember.run.scheduleOnce('afterRender', this, () => this.applySortable());
+ scheduleOnce('afterRender', this, () => this.applySortable());
},
- @computed('type')
- header: (type) => `admin.wizard.${type}.header`,
+ @discourseComputed('itemType')
+ header: (itemType) => `admin.wizard.${itemType}.header`,
- @computed('items.@each.id', 'current')
- links(items, current) {
+ @discourseComputed('current', 'items.@each.id', 'items.@each.type', 'items.@each.label', 'items.@each.title')
+ links(current, items) {
if (!items) return;
return items.map((item) => {
if (item) {
- const id = item.get('id');
- const type = this.get('type');
- const label = type === 'action' ? id : (item.get('label') || item.get('title') || id);
- let link = { id, label };
+ let link = {
+ id: item.id
+ }
+
+ let label = item.label || item.title || item.id;
+ if (this.generateLabels && item.type) {
+ label = generateName(item.type);
+ }
+
+ link.label = `${label} (${item.id})`;
let classes = 'btn';
- if (current && item.get('id') === current.get('id')) {
+ if (current && item.id === current.id) {
classes += ' btn-primary';
};
- link['classes'] = classes;
+ link.classes = classes;
return link;
}
});
},
+
+ setDefaults(object, params) {
+ Object.keys(object).forEach(property => {
+ if (object[property]) {
+ params[property] = object[property];
+ }
+ });
+ return params;
+ },
actions: {
add() {
- const items = this.get('items');
- const type = this.get('type');
- const newId = `${type}_${items.length + 1}`;
- let params = { id: newId, isNew: true };
-
- if (type === 'step') {
- params['fields'] = Ember.A();
- params['actions'] = Ember.A();
+ const items = this.items;
+ const itemType = this.itemType;
+ let next = 1;
+
+ if (items.length) {
+ next = Math.max.apply(Math, items.map((i) => (i.id.split('_')[1]))) + 1;
+ }
+
+ let params = {
+ id: `${itemType}_${next}`,
+ isNew: true
};
-
- const newItem = Ember.Object.create(params);
+
+ let objectArrays = schema[itemType].objectArrays;
+ if (objectArrays) {
+ Object.keys(objectArrays).forEach(objectType => {
+ params[objectArrays[objectType].property] = A();
+ });
+ };
+
+ params = this.setDefaults(schema[itemType].basic, params);
+
+ let types = schema[itemType].types;
+ if (types && params.type) {
+ params = this.setDefaults(types[params.type], params);
+ }
+
+ const newItem = EmberObject.create(params);
items.pushObject(newItem);
+
this.set('current', newItem);
- this.sendAction('isNew');
},
change(itemId) {
- const items = this.get('items');
- this.set('current', items.findBy('id', itemId));
+ this.set('current', this.items.findBy('id', itemId));
},
remove(itemId) {
- const items = this.get('items');
- items.removeObject(items.findBy('id', itemId));
- this.set('current', items[items.length - 1]);
+ const items = this.items;
+ let item;
+ let index;
+
+ items.forEach((it, ind) => {
+ if (it.id === itemId) {
+ item = it;
+ index = ind;
+ }
+ });
+
+ let nextIndex;
+ if (this.current.id === itemId) {
+ nextIndex = index < (items.length-2) ? (index+1) : (index-1);
+ }
+
+ items.removeObject(item);
+
+ if (nextIndex) {
+ this.set('current', items[nextIndex]);
+ }
}
}
});
diff --git a/assets/javascripts/discourse/components/wizard-mapper-connector.js.es6 b/assets/javascripts/discourse/components/wizard-mapper-connector.js.es6
new file mode 100644
index 00000000..946acaed
--- /dev/null
+++ b/assets/javascripts/discourse/components/wizard-mapper-connector.js.es6
@@ -0,0 +1,26 @@
+import Component from "@ember/component";
+import { gt } from '@ember/object/computed';
+import { computed } from "@ember/object";
+import { defaultConnector } from '../lib/wizard-mapper';
+import { later } from "@ember/runloop";
+
+export default Component.extend({
+ classNameBindings: [':mapper-connector', ':mapper-block', 'hasMultiple::single'],
+ hasMultiple: gt('connectors.length', 1),
+ connectorLabel: computed(function() {
+ let key = this.connector;
+ let path = this.inputTypes ? `input.${key}.name` : `connector.${key}`;
+ return I18n.t(`admin.wizard.${path}`);
+ }),
+
+ didReceiveAttrs() {
+ if (!this.connector) {
+ later(() => {
+ this.set(
+ 'connector',
+ defaultConnector(this.connectorType, this.inputType, this.options)
+ );
+ });
+ }
+ }
+});
\ No newline at end of file
diff --git a/assets/javascripts/discourse/components/wizard-mapper-input.js.es6 b/assets/javascripts/discourse/components/wizard-mapper-input.js.es6
new file mode 100644
index 00000000..f97bf44c
--- /dev/null
+++ b/assets/javascripts/discourse/components/wizard-mapper-input.js.es6
@@ -0,0 +1,65 @@
+import { computed, set } from "@ember/object";
+import { alias, equal, or, not } from "@ember/object/computed";
+import { newPair, connectorContent, inputTypesContent, defaultSelectionType, defaultConnector } from '../lib/wizard-mapper';
+import Component from "@ember/component";
+import { observes } from "discourse-common/utils/decorators";
+import { A } from "@ember/array";
+
+export default Component.extend({
+ classNameBindings: [':mapper-input', 'inputType'],
+ inputType: alias('input.type'),
+ isConditional: equal('inputType', 'conditional'),
+ isAssignment: equal('inputType', 'assignment'),
+ isAssociation: equal('inputType', 'association'),
+ isValidation: equal('inputType', 'validation'),
+ hasOutput: or('isConditional', 'isAssignment'),
+ hasPairs: or('isConditional', 'isAssociation', 'isValidation'),
+ canAddPair: not('isAssignment'),
+ connectors: computed(function() { return connectorContent('output', this.input.type, this.options) }),
+ inputTypes: computed(function() { return inputTypesContent(this.options) }),
+
+ @observes('input.type')
+ setupType() {
+ if (this.hasPairs && (!this.input.pairs || this.input.pairs.length < 1)) {
+ this.send('addPair');
+ }
+
+ if (this.hasOutput && !this.input.outputConnector) {
+ const options = this.options;
+ this.set('input.output_type', defaultSelectionType('output', options));
+ this.set('input.output_connector', defaultConnector('output', this.inputType, options));
+ }
+ },
+
+ actions: {
+ addPair() {
+ if (!this.input.pairs) {
+ this.set('input.pairs', A());
+ }
+
+ const pairs = this.input.pairs;
+ const pairCount = pairs.length + 1;
+
+ pairs.forEach(p => (set(p, 'pairCount', pairCount)));
+
+ pairs.pushObject(
+ newPair(
+ this.input.type,
+ Object.assign(
+ {},
+ this.options,
+ { index: pairs.length, pairCount }
+ )
+ )
+ );
+ },
+
+ removePair(pair) {
+ const pairs = this.input.pairs;
+ const pairCount = pairs.length - 1;
+
+ pairs.forEach(p => (set(p, 'pairCount', pairCount)));
+ pairs.removeObject(pair);
+ }
+ }
+});
diff --git a/assets/javascripts/discourse/components/wizard-mapper-pair.js.es6 b/assets/javascripts/discourse/components/wizard-mapper-pair.js.es6
new file mode 100644
index 00000000..b7d88c5e
--- /dev/null
+++ b/assets/javascripts/discourse/components/wizard-mapper-pair.js.es6
@@ -0,0 +1,12 @@
+import { connectorContent } from '../lib/wizard-mapper';
+import { gt, or, alias } from "@ember/object/computed";
+import { computed, observes } from "@ember/object";
+import Component from "@ember/component";
+
+export default Component.extend({
+ classNameBindings: [':mapper-pair', 'hasConnector::no-connector'],
+ firstPair: gt('pair.index', 0),
+ showRemove: alias('firstPair'),
+ showJoin: computed('pair.pairCount', function() { return this.pair.index < (this.pair.pairCount - 1) }),
+ connectors: computed(function() { return connectorContent('pair', this.inputType, this.options) })
+});
\ No newline at end of file
diff --git a/assets/javascripts/discourse/components/wizard-mapper-selector-type.js.es6 b/assets/javascripts/discourse/components/wizard-mapper-selector-type.js.es6
new file mode 100644
index 00000000..53baccef
--- /dev/null
+++ b/assets/javascripts/discourse/components/wizard-mapper-selector-type.js.es6
@@ -0,0 +1,14 @@
+import discourseComputed from 'discourse-common/utils/decorators';
+import Component from "@ember/component";
+
+export default Component.extend({
+ tagName: 'a',
+ classNameBindings: ['active'],
+
+ @discourseComputed('item.type', 'activeType')
+ active(type, activeType) { return type === activeType },
+
+ click() {
+ this.toggle(this.item.type)
+ }
+})
\ No newline at end of file
diff --git a/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6 b/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6
new file mode 100644
index 00000000..4b1daad1
--- /dev/null
+++ b/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6
@@ -0,0 +1,162 @@
+import { alias, or, gt } from "@ember/object/computed";
+import { computed } from "@ember/object";
+import { default as discourseComputed, observes, on } from "discourse-common/utils/decorators";
+import { getOwner } from 'discourse-common/lib/get-owner';
+import { defaultSelectionType, selectionTypes } from '../lib/wizard-mapper';
+import { snakeCase } from '../lib/wizard';
+import Component from "@ember/component";
+import { bind, later } from "@ember/runloop";
+
+export default Component.extend({
+ classNameBindings: [':mapper-selector', 'activeType'],
+ groups: alias('site.groups'),
+ categories: alias('site.categories'),
+ showText: computed('activeType', function() { return this.showInput('text') }),
+ showWizardField: computed('activeType', function() { return this.showInput('wizardField') }),
+ showUserField: computed('activeType', function() { return this.showInput('userField') }),
+ showCategory: computed('activeType', function() { return this.showInput('category') }),
+ showTag: computed('activeType', function() { return this.showInput('tag') }),
+ showGroup: computed('activeType', function() { return this.showInput('group') }),
+ showUser: computed('activeType', function() { return this.showInput('user') }),
+ showList: computed('activeType', function() { return this.showInput('list') }),
+ showComboBox: or('showWizardField', 'showUserField'),
+ showMultiSelect: or('showCategory', 'showGroup'),
+ textEnabled: computed('options.textSelection', 'inputType', function() { return this.optionEnabled('textSelection') }),
+ wizardFieldEnabled: computed('options.wizardFieldSelection', 'inputType', function() { return this.optionEnabled('wizardFieldSelection') }),
+ userFieldEnabled: computed('options.userFieldSelection', 'inputType', function() { return this.optionEnabled('userFieldSelection') }),
+ categoryEnabled: computed('options.categorySelection', 'inputType', function() { return this.optionEnabled('categorySelection') }),
+ tagEnabled: computed('options.tagSelection', 'inputType', function() { return this.optionEnabled('tagSelection') }),
+ groupEnabled: computed('options.groupSelection', 'inputType', function() { return this.optionEnabled('groupSelection') }),
+ userEnabled: computed('options.userSelection', 'inputType', function() { return this.optionEnabled('userSelection') }),
+ listEnabled: computed('options.listSelection', 'inputType', function() { return this.optionEnabled('listSelection') }),
+ hasTypes: gt('selectorTypes.length', 1),
+ showTypes: false,
+
+ didInsertElement() {
+ if (this.activeType && !this[`${this.activeType}Enabled`]) {
+ later(() => this.resetActiveType());
+ }
+
+ $(document).on("click", bind(this, this.documentClick));
+ },
+
+ willDestroyElement() {
+ $(document).off("click", bind(this, this.documentClick));
+ },
+
+ documentClick(e) {
+ if (this._state == "destroying") return;
+ let $target = $(e.target);
+
+ if (!$target.parents('.type-selector').length && this.showTypes) {
+ this.set('showTypes', false);
+ }
+ },
+
+ @discourseComputed
+ selectorTypes() {
+ return selectionTypes.filter(type => (this[`${type}Enabled`]))
+ .map(type => ({ type, label: this.typeLabel(type) }));
+ },
+
+ @discourseComputed('activeType')
+ activeTypeLabel(activeType) {
+ return this.typeLabel(activeType);
+ },
+
+ typeLabel(type) {
+ return I18n.t(`admin.wizard.selector.label.${snakeCase(type)}`)
+ },
+
+ @observes('inputType')
+ resetActiveType() {
+ this.set('activeType', defaultSelectionType(this.selectorType, this.options));
+ },
+
+ @observes('activeType')
+ clearValue() {
+ this.set('value', null);
+ },
+
+ @discourseComputed('activeType')
+ comboBoxContent(activeType) {
+ const controller = getOwner(this).lookup('controller:admin-wizards-wizard-show');
+ let content = controller[`${activeType}s`];
+
+ // you can't select the current field in the field context
+ if (activeType === 'wizardField' && this.options.context === 'field') {
+ content = content.filter(field => field.id !== controller.currentField.id);
+ }
+
+ // updating certain user fields via the profile update action is not supported
+ if (activeType === 'userField' &&
+ this.options.context === 'action' &&
+ this.inputType === 'association' &&
+ this.selectorType === 'key') {
+
+ const excludedFields = ['username','email', 'trust_level'];
+ content = content.filter(userField => excludedFields.indexOf(userField.id) === -1);
+ }
+
+ return content;
+ },
+
+ @discourseComputed('activeType')
+ multiSelectContent(activeType) {
+ return {
+ category: this.categories,
+ group: this.groups,
+ list: ''
+ }[activeType];
+ },
+
+ @discourseComputed('activeType', 'inputType')
+ placeholderKey(activeType, inputType) {
+ if (activeType === 'text' && this.options[`${this.selectorType}Placeholder`]) {
+ return this.options[`${this.selectorType}Placeholder`];
+ } else {
+ return `admin.wizard.selector.placeholder.${snakeCase(activeType)}`;
+ }
+ },
+
+ @discourseComputed('activeType')
+ multiSelectOptions(activeType) {
+ let result = {
+ none: this.placeholderKey
+ };
+
+ if (activeType === 'list') {
+ result.allowAny = true;
+ }
+
+ return result;
+ },
+
+ optionEnabled(type) {
+ const options = this.options;
+ if (!options) return false;
+
+ const option = options[type];
+ if (option === true) return true;
+ if (typeof option !== 'string') return false;
+
+ return option.split(',').filter(option => {
+ return [this.selectorType, this.inputType].indexOf(option) !== -1;
+ }).length;
+ },
+
+ showInput(type) {
+ return this.activeType === type && this[`${type}Enabled`];
+ },
+
+ actions: {
+ toggleType(type) {
+ this.set('activeType', type);
+ this.set('showTypes', false);
+ },
+
+ toggleTypes() {
+ this.toggleProperty('showTypes');
+ }
+ }
+})
\ No newline at end of file
diff --git a/assets/javascripts/discourse/components/wizard-mapper.js.es6 b/assets/javascripts/discourse/components/wizard-mapper.js.es6
new file mode 100644
index 00000000..525cf4ba
--- /dev/null
+++ b/assets/javascripts/discourse/components/wizard-mapper.js.es6
@@ -0,0 +1,73 @@
+import { getOwner } from 'discourse-common/lib/get-owner';
+import { newInput, selectionTypes } from '../lib/wizard-mapper';
+import { default as discourseComputed, observes, on } from 'discourse-common/utils/decorators';
+import { later } from "@ember/runloop";
+import Component from "@ember/component";
+import { A } from "@ember/array";
+
+export default Component.extend({
+ classNames: 'wizard-mapper',
+
+ didReceiveAttrs() {
+ if (this.inputs && this.inputs.constructor !== Array) {
+ later(() => this.set('inputs', null));
+ }
+ },
+
+ @discourseComputed('inputs.@each.type')
+ canAdd(inputs) {
+ return !inputs ||
+ inputs.constructor !== Array ||
+ inputs.every(i => {
+ return ['assignment','association'].indexOf(i.type) === -1;
+ });
+ },
+
+ @discourseComputed('options.@each.inputType')
+ inputOptions(options) {
+ let result = {
+ inputTypes: options.inputTypes || 'assignment,conditional',
+ inputConnector: options.inputConnector || 'or',
+ pairConnector: options.pairConnector || null,
+ outputConnector: options.outputConnector || null,
+ context: options.context || null
+ }
+
+ let inputTypes = ['key', 'value', 'output'];
+ inputTypes.forEach(type => {
+ result[`${type}Placeholder`] = options[`${type}Placeholder`] || null;
+ result[`${type}DefaultSelection`] = options[`${type}DefaultSelection`] || null;
+ });
+
+ selectionTypes.forEach(type => {
+ if (options[`${type}Selection`] !== undefined) {
+ result[`${type}Selection`] = options[`${type}Selection`]
+ } else {
+ result[`${type}Selection`] = type === 'text' ? true : false;
+ }
+ });
+
+ return result;
+ },
+
+ actions: {
+ add() {
+ if (!this.get('inputs')) {
+ this.set('inputs', A());
+ }
+
+ this.get('inputs').pushObject(
+ newInput(this.inputOptions, this.inputs.length)
+ );
+ },
+
+ remove(input) {
+ const inputs = this.inputs;
+ inputs.removeObject(input);
+
+ if (inputs.length) {
+ inputs[0].set('connector', null);
+ }
+ }
+ }
+});
diff --git a/assets/javascripts/discourse/components/wizard-text-editor.js.es6 b/assets/javascripts/discourse/components/wizard-text-editor.js.es6
new file mode 100644
index 00000000..4cbb7efb
--- /dev/null
+++ b/assets/javascripts/discourse/components/wizard-text-editor.js.es6
@@ -0,0 +1,57 @@
+import { default as discourseComputed, on } from 'discourse-common/utils/decorators';
+import { notEmpty } from "@ember/object/computed";
+import { userProperties } from '../lib/wizard';
+import { scheduleOnce } from "@ember/runloop";
+import Component from "@ember/component";
+
+export default Component.extend({
+ classNames: 'wizard-text-editor',
+ barEnabled: true,
+ previewEnabled: true,
+ fieldsEnabled: true,
+ hasWizardFields: notEmpty('wizardFieldList'),
+
+ didReceiveAttrs() {
+ this._super(...arguments);
+
+ if (!this.barEnabled) {
+ scheduleOnce('afterRender', () => {
+ $(this.element).find('.d-editor-button-bar').addClass('hidden');
+ });
+ }
+ },
+
+ @discourseComputed('forcePreview')
+ previewLabel(forcePreview) {
+ return I18n.t("admin.wizard.editor.preview", {
+ action: I18n.t(`admin.wizard.editor.${forcePreview ? 'hide' : 'show'}`)
+ });
+ },
+
+ @discourseComputed('showPopover')
+ popoverLabel(showPopover) {
+ return I18n.t("admin.wizard.editor.popover", {
+ action: I18n.t(`admin.wizard.editor.${showPopover ? 'hide' : 'show'}`)
+ });
+ },
+
+ @discourseComputed()
+ userFieldList() {
+ return userProperties.map((f) => ` u{${f}}`);
+ },
+
+ @discourseComputed('wizardFields')
+ wizardFieldList(wizardFields) {
+ return wizardFields.map((f) => ` w{${f.id}}`);
+ },
+
+ actions: {
+ togglePreview() {
+ this.toggleProperty('forcePreview');
+ },
+
+ togglePopover() {
+ this.toggleProperty('showPopover');
+ }
+ }
+});
\ No newline at end of file
diff --git a/assets/javascripts/discourse/connectors/admin-menu/wizards-nav-button.hbs b/assets/javascripts/discourse/connectors/admin-menu/wizards-nav-button.hbs
index 3807aa18..5398e27d 100644
--- a/assets/javascripts/discourse/connectors/admin-menu/wizards-nav-button.hbs
+++ b/assets/javascripts/discourse/connectors/admin-menu/wizards-nav-button.hbs
@@ -1,3 +1,3 @@
{{#if currentUser.admin}}
- {{nav-item route='adminWizards' label='admin.wizard.label'}}
+ {{nav-item route='adminWizards' label='admin.wizard.nav_label'}}
{{/if}}
diff --git a/assets/javascripts/discourse/connectors/top-notices/prompt-completion.hbs b/assets/javascripts/discourse/connectors/top-notices/prompt-completion.hbs
new file mode 100644
index 00000000..503ee113
--- /dev/null
+++ b/assets/javascripts/discourse/connectors/top-notices/prompt-completion.hbs
@@ -0,0 +1,7 @@
+{{#each site.complete_custom_wizard as |wizard|}}
+
+{{/each}}
\ No newline at end of file
diff --git a/assets/javascripts/discourse/connectors/top-notices/prompt-completion.js.es6 b/assets/javascripts/discourse/connectors/top-notices/prompt-completion.js.es6
new file mode 100644
index 00000000..8d097475
--- /dev/null
+++ b/assets/javascripts/discourse/connectors/top-notices/prompt-completion.js.es6
@@ -0,0 +1,6 @@
+export default {
+ shouldRender(_, ctx) {
+ return ctx.siteSettings.custom_wizard_enabled &&
+ ctx.site.complete_custom_wizard;
+ }
+}
\ No newline at end of file
diff --git a/assets/javascripts/discourse/controllers/admin-wizard.js.es6 b/assets/javascripts/discourse/controllers/admin-wizard.js.es6
deleted file mode 100644
index af36539a..00000000
--- a/assets/javascripts/discourse/controllers/admin-wizard.js.es6
+++ /dev/null
@@ -1,55 +0,0 @@
-import { default as computed } from 'ember-addons/ember-computed-decorators';
-import showModal from 'discourse/lib/show-modal';
-
-export default Ember.Controller.extend({
- @computed('model.id', 'model.name')
- wizardUrl(wizardId) {
- return window.location.origin + '/w/' + Ember.String.dasherize(wizardId);
- },
-
- @computed('model.after_time_scheduled')
- nextSessionScheduledLabel(scheduled) {
- return scheduled ? moment(scheduled).format('MMMM Do, HH:mm') :
- I18n.t('admin.wizard.after_time_time_label');
- },
-
- actions: {
- save() {
- this.setProperties({
- saving: true,
- error: null
- });
- const wizard = this.get('model');
- wizard.save().then(() => {
- this.set('saving', false);
- if (this.get('newWizard')) {
- this.send("refreshAllWizards");
- } else {
- this.send("refreshWizard");
- }
- }).catch((result) => {
- this.set('saving', false);
- this.set('error', I18n.t(`admin.wizard.error.${result.error}`));
- Ember.run.later(() => this.set('error', null), 10000);
- });
- },
-
- remove() {
- const wizard = this.get('model');
- wizard.remove().then(() => {
- this.send("refreshAllWizards");
- });
- },
-
- setNextSessionScheduled() {
- let controller = showModal('next-session-scheduled', {
- model: {
- dateTime: this.get('model.after_time_scheduled'),
- update: (dateTime) => this.set('model.after_time_scheduled', dateTime)
- }
- });
-
- controller.setup();
- },
- }
-});
diff --git a/assets/javascripts/discourse/controllers/admin-wizards-api.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-api-show.js.es6
similarity index 82%
rename from assets/javascripts/discourse/controllers/admin-wizards-api.js.es6
rename to assets/javascripts/discourse/controllers/admin-wizards-api-show.js.es6
index 8f0972e6..d083b157 100644
--- a/assets/javascripts/discourse/controllers/admin-wizards-api.js.es6
+++ b/assets/javascripts/discourse/controllers/admin-wizards-api-show.js.es6
@@ -1,42 +1,44 @@
import { ajax } from 'discourse/lib/ajax';
import { popupAjaxError } from 'discourse/lib/ajax-error';
import CustomWizardApi from '../models/custom-wizard-api';
-import { default as computed } from 'ember-addons/ember-computed-decorators';
-import { generateSelectKitContent } from '../lib/custom-wizard';
+import { default as discourseComputed } from 'discourse-common/utils/decorators';
+import { not, and, equal } from "@ember/object/computed";
+import { selectKitContent } from '../lib/wizard';
+import Controller from "@ember/controller";
-export default Ember.Controller.extend({
+export default Controller.extend({
queryParams: ['refresh_list'],
loadingSubscriptions: false,
- notAuthorized: Ember.computed.not('api.authorized'),
- endpointMethods: generateSelectKitContent(['GET', 'PUT', 'POST', 'PATCH', 'DELETE']),
- showRemove: Ember.computed.not('isNew'),
- showRedirectUri: Ember.computed.and('threeLeggedOauth', 'api.name'),
+ notAuthorized: not('api.authorized'),
+ endpointMethods: selectKitContent(['GET', 'PUT', 'POST', 'PATCH', 'DELETE']),
+ showRemove: not('isNew'),
+ showRedirectUri: and('threeLeggedOauth', 'api.name'),
responseIcon: null,
- contentTypes: generateSelectKitContent(['application/json', 'application/x-www-form-urlencoded']),
- successCodes: generateSelectKitContent([100, 101, 102, 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, 302, 303, 303, 304, 305, 306, 307, 308]),
+ contentTypes: selectKitContent(['application/json', 'application/x-www-form-urlencoded']),
+ successCodes: selectKitContent([100, 101, 102, 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, 302, 303, 303, 304, 305, 306, 307, 308]),
- @computed('saveDisabled', 'api.authType', 'api.authUrl', 'api.tokenUrl', 'api.clientId', 'api.clientSecret', 'threeLeggedOauth')
+ @discourseComputed('saveDisabled', 'api.authType', 'api.authUrl', 'api.tokenUrl', 'api.clientId', 'api.clientSecret', 'threeLeggedOauth')
authDisabled(saveDisabled, authType, authUrl, tokenUrl, clientId, clientSecret, threeLeggedOauth) {
if (saveDisabled || !authType || !tokenUrl || !clientId || !clientSecret) return true;
if (threeLeggedOauth) return !authUrl;
return false;
},
- @computed('api.name', 'api.authType')
+ @discourseComputed('api.name', 'api.authType')
saveDisabled(name, authType) {
return !name || !authType;
},
- authorizationTypes: generateSelectKitContent(['none', 'basic', 'oauth_2', 'oauth_3']),
- isBasicAuth: Ember.computed.equal('api.authType', 'basic'),
+ authorizationTypes: selectKitContent(['none', 'basic', 'oauth_2', 'oauth_3']),
+ isBasicAuth: equal('api.authType', 'basic'),
- @computed('api.authType')
+ @discourseComputed('api.authType')
isOauth(authType) {
return authType && authType.indexOf('oauth') > -1;
},
- twoLeggedOauth: Ember.computed.equal('api.authType', 'oauth_2'),
- threeLeggedOauth: Ember.computed.equal('api.authType', 'oauth_3'),
+ twoLeggedOauth: equal('api.authType', 'oauth_2'),
+ threeLeggedOauth: equal('api.authType', 'oauth_3'),
actions: {
addParam() {
diff --git a/assets/javascripts/discourse/controllers/admin-wizards-apis.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-apis.js.es6
deleted file mode 100644
index 52748bc8..00000000
--- a/assets/javascripts/discourse/controllers/admin-wizards-apis.js.es6
+++ /dev/null
@@ -1,3 +0,0 @@
-export default Ember.Controller.extend({
- queryParams: ['refresh']
-});
diff --git a/assets/javascripts/discourse/controllers/admin-wizards-logs.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-logs.js.es6
new file mode 100644
index 00000000..26e2d622
--- /dev/null
+++ b/assets/javascripts/discourse/controllers/admin-wizards-logs.js.es6
@@ -0,0 +1,49 @@
+import { default as computed } from 'discourse-common/utils/decorators';
+import { popupAjaxError } from 'discourse/lib/ajax-error';
+import { ajax } from 'discourse/lib/ajax';
+import { notEmpty } from "@ember/object/computed";
+import CustomWizardLogs from '../models/custom-wizard-logs';
+
+export default Ember.Controller.extend({
+ refreshing: false,
+ hasLogs: notEmpty("logs"),
+ page: 0,
+ canLoadMore: true,
+ logs: [],
+
+ loadLogs() {
+ if (!this.canLoadMore) return;
+
+ this.set("refreshing", true);
+
+ CustomWizardLogs.list()
+ .then(result => {
+ if (!result || result.length === 0) {
+ this.set('canLoadMore', false);
+ }
+ this.set("logs", this.logs.concat(result));
+ })
+ .finally(() => this.set("refreshing", false));
+ },
+
+ @computed('hasLogs', 'refreshing')
+ noResults(hasLogs, refreshing) {
+ return !hasLogs && !refreshing;
+ },
+
+ actions: {
+ loadMore() {
+ this.set('page', this.page += 1);
+ this.loadLogs();
+ },
+
+ refresh() {
+ this.setProperties({
+ canLoadMore: true,
+ page: 0,
+ logs: []
+ })
+ this.loadLogs();
+ }
+ }
+});
\ No newline at end of file
diff --git a/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6
new file mode 100644
index 00000000..57b0ab57
--- /dev/null
+++ b/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6
@@ -0,0 +1,6 @@
+import Controller from "@ember/controller";
+import { fmt } from "discourse/lib/computed";
+
+export default Controller.extend({
+ downloadUrl: fmt("wizard.id", "/admin/wizards/submissions/%@/download")
+});
\ No newline at end of file
diff --git a/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6
index 77c79b72..7ae8f5a1 100644
--- a/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6
+++ b/assets/javascripts/discourse/controllers/admin-wizards-transfer.js.es6
@@ -1 +1,3 @@
-export default Ember.Controller.extend();
+import Controller from "@ember/controller";
+
+export default Controller.extend();
diff --git a/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6
new file mode 100644
index 00000000..5f10283e
--- /dev/null
+++ b/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6
@@ -0,0 +1,122 @@
+import { default as discourseComputed, observes, on } from 'discourse-common/utils/decorators';
+import { notEmpty, alias } from "@ember/object/computed";
+import showModal from 'discourse/lib/show-modal';
+import { generateId, wizardFieldList } from '../lib/wizard';
+import { buildProperties } from '../lib/wizard-json';
+import { dasherize } from "@ember/string";
+import EmberObject from "@ember/object";
+import { scheduleOnce, later } from "@ember/runloop";
+import Controller from "@ember/controller";
+import copyText from "discourse/lib/copy-text";
+import CustomWizard from '../models/custom-wizard';
+
+export default Controller.extend({
+ hasName: notEmpty('wizard.name'),
+
+ @observes('currentStep')
+ resetCurrentObjects() {
+ const currentStep = this.currentStep;
+
+ if (currentStep) {
+ const fields = currentStep.fields;
+ this.set('currentField', fields && fields.length ? fields[0] : null)
+ }
+
+ scheduleOnce('afterRender', () => ($("body").addClass('admin-wizard')));
+ },
+
+ @observes('wizard.name')
+ setId() {
+ const wizard = this.wizard;
+ if (wizard && !wizard.existingId) {
+ this.set('wizard.id', generateId(wizard.name));
+ }
+ },
+
+ @discourseComputed('wizard.id')
+ wizardUrl(wizardId) {
+ return window.location.origin + '/w/' + dasherize(wizardId);
+ },
+
+ @discourseComputed('wizard.after_time_scheduled')
+ nextSessionScheduledLabel(scheduled) {
+ return scheduled ?
+ moment(scheduled).format('MMMM Do, HH:mm') :
+ I18n.t('admin.wizard.after_time_time_label');
+ },
+
+ @discourseComputed('currentStep.id', 'wizard.save_submissions', 'wizard.steps.@each.fields[]')
+ wizardFields(currentStepId, saveSubmissions) {
+ let steps = this.wizard.steps;
+ if (!saveSubmissions) {
+ steps = [steps.findBy('id', currentStepId)];
+ }
+ return wizardFieldList(steps);
+ },
+
+ actions: {
+ save() {
+ this.setProperties({
+ saving: true,
+ error: null
+ });
+
+ const wizard = this.wizard;
+ const creating = this.creating;
+ let opts = {};
+
+ if (creating) {
+ opts.create = true;
+ }
+
+ wizard.save(opts).then((result) => {
+ this.send('afterSave', result.wizard_id);
+ }).catch((result) => {
+ let errorType = 'failed';
+ let errorParams = {};
+
+ if (result.error) {
+ errorType = result.error.type;
+ errorParams = result.error.params;
+ }
+
+ this.set('error', I18n.t(`admin.wizard.error.${errorType}`, errorParams));
+
+ later(() => this.set('error', null), 10000);
+ }).finally(() => this.set('saving', false));
+ },
+
+ remove() {
+ this.wizard.remove().then(() => this.send('afterDestroy'));
+ },
+
+ setNextSessionScheduled() {
+ let controller = showModal('next-session-scheduled', {
+ model: {
+ dateTime: this.wizard.after_time_scheduled,
+ update: (dateTime) => this.set('wizard.after_time_scheduled', dateTime)
+ }
+ });
+
+ controller.setup();
+ },
+
+ toggleAdvanced() {
+ this.toggleProperty('wizard.showAdvanced');
+ },
+
+ copyUrl() {
+ const $copyRange = $('');
+ $copyRange.html(this.wizardUrl);
+
+ $(document.body).append($copyRange);
+
+ if (copyText(this.wizardUrl, $copyRange[0])) {
+ this.set("copiedUrl", true);
+ later(() => this.set("copiedUrl", false), 2000);
+ }
+
+ $copyRange.remove();
+ }
+ }
+});
diff --git a/assets/javascripts/discourse/controllers/admin-wizards-wizard.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-wizard.js.es6
new file mode 100644
index 00000000..b09e2ecd
--- /dev/null
+++ b/assets/javascripts/discourse/controllers/admin-wizards-wizard.js.es6
@@ -0,0 +1,25 @@
+import Controller from "@ember/controller";
+import { default as discourseComputed } from 'discourse-common/utils/decorators';
+import { equal } from '@ember/object/computed';
+
+export default Controller.extend({
+ creating: equal('wizardId', 'create'),
+
+ @discourseComputed('creating', 'wizardId')
+ wizardListVal(creating, wizardId) {
+ return creating ? null : wizardId;
+ },
+
+ @discourseComputed('creating', 'wizardId')
+ message(creating, wizardId) {
+ let type = 'select';
+
+ if (creating) {
+ type = 'create';
+ } else if (wizardId) {
+ type = 'edit';
+ }
+
+ return I18n.t(`admin.wizard.message.${type}`);
+ }
+});
\ No newline at end of file
diff --git a/assets/javascripts/discourse/controllers/next-session-scheduled.js.es6 b/assets/javascripts/discourse/controllers/next-session-scheduled.js.es6
index 7edec46d..cb7650e7 100644
--- a/assets/javascripts/discourse/controllers/next-session-scheduled.js.es6
+++ b/assets/javascripts/discourse/controllers/next-session-scheduled.js.es6
@@ -1,6 +1,8 @@
-import { default as computed } from 'ember-addons/ember-computed-decorators';
+import { default as discourseComputed } from 'discourse-common/utils/decorators';
+import { scheduleOnce } from "@ember/runloop";
+import Controller from "@ember/controller";
-export default Ember.Controller.extend({
+export default Controller.extend({
title: 'admin.wizard.after_time_modal.title',
setup() {
@@ -14,7 +16,7 @@ export default Ember.Controller.extend({
this.setProperties({ date, time });
- Ember.run.scheduleOnce('afterRender', this, () => {
+ scheduleOnce('afterRender', this, () => {
const $timePicker = $("#time-picker");
$timePicker.timepicker({ timeFormat: 'H:i' });
$timePicker.timepicker('setTime', time);
@@ -22,12 +24,12 @@ export default Ember.Controller.extend({
});
},
- @computed('date', 'time')
+ @discourseComputed('date', 'time')
dateTime: function(date, time) {
return moment(date + 'T' + time).format();
},
- @computed('dateTime')
+ @discourseComputed('dateTime')
submitDisabled(dateTime) {
return moment().isAfter(dateTime);
},
diff --git a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6
index 001569f2..c2722816 100644
--- a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6
+++ b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6
@@ -2,18 +2,22 @@ export default {
resource: 'admin',
map() {
this.route('adminWizards', { path: '/wizards', resetNamespace: true }, function() {
- this.route('adminWizardsCustom', { path: '/custom', resetNamespace: true }, function() {
- this.route('adminWizard', { path: '/:wizard_id', resetNamespace: true });
+
+ this.route('adminWizardsWizard', { path: '/wizard/', resetNamespace: true }, function() {
+ this.route('adminWizardsWizardShow', { path: '/:wizardId/', resetNamespace: true });
});
+
this.route('adminWizardsSubmissions', { path: '/submissions', resetNamespace: true }, function() {
- this.route('adminWizardSubmissions', { path: '/:wizard_id', resetNamespace: true });
- });
- this.route('adminWizardsApis', { path: '/apis', resetNamespace: true }, function() {
- this.route('adminWizardsApi', { path: '/:name', resetNamespace: true });
+ this.route('adminWizardsSubmissionsShow', { path: '/:wizardId/', resetNamespace: true });
+ })
+
+ this.route('adminWizardsApi', { path: '/api', resetNamespace: true }, function() {
+ this.route('adminWizardsApiShow', { path: '/:name', resetNamespace: true });
});
+
+ this.route('adminWizardsLogs', { path: '/logs', resetNamespace: true });
this.route('adminWizardsTransfer', { path: '/transfer', resetNamespace: true });
-
});
}
};
diff --git a/assets/javascripts/discourse/helpers/custom-wizard.js.es6 b/assets/javascripts/discourse/helpers/custom-wizard.js.es6
index c761d9a0..3b73e476 100644
--- a/assets/javascripts/discourse/helpers/custom-wizard.js.es6
+++ b/assets/javascripts/discourse/helpers/custom-wizard.js.es6
@@ -1,5 +1,6 @@
import { registerUnbound } from 'discourse-common/lib/helpers';
+import { dasherize } from "@ember/string";
registerUnbound('dasherize', function(string) {
- return Ember.String.dasherize(string);
+ return dasherize(string);
});
diff --git a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6
index 1e4436cb..cd2d9558 100644
--- a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6
+++ b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6
@@ -7,25 +7,6 @@ export default {
const siteSettings = container.lookup('site-settings:main');
if (!siteSettings.custom_wizard_enabled) return;
-
- withPluginApi('0.8.12', api => {
- api.modifyClass('component:global-notice', {
- buildBuffer(buffer) {
- this._super(...arguments);
- const wizards = this.site.get('complete_custom_wizard');
- if (wizards) {
- wizards.forEach((w) => {
- const text = I18n.t('wizard.complete_custom', {
- wizard_url: w.url,
- wizard_name: w.name,
- site_name: this.siteSettings.title
- });
- buffer.push(``);
- });
- }
- }
- });
- });
const existing = DiscourseURL.routeTo;
DiscourseURL.routeTo = function(path, opts) {
diff --git a/assets/javascripts/discourse/lib/custom-wizard.js.es6 b/assets/javascripts/discourse/lib/custom-wizard.js.es6
deleted file mode 100644
index 16010372..00000000
--- a/assets/javascripts/discourse/lib/custom-wizard.js.es6
+++ /dev/null
@@ -1,5 +0,0 @@
-function generateSelectKitContent(content) {
- return content.map(i => ({id: i, name: i}))
-}
-
-export { generateSelectKitContent };
\ No newline at end of file
diff --git a/assets/javascripts/discourse/lib/wizard-json.js.es6 b/assets/javascripts/discourse/lib/wizard-json.js.es6
new file mode 100644
index 00000000..c1325514
--- /dev/null
+++ b/assets/javascripts/discourse/lib/wizard-json.js.es6
@@ -0,0 +1,178 @@
+import { schema, listProperties, camelCase, snakeCase } from '../lib/wizard';
+import EmberObject from '@ember/object';
+import { A } from "@ember/array";
+
+function present(val) {
+ if (val === null || val === undefined) {
+ return false;
+ } else if (typeof val === 'object') {
+ return Object.keys(val).length !== 0;
+ } else if (typeof val === 'string' || val.constructor === Array) {
+ return val && val.length;
+ } else {
+ return false;
+ }
+}
+
+function mapped(property, type) {
+ return schema[type].mapped.indexOf(property) > -1;
+}
+
+function castCase(property, value) {
+ return property.indexOf('_type') > -1 ? camelCase(value) : value;
+}
+
+function buildProperty(json, property, type) {
+ let value = json[property];
+
+ if (mapped(property, type) &&
+ present(value) &&
+ value.constructor === Array) {
+
+ let inputs = [];
+
+ value.forEach(inputJson => {
+ let input = {}
+
+ Object.keys(inputJson).forEach(inputKey => {
+ if (inputKey === 'pairs') {
+ let pairs = [];
+ let pairCount = inputJson.pairs.length;
+
+ inputJson.pairs.forEach(pairJson => {
+ let pair = {};
+
+ Object.keys(pairJson).forEach(pairKey => {
+ pair[pairKey] = castCase(pairKey, pairJson[pairKey]);
+ });
+
+ pair.pairCount = pairCount;
+
+ pairs.push(
+ EmberObject.create(pair)
+ );
+ });
+
+ input.pairs = pairs;
+ } else {
+ input[inputKey] = castCase(inputKey, inputJson[inputKey]);
+ }
+ });
+
+ inputs.push(
+ EmberObject.create(input)
+ );
+ });
+
+ return A(inputs);
+ } else {
+ return value;
+ }
+}
+
+function buildObject(json, type) {
+ let props = {
+ isNew: false
+ }
+
+ Object.keys(json).forEach(prop => {
+ props[prop] = buildProperty(json, prop, type)
+ });
+
+ return EmberObject.create(props);
+}
+
+function buildObjectArray(json, type) {
+ let array = A();
+
+ if (present(json)) {
+ json.forEach((objJson) => {
+ let object = buildObject(objJson, type);
+
+ if (hasAdvancedProperties(object, type)) {
+ object.set('showAdvanced', true);
+ }
+
+ array.pushObject(object);
+ });
+ }
+
+ return array;
+}
+
+function buildBasicProperties(json, type, props) {
+ listProperties(type).forEach((p) => {
+ props[p] = buildProperty(json, p, type);
+
+ if (hasAdvancedProperties(json, type)) {
+ result.showAdvanced = true;
+ }
+ });
+
+ return props;
+}
+
+function hasAdvancedProperties(object, type) {
+ return Object.keys(object).some(p => {
+ return schema[type].advanced.indexOf(p) > -1 && present(object[p]);
+ });
+}
+
+/// to be removed: necessary due to action array being moved from step to wizard
+function actionPatch(json) {
+ let actions = json.actions || [];
+
+ json.steps.forEach(step => {
+ if (step.actions && step.actions.length) {
+ step.actions.forEach(action => {
+ action.run_after = 'wizard_completion';
+ actions.push(action);
+ });
+ }
+ });
+
+ json.actions = actions;
+
+ return json;
+}
+///
+
+function buildProperties(json) {
+ let props = {
+ steps: A(),
+ actions: A()
+ };
+
+ if (present(json)) {
+ props.existingId = true;
+ props = buildBasicProperties(json, 'wizard', props);
+
+ if (present(json.steps)) {
+ json.steps.forEach((stepJson) => {
+ let stepProps = {
+ isNew: false
+ };
+
+ stepProps = buildBasicProperties(stepJson, 'step', stepProps);
+ stepProps.fields = buildObjectArray(stepJson.fields, 'field');
+
+ props.steps.pushObject(EmberObject.create(stepProps));
+ });
+ };
+
+ json = actionPatch(json); // to be removed - see above
+ props.actions = buildObjectArray(json.actions, 'action');
+ } else {
+ listProperties('wizard').forEach(prop => {
+ props[prop] = schema.wizard.basic[prop];
+ });
+ }
+
+ return props;
+}
+
+export {
+ buildProperties,
+ present,
+ mapped
+}
\ No newline at end of file
diff --git a/assets/javascripts/discourse/lib/wizard-mapper.js.es6 b/assets/javascripts/discourse/lib/wizard-mapper.js.es6
new file mode 100644
index 00000000..514619a4
--- /dev/null
+++ b/assets/javascripts/discourse/lib/wizard-mapper.js.es6
@@ -0,0 +1,168 @@
+import EmberObject from "@ember/object";
+import { A } from "@ember/array";
+
+// Inputs
+
+function defaultInputType(options = {}) {
+ return options.inputTypes.split(',')[0];
+}
+
+function mapInputTypes(types) {
+ return types.map(function(type) {
+ return {
+ id: type,
+ name: I18n.t(`admin.wizard.input.${type}.name`)
+ };
+ });
+}
+
+function inputTypesContent(options = {}) {
+ return options.inputTypes ?
+ mapInputTypes(options.inputTypes.split(',')) :
+ mapInputTypes(selectableInputTypes);
+}
+
+// connectorTypes
+
+const connectors = {
+ pair: [
+ 'equal',
+ 'greater',
+ 'less',
+ 'greater_or_equal',
+ 'less_or_equal',
+ 'regex'
+ ],
+ output: [
+ 'then',
+ 'set',
+ ],
+}
+
+function defaultConnector(connectorType, inputType, options={}) {
+ if (connectorType === 'input') {
+ return defaultInputType(options);
+ }
+ if (connectorType === 'pair') {
+ if (inputType === 'conditional') return 'equal';
+ if (inputType === 'association') return 'association';
+ if (inputType === 'validation') return 'equal';
+ }
+ if (connectorType === 'output') {
+ if (inputType === 'conditional') return 'then';
+ if (inputType === 'assignment') return 'set';
+ }
+ return 'equal';
+}
+
+function connectorContent(connectorType, inputType, opts) {
+ let connector = opts[`${connectorType}Connector`];
+
+ if ((!connector && connectorType === 'output') || inputType === 'association') {
+ connector = defaultConnector(connectorType, inputType);
+ }
+
+ let content = connector ? [connector] : connectors[connectorType];
+
+ return content.map(function(item) {
+ return {
+ id: item,
+ name: I18n.t(`admin.wizard.connector.${item}`)
+ };
+ });
+}
+
+// Selectors
+
+const selectionTypes = [
+ 'text',
+ 'list',
+ 'wizardField',
+ 'userField',
+ 'group',
+ 'category',
+ 'tag',
+ 'user'
+]
+
+function defaultSelectionType(inputType, options = {}) {
+ if (options[`${inputType}DefaultSelection`]) {
+ return options[`${inputType}DefaultSelection`];
+ }
+
+ let type = selectionTypes[0];
+
+ for (let t of selectionTypes) {
+ let inputTypes = options[`${t}Selection`];
+
+ if (inputTypes === true ||
+ ((typeof inputTypes === 'string') &&
+ inputTypes.split(',').indexOf(inputType) > -1)) {
+
+ type = t;
+ break;
+ }
+ }
+
+ return type;
+}
+
+// items
+
+function newPair(inputType, options = {}) {
+ let params = {
+ index: options.index,
+ pairCount: options.pairCount,
+ key: '',
+ key_type: defaultSelectionType('key', options),
+ value: '',
+ value_type: defaultSelectionType('value', options),
+ connector: defaultConnector('pair', inputType, options)
+ }
+
+ return EmberObject.create(params);
+}
+
+function newInput(options = {}, count) {
+ const inputType = defaultInputType(options);
+
+ let params = {
+ type: inputType,
+ pairs: A(
+ [
+ newPair(
+ inputType,
+ Object.assign({},
+ options,
+ { index: 0, pairCount: 1 }
+ )
+ )
+ ]
+ )
+ }
+
+ if (count > 0) {
+ params.connector = options.inputConnector;
+ }
+
+ if (['conditional', 'assignment'].indexOf(inputType) > -1 ||
+ options.outputDefaultSelection ||
+ options.outputConnector) {
+
+ params['output_type'] = defaultSelectionType('output', options);
+ params['output_connector'] = defaultConnector('output', inputType, options);
+ }
+
+ return EmberObject.create(params);
+}
+
+export {
+ defaultInputType,
+ defaultSelectionType,
+ defaultConnector,
+ connectorContent,
+ inputTypesContent,
+ selectionTypes,
+ newInput,
+ newPair
+}
\ No newline at end of file
diff --git a/assets/javascripts/discourse/lib/wizard.js.es6 b/assets/javascripts/discourse/lib/wizard.js.es6
new file mode 100644
index 00000000..d90886eb
--- /dev/null
+++ b/assets/javascripts/discourse/lib/wizard.js.es6
@@ -0,0 +1,331 @@
+import EmberObject from "@ember/object";
+
+function selectKitContent(content) {
+ return content.map(i => ({id: i, name: i}));
+}
+
+function generateName(id) {
+ return id ? sentenceCase(id) : '';
+}
+
+function generateId(name, opts={}) {
+ return name ? snakeCase(name) : '';
+}
+
+function sentenceCase(string) {
+ return string.replace(/[_\-]+/g, ' ')
+ .toLowerCase()
+ .replace(/(^\w|\b\w)/g, (m) => m.toUpperCase());
+}
+
+function snakeCase(string) {
+ return string.match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g)
+ .map(x => x.toLowerCase())
+ .join('_');
+}
+
+function camelCase(string) {
+ return string.replace(/([-_][a-z])/ig, ($1) => {
+ return $1.toUpperCase()
+ .replace('-', '')
+ .replace('_', '');
+ });
+}
+
+const userProperties = [
+ 'name',
+ 'username',
+ 'email',
+ 'avatar',
+ 'date_of_birth',
+ 'title',
+ 'profile_background',
+ 'card_background',
+ 'locale',
+ 'location',
+ 'website',
+ 'bio_raw',
+ 'trust_level'
+];
+
+const wizardProperties = {
+ basic: {
+ id: null,
+ name: null,
+ background: null,
+ save_submissions: true,
+ multiple_submissions: null,
+ after_signup: null,
+ after_time: null,
+ after_time_scheduled: null,
+ required: null,
+ prompt_completion: null,
+ restart_on_revisit: null,
+ theme_id: null,
+ permitted: null
+ },
+ mapped: [
+ 'permitted'
+ ],
+ advanced: [
+ 'restart_on_revisit',
+ ],
+ required: [
+ 'id',
+ ],
+ dependent: {
+ after_time: 'after_time_scheduled'
+ },
+ objectArrays: {
+ step: {
+ property: 'steps',
+ required: false
+ },
+ action: {
+ property: 'actions',
+ required: false
+ }
+ }
+};
+
+const stepProperties = {
+ basic: {
+ id: null,
+ title: null,
+ key: null,
+ banner: null,
+ raw_description: null,
+ required_data: null,
+ required_data_message: null,
+ permitted_params: null
+ },
+ mapped: [
+ 'required_data',
+ 'permitted_params'
+ ],
+ advanced: [
+ 'required_data',
+ 'permitted_params'
+ ],
+ required: [
+ 'id'
+ ],
+ dependent: {
+ },
+ objectArrays: {
+ field: {
+ property: 'fields',
+ required: false
+ }
+ }
+}
+
+const fieldProperties = {
+ basic: {
+ id: null,
+ label: null,
+ image: null,
+ description: null,
+ required: null,
+ key: null,
+ type: null
+ },
+ types: {
+ text: {
+ min_length: null
+ },
+ textarea: {
+ min_length: null
+ },
+ composer: {
+ min_length: null
+ },
+ text_only: {
+ },
+ number: {
+ },
+ checkbox: {
+ },
+ url: {
+ min_length: null
+ },
+ upload: {
+ file_types: '.jpg,.png'
+ },
+ dropdown: {
+ prefill: null,
+ content: null
+ },
+ tag: {
+ limit: null,
+ prefill: null,
+ content: null
+ },
+ category: {
+ limit: 1,
+ property: 'id',
+ prefill: null,
+ content: null
+ },
+ group: {
+ prefill: null,
+ content: null
+ },
+ user_selector: {
+ }
+ },
+ mapped: [
+ 'prefill',
+ 'content'
+ ],
+ advanced: [
+ 'property',
+ 'key'
+ ],
+ required: [
+ 'id',
+ 'type'
+ ],
+ dependent: {
+ },
+ objectArrays: {
+ }
+}
+
+const actionProperties = {
+ basic: {
+ id: null,
+ run_after: 'wizard_completion',
+ type: null
+ },
+ types: {
+ create_topic: {
+ title: null,
+ post: null,
+ post_builder: null,
+ post_template: null,
+ category: null,
+ tags: null,
+ custom_fields: null,
+ skip_redirect: null
+ },
+ send_message: {
+ title: null,
+ post: null,
+ post_builder: null,
+ post_template: null,
+ skip_redirect: null,
+ custom_fields: null,
+ required: null,
+ recipient: null
+ },
+ open_composer: {
+ title: null,
+ post: null,
+ post_builder: null,
+ post_template: null,
+ category: null,
+ tags: null,
+ custom_fields: null
+ },
+ update_profile: {
+ profile_updates: null,
+ custom_fields: null
+ },
+ add_to_group: {
+ group: null
+ },
+ route_to: {
+ url: null,
+ code: null
+ }
+ },
+ mapped: [
+ 'title',
+ 'category',
+ 'tags',
+ 'custom_fields',
+ 'required',
+ 'recipient',
+ 'profile_updates',
+ 'group'
+ ],
+ advanced: [
+ 'code',
+ 'custom_fields',
+ 'skip_redirect',
+ 'required'
+ ],
+ required: [
+ 'id',
+ 'type'
+ ],
+ dependent: {
+ },
+ objectArrays: {
+ }
+}
+
+if (Discourse.SiteSettings.wizard_apis_enabled) {
+ actionProperties.types.send_to_api = {
+ api: null,
+ api_endpoint: null,
+ api_body: null
+ }
+}
+
+const schema = {
+ wizard: wizardProperties,
+ step: stepProperties,
+ field: fieldProperties,
+ action: actionProperties
+}
+
+function listProperties(type, objectType = null) {
+ let properties = Object.keys(schema[type].basic);
+
+ if (schema[type].types && objectType) {
+ properties = properties.concat(Object.keys(schema[type].types[objectType]));
+ }
+
+ return properties;
+}
+
+function wizardFieldList(steps = [], opts = {}) {
+ let upToIndex = null;
+
+ if (opts.upTo) {
+ upToIndex = steps.map((s) => (s.id)).indexOf(opts.upTo);
+ }
+
+ return steps.reduce((result, step, index) => {
+ let fields = step.fields;
+
+ if (fields && fields.length > 0) {
+
+ if (upToIndex === null || index < upToIndex) {
+ result.push(...fields.map((field) => {
+ return EmberObject.create({
+ id: field.id,
+ label: `${field.label} (${step.id}, ${field.id})`,
+ type: field.type
+ });
+ }));
+ }
+ }
+
+ return result;
+ }, []);
+}
+
+export {
+ selectKitContent,
+ generateName,
+ generateId,
+ camelCase,
+ snakeCase,
+ schema,
+ userProperties,
+ listProperties,
+ wizardFieldList
+};
\ No newline at end of file
diff --git a/assets/javascripts/discourse/models/custom-wizard-api.js.es6 b/assets/javascripts/discourse/models/custom-wizard-api.js.es6
index b335b88a..5359534e 100644
--- a/assets/javascripts/discourse/models/custom-wizard-api.js.es6
+++ b/assets/javascripts/discourse/models/custom-wizard-api.js.es6
@@ -1,8 +1,10 @@
import { ajax } from 'discourse/lib/ajax';
-import { default as computed } from 'ember-addons/ember-computed-decorators';
+import { default as discourseComputed } from 'discourse-common/utils/decorators';
+import EmberObject from "@ember/object";
+import { A } from "@ember/array";
-const CustomWizardApi = Discourse.Model.extend({
- @computed('name')
+const CustomWizardApi = EmberObject.extend({
+ @discourseComputed('name')
redirectUri(name) {
let nameParam = name.toString().dasherize();
const baseUrl = location.protocol+'//'+location.hostname+(location.port ? ':'+location.port: '');
@@ -27,14 +29,14 @@ CustomWizardApi.reopenClass({
clientSecret: authorization.client_secret,
username: authorization.username,
password: authorization.password,
- authParams: Ember.A(authorization.auth_params),
+ authParams: A(authorization.auth_params),
authorized: authorization.authorized,
accessToken: authorization.access_token,
refreshToken: authorization.refresh_token,
code: authorization.code,
tokenExpiresAt: authorization.token_expires_at,
tokenRefreshAt: authorization.token_refresh_at,
- endpoints: Ember.A(endpoints),
+ endpoints: A(endpoints),
isNew: params.isNew,
log: params.log
});
diff --git a/assets/javascripts/discourse/models/custom-wizard-logs.js.es6 b/assets/javascripts/discourse/models/custom-wizard-logs.js.es6
new file mode 100644
index 00000000..10f46d53
--- /dev/null
+++ b/assets/javascripts/discourse/models/custom-wizard-logs.js.es6
@@ -0,0 +1,17 @@
+import { ajax } from 'discourse/lib/ajax';
+import { popupAjaxError } from 'discourse/lib/ajax-error';
+import EmberObject from "@ember/object";
+
+const CustomWizardLogs = EmberObject.extend();
+
+CustomWizardLogs.reopenClass({
+ list(page = 0) {
+ return ajax('/admin/wizards/logs', {
+ data: {
+ page
+ }
+ }).catch(popupAjaxError);
+ }
+});
+
+export default CustomWizardLogs;
\ No newline at end of file
diff --git a/assets/javascripts/discourse/models/custom-wizard.js.es6 b/assets/javascripts/discourse/models/custom-wizard.js.es6
index e7860c01..e65ad169 100644
--- a/assets/javascripts/discourse/models/custom-wizard.js.es6
+++ b/assets/javascripts/discourse/models/custom-wizard.js.es6
@@ -1,57 +1,30 @@
import { ajax } from 'discourse/lib/ajax';
+import EmberObject from "@ember/object";
+import { buildProperties, present, mapped } from '../lib/wizard-json';
+import { schema, listProperties, camelCase, snakeCase } from '../lib/wizard';
+import { Promise } from "rsvp";
-const wizardProperties = [
- 'name',
- 'background',
- 'save_submissions',
- 'multiple_submissions',
- 'after_signup',
- 'after_time',
- 'after_time_scheduled',
- 'required',
- 'prompt_completion',
- 'restart_on_revisit',
- 'min_trust',
- 'theme_id'
-];
-
-const CustomWizard = Discourse.Model.extend({
- save() {
- return new Ember.RSVP.Promise((resolve, reject) => {
-
- const id = this.get('id');
- if (!id || !id.underscore()) return reject({ error: 'id_required' });
-
- let wizard = { id: id.underscore() };
-
- wizardProperties.forEach((p) => {
- const value = this.get(p);
- if (value) wizard[p] = value;
- });
-
- if (wizard['after_time'] && !wizard['after_time_scheduled']) {
- return reject({ error: 'after_time_need_time' });
+const CustomWizard = EmberObject.extend({
+ save(opts) {
+ return new Promise((resolve, reject) => {
+ let wizard = this.buildJson(this, 'wizard');
+
+ if (wizard.error) {
+ reject(wizard);
+ }
+
+ let data = {
+ wizard
};
-
- const steps = this.get('steps');
- if (steps.length > 0) {
- const stepsResult = this.buildSteps(steps);
- if (stepsResult.error) {
- reject({ error: stepsResult.error });
- } else {
- wizard['steps'] = stepsResult.steps;
- }
+
+ if (opts.create) {
+ data.create = true;
}
-
- if (steps.length < 1 || !wizard['steps'] || wizard['steps'].length < 1) {
- return reject({ error: 'steps_required' });
- }
-
- ajax("/admin/wizards/custom/save", {
+
+ ajax(`/admin/wizards/wizard/${wizard.id}`, {
type: 'PUT',
- data: {
- wizard: JSON.stringify(wizard)
- }
+ contentType: "application/json",
+ data: JSON.stringify(data)
}).then((result) => {
if (result.error) {
reject(result);
@@ -62,216 +35,177 @@ const CustomWizard = Discourse.Model.extend({
});
},
- buildSteps(stepsObj) {
- let steps = [];
- let error = null;
-
- stepsObj.some((s) => {
- if (!s.id || !s.id.underscore()) {
- error = 'id_required';
- return;
- };
-
- let step = { id: s.id.underscore() };
-
- if (s.title) step['title'] = s.title;
- if (s.key) step['key'] = s.key;
- if (s.banner) step['banner'] = s.banner;
- if (s.raw_description) step['raw_description'] = s.raw_description;
- if (s.required_data) step['required_data'] = s.required_data;
- if (s.required_data_message) step['required_data_message'] = s.required_data_message;
- if (s.permitted_params) step['permitted_params'] = s.permitted_params;
-
- const fields = s.get('fields');
- if (fields.length) {
- step['fields'] = [];
-
- fields.some((f) => {
- let id = f.id;
-
- if (!id || !id.underscore()) {
- error = 'id_required';
- return;
- }
-
- if (!f.type) {
- error = 'type_required';
- return;
- }
-
- f.set('id', id.underscore());
-
- if (f.label === '') delete f.label;
- if (f.description === '') delete f.description;
-
- if (f.type === 'dropdown') {
- const choices = f.choices;
- if ((!choices || choices.length < 1) && !f.choices_key && !f.choices_preset) {
- error = 'field.need_choices';
- return;
- }
-
- if (f.dropdown_none === '') delete f.dropdown_none;
- }
-
- delete f.isNew;
-
- step['fields'].push(f);
- });
-
- if (error) return;
+ buildJson(object, type, result = {}) {
+ let objectType = object.type || null;
+
+ if (schema[type].types) {
+ if (!objectType) {
+ result.error = {
+ type: 'required',
+ params: { type, property: 'type' }
+ }
+ return result;
}
-
- const actions = s.actions;
- if (actions.length) {
- step['actions'] = [];
-
- actions.some((a) => {
- let id = a.get('id');
- if (!id || !id.underscore()) {
- error = 'id_required';
- return;
- }
- //check if api_body is valid JSON
- let api_body = a.get('api_body');
- if (api_body) {
- try {
- JSON.parse(api_body);
- } catch (e) {
- error = 'invalid_api_body';
- return;
- }
- }
-
- a.set('id', id.underscore());
-
- delete a.isNew;
-
- step['actions'].push(a);
- });
-
- if (error) return;
+ }
+
+ for (let property of listProperties(type, objectType)) {
+ let value = object.get(property);
+
+ result = this.validateValue(property, value, object, type, result);
+
+ if (result.error) {
+ break;
+ }
+
+ if (mapped(property, type)) {
+ value = this.buildMappedJson(value);
+ }
+
+ if (value !== undefined && value !== null) {
+ result[property] = value;
}
-
- steps.push(step);
- });
-
- if (error) {
- return { error };
- } else {
- return { steps };
};
+
+ if (!result.error) {
+ for (let arrayObjectType of Object.keys(schema[type].objectArrays)) {
+ let arraySchema = schema[type].objectArrays[arrayObjectType];
+ let objectArray = object.get(arraySchema.property);
+
+ if (arraySchema.required && !present(objectArray)) {
+ result.error = {
+ type: 'required',
+ params: { type, property: arraySchema.property }
+ }
+ break;
+ }
+
+ result[arraySchema.property] = [];
+
+ for (let item of objectArray) {
+ let itemProps = this.buildJson(item, arrayObjectType);
+
+ if (itemProps.error) {
+ result.error = itemProps.error;
+ break;
+ } else {
+ result[arraySchema.property].push(itemProps);
+ }
+ }
+ };
+ }
+
+ return result;
+ },
+
+ validateValue(property, value, object, type, result) {
+ if (schema[type].required.indexOf(property) > -1 && !value) {
+ result.error = {
+ type: 'required',
+ params: { type, property }
+ }
+ }
+
+ let dependent = schema[type].dependent[property];
+ if (dependent && value && !object[dependent]) {
+ result.error = {
+ type: 'dependent',
+ params: { property, dependent }
+ }
+ }
+
+ if (property === 'api_body') {
+ try {
+ value = JSON.parse(value);
+ } catch (e) {
+ result.error = {
+ type: 'invalid',
+ params: { type, property }
+ }
+ }
+ }
+
+ return result;
+ },
+
+ buildMappedJson(inputs) {
+ if (!inputs || !inputs.length) return false;
+
+ let result = [];
+
+ inputs.forEach(inpt => {
+ let input = {
+ type: inpt.type,
+ };
+
+ if (inpt.connector) {
+ input.connector = inpt.connector;
+ }
+
+ if (present(inpt.output)) {
+ input.output = inpt.output;
+ input.output_type = snakeCase(inpt.output_type);
+ input.output_connector = inpt.output_connector;
+ }
+
+ if (present(inpt.pairs)) {
+ input.pairs = [];
+
+ inpt.pairs.forEach(pr => {
+ if (present(pr.key) && present(pr.value)) {
+
+ let pairParams = {
+ index: pr.index,
+ key: pr.key,
+ key_type: snakeCase(pr.key_type),
+ value: pr.value,
+ value_type: snakeCase(pr.value_type),
+ connector: pr.connector
+ }
+
+ input.pairs.push(pairParams);
+ }
+ });
+ }
+
+ if ((input.type === 'assignment' && present(input.output)) ||
+ present(input.pairs)) {
+
+ result.push(input);
+ }
+ });
+
+ if (!result.length) {
+ result = false;
+ }
+
+ return result;
},
remove() {
- return ajax("/admin/wizards/custom/remove", {
- type: 'DELETE',
- data: {
- id: this.get('id')
- }
+ return ajax(`/admin/wizards/wizard/${this.id}`, {
+ type: 'DELETE'
}).then(() => this.destroy());
}
});
CustomWizard.reopenClass({
all() {
- return ajax("/admin/wizards/custom/all", {
+ return ajax("/admin/wizards/wizard", {
type: 'GET'
}).then(result => {
- return result.wizards.map(w => CustomWizard.create(w));
+ return result.wizard_list;
});
},
submissions(wizardId) {
return ajax(`/admin/wizards/submissions/${wizardId}`, {
type: "GET"
- }).then(result => {
- return result.submissions;
});
},
- create(w) {
+ create(wizardJson = {}) {
const wizard = this._super.apply(this);
- let steps = Ember.A();
- let props = { steps };
-
- if (w) {
- props['id'] = w.id;
- props['existingId'] = true;
-
- wizardProperties.forEach((p) => {
- props[p] = w[p];
- });
-
- if (w.steps && w.steps.length) {
- w.steps.forEach((s) => {
- // clean empty strings
- Object.keys(s).forEach((key) => (s[key] === '') && delete s[key]);
-
- let fields = Ember.A();
-
- if (s.fields && s.fields.length) {
- s.fields.forEach((f) => {
- Object.keys(f).forEach((key) => (f[key] === '') && delete f[key]);
-
- const fieldParams = { isNew: false };
- let field = Ember.Object.create($.extend(f, fieldParams));
-
- if (f.choices) {
- let choices = Ember.A();
-
- f.choices.forEach((c) => {
- choices.pushObject(Ember.Object.create(c));
- });
-
- field.set('choices', choices);
- }
-
- fields.pushObject(field);
- });
- }
-
- let actions = Ember.A();
- if (s.actions && s.actions.length) {
- s.actions.forEach((a) => {
- const actionParams = { isNew: false };
- const action = Ember.Object.create($.extend(a, actionParams));
- actions.pushObject(action);
- });
- }
-
- steps.pushObject(Ember.Object.create({
- id: s.id,
- key: s.key,
- title: s.title,
- raw_description: s.raw_description,
- banner: s.banner,
- required_data: s.required_data,
- required_data_message: s.required_data_message,
- permitted_params: s.permitted_params,
- fields,
- actions,
- isNew: false
- }));
- });
- };
- } else {
- props['id'] = '';
- props['name'] = '';
- props['background'] = '';
- props['save_submissions'] = true;
- props['multiple_submissions'] = false;
- props['after_signup'] = false;
- props['after_time'] = false;
- props['required'] = false;
- props['prompt_completion'] = false;
- props['restart_on_revisit'] = false;
- props['min_trust'] = 0;
- props['steps'] = Ember.A();
- };
-
- wizard.setProperties(props);
-
+ wizard.setProperties(buildProperties(wizardJson));
return wizard;
}
});
diff --git a/assets/javascripts/discourse/routes/admin-wizard-submissions.js.es6 b/assets/javascripts/discourse/routes/admin-wizard-submissions.js.es6
deleted file mode 100644
index bff8011f..00000000
--- a/assets/javascripts/discourse/routes/admin-wizard-submissions.js.es6
+++ /dev/null
@@ -1,29 +0,0 @@
-import CustomWizard from '../models/custom-wizard';
-
-export default Discourse.Route.extend({
- model(params) {
- return CustomWizard.submissions(params.wizard_id);
- },
-
- setupController(controller, model) {
- let fields = [];
- model.forEach((s) => {
- Object.keys(s).forEach((k) => {
- if (fields.indexOf(k) < 0) {
- fields.push(k);
- }
- });
- });
-
- let submissions = [];
- model.forEach((s) => {
- let submission = {};
- fields.forEach((f) => {
- submission[f] = s[f];
- });
- submissions.push(submission);
- });
-
- controller.setProperties({ submissions, fields });
- }
-});
diff --git a/assets/javascripts/discourse/routes/admin-wizard.js.es6 b/assets/javascripts/discourse/routes/admin-wizard.js.es6
deleted file mode 100644
index 383e6dc8..00000000
--- a/assets/javascripts/discourse/routes/admin-wizard.js.es6
+++ /dev/null
@@ -1,78 +0,0 @@
-import CustomWizard from '../models/custom-wizard';
-import { ajax } from 'discourse/lib/ajax';
-import { generateSelectKitContent } from '../lib/custom-wizard';
-
-export default Discourse.Route.extend({
- beforeModel() {
- const param = this.paramsFor('adminWizard').wizard_id;
- const wizards = this.modelFor('admin-wizards-custom');
-
- if (wizards.length && (param === 'first' || param === 'last')) {
- const wizard = wizards.get(`${param}Object`);
- if (wizard) {
- this.transitionTo('adminWizard', wizard.id.dasherize());
- }
- }
- },
-
- model(params) {
- const wizardId = params.wizard_id;
-
- if (wizardId === 'new') {
- this.set('newWizard', true);
- return CustomWizard.create();
- };
- this.set('newWizard', false);
-
- const wizard = this.modelFor('admin-wizards-custom').findBy('id', wizardId.underscore());
-
- if (!wizard) return this.transitionTo('adminWizard', 'new');
-
- return wizard;
- },
-
- afterModel(model) {
- return Ember.RSVP.all([
- this._getFieldTypes(model),
- this._getThemes(model),
- this._getApis(model)
- ]);
- },
-
- _getFieldTypes(model) {
- return ajax('/admin/wizards/field-types')
- .then((result) => {
- model.set(
- 'fieldTypes',
- generateSelectKitContent([...result.types])
- )
- });
- },
-
- _getThemes(model) {
- return this.store.findAll('theme').then((result) => {
- model.set('themes', result.content);
- });
- },
-
- _getApis(model) {
- return ajax('/admin/wizards/apis')
- .then((result) => model.set('apis', result));
- },
-
- setupController(controller, model) {
- const newWizard = this.get('newWizard');
- const steps = model.get('steps') || [];
- controller.setProperties({
- newWizard,
- model,
- currentStep: steps[0]
- });
- },
-
- actions: {
- refreshWizard() {
- this.refresh();
- }
- }
-});
diff --git a/assets/javascripts/discourse/routes/admin-wizards-api-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-api-show.js.es6
new file mode 100644
index 00000000..6d710c41
--- /dev/null
+++ b/assets/javascripts/discourse/routes/admin-wizards-api-show.js.es6
@@ -0,0 +1,22 @@
+import CustomWizardApi from '../models/custom-wizard-api';
+import DiscourseRoute from "discourse/routes/discourse";
+
+export default DiscourseRoute.extend({
+ queryParams: {
+ refresh_list: {
+ refreshModel: true
+ }
+ },
+
+ model(params) {
+ if (params.name === 'new') {
+ return CustomWizardApi.create({ isNew: true });
+ } else {
+ return CustomWizardApi.find(params.name);
+ }
+ },
+
+ setupController(controller, model){
+ controller.set("api", model);
+ }
+});
diff --git a/assets/javascripts/discourse/routes/admin-wizards-api.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-api.js.es6
index 58f624b5..62ecd8fe 100644
--- a/assets/javascripts/discourse/routes/admin-wizards-api.js.es6
+++ b/assets/javascripts/discourse/routes/admin-wizards-api.js.es6
@@ -1,21 +1,7 @@
-import CustomWizardApi from '../models/custom-wizard-api';
+import DiscourseRoute from "discourse/routes/discourse";
-export default Discourse.Route.extend({
- queryParams: {
- refresh_list: {
- refreshModel: true
- }
- },
-
- model(params) {
- if (params.name === 'new') {
- return CustomWizardApi.create({ isNew: true });
- } else {
- return CustomWizardApi.find(params.name);
- }
- },
-
- setupController(controller, model){
- controller.set("api", model);
+export default DiscourseRoute.extend({
+ beforeModel() {
+ this.transitionTo('adminWizardsApiShow');
}
-});
+});
\ No newline at end of file
diff --git a/assets/javascripts/discourse/routes/admin-wizards-apis.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-apis.js.es6
deleted file mode 100644
index 54174c6b..00000000
--- a/assets/javascripts/discourse/routes/admin-wizards-apis.js.es6
+++ /dev/null
@@ -1,31 +0,0 @@
-import CustomWizardApi from '../models/custom-wizard-api';
-
-export default Discourse.Route.extend({
- model() {
- return CustomWizardApi.list();
- },
-
- afterModel(model) {
- const apiParams = this.paramsFor('admin-wizards-api');
-
- if (model.length) {
- if (!apiParams.name) {
- this.transitionTo('adminWizardsApi', model[0].name.dasherize());
- } else {
- return;
- }
- } else {
- this.transitionTo('adminWizardsApi', 'new');
- }
- },
-
- setupController(controller, model){
- controller.set("model", model);
- },
-
- actions: {
- refreshModel() {
- this.refresh();
- }
- }
-});
diff --git a/assets/javascripts/discourse/routes/admin-wizards-custom-index.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-custom-index.js.es6
deleted file mode 100644
index 7231c01d..00000000
--- a/assets/javascripts/discourse/routes/admin-wizards-custom-index.js.es6
+++ /dev/null
@@ -1,5 +0,0 @@
-export default Discourse.Route.extend({
- redirect() {
- this.transitionTo('adminWizard', 'first');
- }
-});
diff --git a/assets/javascripts/discourse/routes/admin-wizards-custom.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-custom.js.es6
deleted file mode 100644
index 49803495..00000000
--- a/assets/javascripts/discourse/routes/admin-wizards-custom.js.es6
+++ /dev/null
@@ -1,26 +0,0 @@
-import CustomWizard from '../models/custom-wizard';
-
-export default Discourse.Route.extend({
- model() {
- return CustomWizard.all();
- },
-
- afterModel(model) {
- const transitionToWizard = this.get('transitionToWizard');
- if (transitionToWizard && model.length) {
- this.set('transitionToWizard', null);
- this.transitionTo('adminWizard', transitionToWizard);
- };
- },
-
- setupController(controller, model){
- controller.set("model", model.toArray());
- },
-
- actions: {
- refreshAllWizards() {
- this.set('transitionToWizard', 'last');
- this.refresh();
- }
- }
-});
diff --git a/assets/javascripts/discourse/routes/admin-wizards-index.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-index.js.es6
deleted file mode 100644
index 149e51be..00000000
--- a/assets/javascripts/discourse/routes/admin-wizards-index.js.es6
+++ /dev/null
@@ -1,5 +0,0 @@
-export default Discourse.Route.extend({
- redirect() {
- this.transitionTo('adminWizardsCustom');
- }
-});
diff --git a/assets/javascripts/discourse/routes/admin-wizards-logs.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-logs.js.es6
new file mode 100644
index 00000000..1c1de391
--- /dev/null
+++ b/assets/javascripts/discourse/routes/admin-wizards-logs.js.es6
@@ -0,0 +1,11 @@
+import CustomWizardLogs from '../models/custom-wizard-logs';
+
+export default Discourse.Route.extend({
+ model() {
+ return CustomWizardLogs.list();
+ },
+
+ setupController(controller, model) {
+ controller.set('logs', model);
+ }
+})
\ No newline at end of file
diff --git a/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6
new file mode 100644
index 00000000..d4818024
--- /dev/null
+++ b/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6
@@ -0,0 +1,36 @@
+import CustomWizard from '../models/custom-wizard';
+import DiscourseRoute from "discourse/routes/discourse";
+
+export default DiscourseRoute.extend({
+ model(params) {
+ return CustomWizard.submissions(params.wizardId);
+ },
+
+ setupController(controller, model) {
+ if (model.submissions) {
+ let fields = [];
+ model.submissions.forEach((s) => {
+ Object.keys(s).forEach((k) => {
+ if (fields.indexOf(k) < 0) {
+ fields.push(k);
+ }
+ });
+ });
+
+ let submissions = [];
+ model.submissions.forEach((s) => {
+ let submission = {};
+ fields.forEach((f) => {
+ submission[f] = s[f];
+ });
+ submissions.push(submission);
+ });
+
+ controller.setProperties({
+ wizard: model.wizard,
+ submissions,
+ fields
+ });
+ }
+ }
+});
diff --git a/assets/javascripts/discourse/routes/admin-wizards-submissions.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-submissions.js.es6
index d652aada..6dff576d 100644
--- a/assets/javascripts/discourse/routes/admin-wizards-submissions.js.es6
+++ b/assets/javascripts/discourse/routes/admin-wizards-submissions.js.es6
@@ -1,11 +1,24 @@
-import CustomWizard from '../models/custom-wizard';
+import DiscourseRoute from "discourse/routes/discourse";
+import { ajax } from 'discourse/lib/ajax';
-export default Discourse.Route.extend({
+export default DiscourseRoute.extend({
model() {
- return CustomWizard.all();
+ return ajax(`/admin/wizards/wizard`);
},
-
- setupController(controller, model){
- controller.set("model", model);
+
+ setupController(controller, model) {
+ const showParams = this.paramsFor('adminWizardsSubmissionsShow');
+
+ controller.setProperties({
+ wizardId: showParams.wizardId,
+ wizardList: model.wizard_list
+ })
+ },
+
+ actions: {
+ changeWizard(wizardId) {
+ this.controllerFor('adminWizardsSubmissions').set('wizardId', wizardId);
+ this.transitionTo('adminWizardsSubmissionsShow', wizardId);
+ }
}
-});
+});
\ No newline at end of file
diff --git a/assets/javascripts/discourse/routes/admin-wizards-transfer.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-transfer.js.es6
index b86b49cc..faba2e0e 100644
--- a/assets/javascripts/discourse/routes/admin-wizards-transfer.js.es6
+++ b/assets/javascripts/discourse/routes/admin-wizards-transfer.js.es6
@@ -1,7 +1,12 @@
import CustomWizard from '../models/custom-wizard';
+import DiscourseRoute from "discourse/routes/discourse";
-export default Discourse.Route.extend({
+export default DiscourseRoute.extend({
model() {
return CustomWizard.all();
+ },
+
+ setupController(controller, model) {
+ controller.set('wizards', model)
}
});
diff --git a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6
new file mode 100644
index 00000000..d2e7ff2f
--- /dev/null
+++ b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6
@@ -0,0 +1,37 @@
+import CustomWizard from '../models/custom-wizard';
+import { ajax } from 'discourse/lib/ajax';
+import DiscourseRoute from "discourse/routes/discourse";
+import { selectKitContent } from '../lib/wizard';
+
+export default DiscourseRoute.extend({
+ model(params) {
+ if (params.wizardId === 'create') {
+ return { create: true };
+ } else {
+ return ajax(`/admin/wizards/wizard/${params.wizardId}`);
+ }
+ },
+
+ afterModel(model) {
+ if (model.none) {
+ return this.transitionTo('adminWizardsWizard');
+ }
+ },
+
+ setupController(controller, model) {
+ const parentModel = this.modelFor('adminWizardsWizard');
+ const wizard = CustomWizard.create((!model || model.create) ? {} : model);
+
+ controller.setProperties({
+ wizardList: parentModel.wizard_list,
+ fieldTypes: selectKitContent(parentModel.field_types),
+ userFields: parentModel.userFields,
+ apis: parentModel.apis,
+ themes: parentModel.themes,
+ wizard,
+ currentStep: wizard.steps[0],
+ currentAction: wizard.actions[0],
+ creating: model.create
+ });
+ }
+});
diff --git a/assets/javascripts/discourse/routes/admin-wizards-wizard.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-wizard.js.es6
new file mode 100644
index 00000000..a4efdd8c
--- /dev/null
+++ b/assets/javascripts/discourse/routes/admin-wizards-wizard.js.es6
@@ -0,0 +1,98 @@
+import DiscourseRoute from "discourse/routes/discourse";
+import { userProperties, generateName } from '../lib/wizard';
+import { set } from "@ember/object";
+import { all } from "rsvp";
+import { ajax } from 'discourse/lib/ajax';
+
+export default DiscourseRoute.extend({
+ model() {
+ return ajax("/admin/wizards/wizard");
+ },
+
+ afterModel(model) {
+ return all([
+ this._getThemes(model),
+ this._getApis(model),
+ this._getUserFields(model)
+ ]);
+ },
+
+ _getThemes(model) {
+ return ajax('/admin/themes')
+ .then((result) => {
+ set(model, 'themes', result.themes.map(t => {
+ return {
+ id: t.id,
+ name: t.name
+ }
+ }));
+ });
+ },
+
+ _getApis(model) {
+ return ajax('/admin/wizards/api')
+ .then((result) => set(model, 'apis', result));
+ },
+
+ _getUserFields(model) {
+ return this.store.findAll('user-field').then((result) => {
+ if (result && result.content) {
+ set(model, 'userFields',
+ result.content.map((f) => ({
+ id: `user_field_${f.id}`,
+ name: f.name
+ })).concat(
+ userProperties.map((f) => ({
+ id: f,
+ name: generateName(f)
+ }))
+ )
+ );
+ }
+ });
+ },
+
+ currentWizard() {
+ const params = this.paramsFor('adminWizardsWizardShow');
+
+ if (params && params.wizardId) {
+ return params.wizardId;
+ } else {
+ return null;
+ }
+ },
+
+ setupController(controller, model) {
+ let props = {
+ wizardList: model.wizard_list,
+ wizardId: this.currentWizard()
+ }
+
+ controller.setProperties(props);
+ },
+
+ actions: {
+ changeWizard(wizardId) {
+ this.controllerFor('adminWizardsWizard').set('wizardId', wizardId);
+
+ if (wizardId) {
+ this.transitionTo('adminWizardsWizardShow', wizardId);
+ } else {
+ this.transitionTo('adminWizardsWizard');
+ }
+ },
+
+ afterDestroy() {
+ this.transitionTo('adminWizardsWizard').then(() => this.refresh());
+ },
+
+ afterSave(wizardId) {
+ this.refresh().then(() => this.send('changeWizard', wizardId));
+ },
+
+ createWizard() {
+ this.controllerFor('adminWizardsWizard').set('wizardId', 'create');
+ this.transitionTo('adminWizardsWizardShow', 'create');
+ }
+ }
+});
\ No newline at end of file
diff --git a/assets/javascripts/discourse/routes/admin-wizards.js.es6 b/assets/javascripts/discourse/routes/admin-wizards.js.es6
new file mode 100644
index 00000000..7647207a
--- /dev/null
+++ b/assets/javascripts/discourse/routes/admin-wizards.js.es6
@@ -0,0 +1,9 @@
+import DiscourseRoute from "discourse/routes/discourse";
+
+export default DiscourseRoute.extend({
+ beforeModel(transition) {
+ if (transition.targetName === "adminWizards.index") {
+ this.transitionTo('adminWizardsWizard');
+ }
+ },
+});
\ No newline at end of file
diff --git a/assets/javascripts/discourse/templates/admin-wizard-submissions.hbs b/assets/javascripts/discourse/templates/admin-wizard-submissions.hbs
deleted file mode 100644
index ec8a55ff..00000000
--- a/assets/javascripts/discourse/templates/admin-wizard-submissions.hbs
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
- {{#each fields as |f|}}
- {{f}} |
- {{/each}}
-
- {{#each submissions as |s|}}
-
- {{#each-in s as |k v|}}
- {{v}} |
- {{/each-in}}
-
- {{/each}}
-
-
diff --git a/assets/javascripts/discourse/templates/admin-wizard.hbs b/assets/javascripts/discourse/templates/admin-wizard.hbs
deleted file mode 100644
index 2d1f2d0d..00000000
--- a/assets/javascripts/discourse/templates/admin-wizard.hbs
+++ /dev/null
@@ -1,150 +0,0 @@
-
-
-
-
-
-
-
{{i18n 'admin.wizard.id'}}
-
-
- {{input name="name" value=model.id placeholderKey="admin.wizard.id_placeholder" disabled=model.existingId}}
-
-
-
-
-
-
{{i18n 'admin.wizard.name'}}
-
-
- {{input name="name" value=model.name placeholderKey="admin.wizard.name_placeholder"}}
-
-
-
-
-
-
{{i18n 'admin.wizard.background'}}
-
-
- {{input name="background" value=model.background placeholderKey="admin.wizard.background_placeholder"}}
-
-
-
-
-
-
{{i18n 'admin.wizard.save_submissions'}}
-
-
- {{input type='checkbox' checked=model.save_submissions}}
- {{i18n 'admin.wizard.save_submissions_label'}}
-
-
-
-
-
-
{{i18n 'admin.wizard.multiple_submissions'}}
-
-
- {{input type='checkbox' checked=model.multiple_submissions}}
- {{i18n 'admin.wizard.multiple_submissions_label'}}
-
-
-
-
-
-
{{i18n 'admin.wizard.required'}}
-
-
- {{input type='checkbox' checked=model.required}}
- {{i18n 'admin.wizard.required_label'}}
-
-
-
-
-
-
{{i18n 'admin.wizard.after_signup'}}
-
-
- {{input type='checkbox' checked=model.after_signup}}
- {{i18n 'admin.wizard.after_signup_label'}}
-
-
-
-
-
-
{{i18n 'admin.wizard.after_time'}}
-
-
- {{input type='checkbox' checked=model.after_time}}
- {{i18n 'admin.wizard.after_time_label'}}
- {{d-button action='setNextSessionScheduled' translatedLabel=nextSessionScheduledLabel icon='calendar-o'}}
-
-
-
-
-
-
{{i18n 'admin.wizard.prompt_completion'}}
-
-
- {{input type='checkbox' checked=model.prompt_completion}}
- {{i18n 'admin.wizard.prompt_completion_label'}}
-
-
-
-
-
-
{{i18n 'admin.wizard.min_trust'}}
-
-
- {{i18n 'admin.wizard.min_trust_label'}}
- {{input type='number' value=model.min_trust class='input-small'}}
-
-
-
-
-
-
{{i18n 'admin.wizard.theme_id'}}
-
-
- {{combo-box
- content=model.themes
- valueProperty='id'
- value=model.theme_id
- none='admin.wizard.no_theme'}}
-
-
-
-
-
-
{{i18n 'admin.wizard.restart_on_revisit'}}
-
-
- {{input type='checkbox' checked=model.restart_on_revisit}}
- {{i18n 'admin.wizard.restart_on_revisit_label'}}
-
-
-
-
-
- {{wizard-links type="step" current=currentStep items=model.steps}}
- {{#if currentStep}}
- {{wizard-custom-step step=currentStep wizard=model}}
- {{/if}}
-
-
-
- {{#unless newWizard}}
-
- {{/unless}}
- {{conditional-loading-spinner condition=saving size='small'}}
- {{#if error}}
- {{d-icon "times"}}{{error}}
- {{/if}}
-
-
diff --git a/assets/javascripts/discourse/templates/admin-wizards-api-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-api-show.hbs
new file mode 100644
index 00000000..c484ab77
--- /dev/null
+++ b/assets/javascripts/discourse/templates/admin-wizards-api-show.hbs
@@ -0,0 +1,310 @@
+
+
+
+
+
+
+
+
+
+ {{#if showRedirectUri}}
+
+
+
+
+ {{api.redirectUri}}
+
+
+
+ {{/if}}
+
+
+
+
+ {{combo-box
+ value=api.authType
+ content=authorizationTypes
+ onChange=(action (mut authorizationTypes))
+ options=(hash
+ none='admin.wizard.api.auth.type_none'
+ )}}
+
+
+
+ {{#if isOauth}}
+ {{#if threeLeggedOauth}}
+
+
+
+ {{input value=api.authUrl}}
+
+
+ {{/if}}
+
+
+
+
+ {{input value=api.tokenUrl}}
+
+
+
+
+
+
+ {{input value=api.clientId}}
+
+
+
+
+
+
+ {{input value=api.clientSecret}}
+
+
+
+
+
+
+ {{#each api.authParams as |param|}}
+
+ {{input value=param.key placeholder=(i18n 'admin.wizard.key')}}
+ {{input value=param.value placeholder=(i18n 'admin.wizard.value')}}
+ {{d-button action=(action "removeParam") actionParam=param icon='times'}}
+
+ {{/each}}
+ {{d-button label='admin.wizard.api.auth.params.new' icon='plus' action=(action "addParam")}}
+
+
+ {{/if}}
+
+ {{#if isBasicAuth}}
+
+
+
+ {{input value=api.username}}
+
+
+
+
+
+
+ {{input value=api.password}}
+
+
+ {{/if}}
+
+
+ {{#if isOauth}}
+
+
+ {{#if api.authorized}}
+
+ {{i18n "admin.wizard.api.status.authorized"}}
+ {{else}}
+
+ {{i18n "admin.wizard.api.status.not_authorized"}}
+ {{/if}}
+
+
+
+
+ {{#if threeLeggedOauth}}
+
+
+
+ {{api.code}}
+
+
+ {{/if}}
+
+
+
+
+ {{api.accessToken}}
+
+
+
+ {{#if threeLeggedOauth}}
+
+
+
+ {{api.refreshToken}}
+
+
+ {{/if}}
+
+
+
+
+ {{api.tokenExpiresAt}}
+
+
+
+
+
+
+ {{api.tokenRefreshAt}}
+
+
+
+ {{/if}}
+
+
+
+
+
+ {{d-button action=(action "addEndpoint") label='admin.wizard.api.endpoint.add' icon='plus'}}
+
+ {{#if api.endpoints}}
+
+
+ {{#each api.endpoints as |endpoint|}}
+ -
+
+
+
+ {{input value=endpoint.name
+ placeholder=(i18n 'admin.wizard.api.endpoint.name')}}
+ {{input value=endpoint.url
+ placeholder=(i18n 'admin.wizard.api.endpoint.url')
+ class='endpoint-url'}}
+ {{d-button action=(action "removeEndpoint")
+ actionParam=endpoint
+ icon='times'
+ class='remove-endpoint'}}
+
+
+ {{combo-box
+ content=endpointMethods
+ value=endpoint.method
+ onChange=(action (mut endpoint.method))
+ options=(hash
+ none="admin.wizard.api.endpoint.method"
+ )}}
+ {{combo-box
+ content=contentTypes
+ value=endpoint.content_type
+ onChange=(action (mut endpoint.content_type))
+ options=(hash
+ none="admin.wizard.api.endpoint.content_type"
+ )}}
+ {{multi-select
+ content=successCodes
+ values=endpoint.success_codes
+ onChange=(action (mut endpoint.success_codes))
+ options=(hash
+ none="admin.wizard.api.endpoint.success_codes"
+ )}}
+
+
+
+
+ {{/each}}
+
+
+ {{/if}}
+
+
+
+
+
+
+
+ Datetime |
+ User |
+ Status |
+ URL |
+ Error |
+ {{#each api.log as |logentry|}}
+
+ {{logentry.time}} |
+
+
+ |
+ {{logentry.status}} |
+ {{logentry.url}} |
+ {{logentry.error}} |
+
+ {{/each}}
+
+
+
diff --git a/assets/javascripts/discourse/templates/admin-wizards-api.hbs b/assets/javascripts/discourse/templates/admin-wizards-api.hbs
index 06980f42..0425da3e 100644
--- a/assets/javascripts/discourse/templates/admin-wizards-api.hbs
+++ b/assets/javascripts/discourse/templates/admin-wizards-api.hbs
@@ -1,298 +1,3 @@
-
-
-
-
-
-
-
-
-
- {{#if showRedirectUri}}
-
-
-
-
- {{api.redirectUri}}
-
-
-
- {{/if}}
-
-
-
-
- {{combo-box
- value=api.authType
- content=authorizationTypes
- none='admin.wizard.api.auth.type_none'}}
-
-
-
- {{#if isOauth}}
- {{#if threeLeggedOauth}}
-
-
-
- {{input value=api.authUrl}}
-
-
- {{/if}}
-
-
-
-
- {{input value=api.tokenUrl}}
-
-
-
-
-
-
- {{input value=api.clientId}}
-
-
-
-
-
-
- {{input value=api.clientSecret}}
-
-
-
-
-
-
- {{#each api.authParams as |param|}}
-
- {{input value=param.key placeholder=(i18n 'admin.wizard.api.auth.params.key')}}
- {{input value=param.value placeholder=(i18n 'admin.wizard.api.auth.params.value')}}
- {{d-button action=(action "removeParam") actionParam=param icon='times'}}
-
- {{/each}}
- {{d-button label='admin.wizard.api.auth.params.new' icon='plus' action=(action "addParam")}}
-
-
- {{/if}}
-
- {{#if isBasicAuth}}
-
-
-
- {{input value=api.username}}
-
-
-
-
-
-
- {{input value=api.password}}
-
-
- {{/if}}
-
-
- {{#if isOauth}}
-
-
- {{#if api.authorized}}
-
- {{i18n "admin.wizard.api.status.authorized"}}
- {{else}}
-
- {{i18n "admin.wizard.api.status.not_authorized"}}
- {{/if}}
-
-
-
-
- {{#if threeLeggedOauth}}
-
-
-
- {{api.code}}
-
-
- {{/if}}
-
-
-
-
- {{api.accessToken}}
-
-
-
- {{#if threeLeggedOauth}}
-
-
-
- {{api.refreshToken}}
-
-
- {{/if}}
-
-
-
-
- {{api.tokenExpiresAt}}
-
-
-
-
-
-
- {{api.tokenRefreshAt}}
-
-
-
- {{/if}}
-
-
-
-
-
- {{d-button action=(action "addEndpoint") label='admin.wizard.api.endpoint.add' icon='plus'}}
-
- {{#if api.endpoints}}
-
-
- {{#each api.endpoints as |endpoint|}}
- -
-
-
-
- {{input value=endpoint.name
- placeholder=(i18n 'admin.wizard.api.endpoint.name')}}
- {{input value=endpoint.url
- placeholder=(i18n 'admin.wizard.api.endpoint.url')
- class='endpoint-url'}}
- {{d-button action=(action "removeEndpoint")
- actionParam=endpoint
- icon='times'
- class='remove-endpoint'}}
-
-
- {{combo-box
- content=endpointMethods
- value=endpoint.method
- none="admin.wizard.api.endpoint.method"}}
- {{combo-box
- content=contentTypes
- value=endpoint.content_type
- none="admin.wizard.api.endpoint.content_type"}}
- {{multi-select
- content=successCodes
- values=endpoint.success_codes
- none="admin.wizard.api.endpoint.success_codes"}}
-
-
-
-
- {{/each}}
-
-
- {{/if}}
-
-
-
-
-
-
-
- Datetime |
- User |
- Status |
- URL |
- Error |
- {{#each api.log as |logentry|}}
-
- {{logentry.time}} |
-
-
- |
- {{logentry.status}} |
- {{logentry.url}} |
- {{logentry.error}} |
-
- {{/each}}
-
-
-
+
+ {{outlet}}
+
\ No newline at end of file
diff --git a/assets/javascripts/discourse/templates/admin-wizards-apis.hbs b/assets/javascripts/discourse/templates/admin-wizards-apis.hbs
deleted file mode 100644
index c5f67660..00000000
--- a/assets/javascripts/discourse/templates/admin-wizards-apis.hbs
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
- {{#each model as |api|}}
- -
- {{#link-to "adminWizardsApi" (dasherize api.name)}}
- {{#if api.title}}
- {{api.title}}
- {{else}}
- {{api.name}}
- {{/if}}
- {{/link-to}}
-
- {{/each}}
-
-
- {{#link-to 'adminWizardsApi' 'new' class="btn"}}
- {{d-icon "plus"}} {{i18n 'admin.wizard.api.new'}}
- {{/link-to}}
-
-
-
-
- {{outlet}}
-
-
diff --git a/assets/javascripts/discourse/templates/admin-wizards-custom-index.hbs b/assets/javascripts/discourse/templates/admin-wizards-custom-index.hbs
deleted file mode 100644
index e9c27e0b..00000000
--- a/assets/javascripts/discourse/templates/admin-wizards-custom-index.hbs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/assets/javascripts/discourse/templates/admin-wizards-custom.hbs b/assets/javascripts/discourse/templates/admin-wizards-custom.hbs
deleted file mode 100644
index 06056ee2..00000000
--- a/assets/javascripts/discourse/templates/admin-wizards-custom.hbs
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
- {{#each model as |w|}}
- -
- {{#link-to "adminWizard" (dasherize w.id)}}{{w.name}}{{/link-to}}
-
- {{/each}}
-
-
- {{#link-to 'adminWizard' 'new' class="btn"}}
- {{d-icon "plus"}} {{i18n 'admin.wizard.new'}}
- {{/link-to}}
-
-
-
-
- {{outlet}}
-
-
diff --git a/assets/javascripts/discourse/templates/admin-wizards-logs.hbs b/assets/javascripts/discourse/templates/admin-wizards-logs.hbs
new file mode 100644
index 00000000..28052fe2
--- /dev/null
+++ b/assets/javascripts/discourse/templates/admin-wizards-logs.hbs
@@ -0,0 +1,34 @@
+
+
{{i18n 'admin.wizard.log.nav_label'}}
+
+ {{d-button
+ label="refresh"
+ icon="refresh"
+ action="refresh"
+ class="refresh"}}
+
+
+{{#load-more selector=".log-list tr" action=(action "loadMore") class="wizard-logs"}}
+ {{#if noResults}}
+ {{i18n 'search.no_results'}}
+ {{else}}
+
+
+
+ Message |
+ Date |
+
+
+
+ {{#each logs as |log|}}
+
+ {{log.message}} |
+ {{bound-date log.date}} |
+
+ {{/each}}
+
+
+ {{/if}}
+
+ {{conditional-loading-spinner condition=refreshing}}
+{{/load-more}}
\ No newline at end of file
diff --git a/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs
new file mode 100644
index 00000000..3b4cb401
--- /dev/null
+++ b/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs
@@ -0,0 +1,29 @@
+{{#if submissions}}
+
+
+
+
+
+ {{#each fields as |f|}}
+ {{f}} |
+ {{/each}}
+
+ {{#each submissions as |s|}}
+
+ {{#each-in s as |k v|}}
+ {{v}} |
+ {{/each-in}}
+
+ {{/each}}
+
+
+{{/if}}
diff --git a/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs b/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs
index f0047e88..ca0b835e 100644
--- a/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs
+++ b/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs
@@ -1,15 +1,13 @@
-
-
-
- {{#each model as |w|}}
- -
- {{#link-to "adminWizardSubmissions" w.id}}{{w.name}}{{/link-to}}
-
- {{/each}}
-
-
-
-
- {{outlet}}
-
+
+ {{combo-box
+ value=wizardId
+ content=wizardList
+ onChange=(route-action 'changeWizard')
+ options=(hash
+ none='admin.wizard.select'
+ )}}
+
+
+
+ {{outlet}}
diff --git a/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs b/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs
index ff36a823..5cfa3f56 100644
--- a/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs
+++ b/assets/javascripts/discourse/templates/admin-wizards-transfer.hbs
@@ -1,2 +1,2 @@
-{{wizard-export wizards=model}}
+{{wizard-export wizards=wizards}}
{{wizard-import}}
diff --git a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs
new file mode 100644
index 00000000..e0d511c1
--- /dev/null
+++ b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs
@@ -0,0 +1,203 @@
+{{#if wizard}}
+
+
+
+
+
+
+
+
+ {{input
+ name="background"
+ value=wizard.background
+ placeholderKey="admin.wizard.background_placeholder"
+ class="small"}}
+
+
+
+
+
+
+
+
+ {{combo-box
+ content=themes
+ valueProperty='id'
+ value=wizard.theme_id
+ onChange=(action (mut wizard.theme_id))
+ options=(hash
+ none='admin.wizard.no_theme'
+ )}}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{input type='checkbox' checked=wizard.required}}
+ {{i18n 'admin.wizard.required_label'}}
+
+
+
+
+
+
+
+
+ {{input type='checkbox' checked=wizard.after_signup}}
+ {{i18n 'admin.wizard.after_signup_label'}}
+
+
+
+
+
+
+
+
+ {{input type='checkbox' checked=wizard.multiple_submissions}}
+ {{i18n 'admin.wizard.multiple_submissions_label'}}
+
+
+
+
+
+
+
+
+ {{input type='checkbox' checked=wizard.prompt_completion}}
+ {{i18n 'admin.wizard.prompt_completion_label'}}
+
+
+
+
+
+
+
+
+ {{input type='checkbox' checked=wizard.after_time}}
+ {{i18n 'admin.wizard.after_time_label'}}
+ {{d-button
+ action='setNextSessionScheduled'
+ translatedLabel=nextSessionScheduledLabel
+ class="btn-after-time"
+ icon='far-calendar'}}
+
+
+
+
+
+
+
+
+ {{wizard-mapper
+ inputs=wizard.permitted
+ options=(hash
+ context='wizard'
+ inputTypes='assignment,validation'
+ groupSelection='output'
+ userFieldSelection='key'
+ textSelection='value'
+ inputConnector='and'
+ )}}
+
+
+
+ {{wizard-advanced-toggle showAdvanced=wizard.showAdvanced}}
+
+ {{#if wizard.showAdvanced}}
+
+
+
+
+
+
+
+ {{input type='checkbox' checked=wizard.save_submissions}}
+ {{i18n 'admin.wizard.save_submissions_label'}}
+
+
+
+
+
+
+
+
+ {{input type='checkbox' checked=wizard.restart_on_revisit}}
+ {{i18n 'admin.wizard.restart_on_revisit_label'}}
+
+
+
+
+ {{/if}}
+
+
+ {{wizard-links
+ itemType="step"
+ current=currentStep
+ items=wizard.steps}}
+
+ {{#if currentStep}}
+ {{wizard-custom-step
+ step=currentStep
+ wizard=wizard
+ currentField=currentField
+ wizardFields=wizardFields
+ fieldTypes=fieldTypes}}
+ {{/if}}
+
+ {{wizard-links
+ itemType="action"
+ current=currentAction
+ items=wizard.actions
+ generateLabels=true}}
+
+ {{#if currentAction}}
+ {{wizard-custom-action
+ action=currentAction
+ wizard=wizard
+ removeAction="removeAction"
+ wizardFields=wizardFields}}
+ {{/if}}
+
+
+
+
+ {{#unless creating}}
+
+ {{/unless}}
+
+ {{conditional-loading-spinner condition=saving size='small'}}
+
+ {{#if error}}
+ {{d-icon "times"}}{{error}}
+ {{/if}}
+
+{{/if}}
diff --git a/assets/javascripts/discourse/templates/admin-wizards-wizard.hbs b/assets/javascripts/discourse/templates/admin-wizards-wizard.hbs
new file mode 100644
index 00000000..a02c5324
--- /dev/null
+++ b/assets/javascripts/discourse/templates/admin-wizards-wizard.hbs
@@ -0,0 +1,37 @@
+
+ {{combo-box
+ value=wizardListVal
+ content=wizardList
+ onChange=(route-action 'changeWizard')
+ options=(hash
+ none='admin.wizard.select'
+ )}}
+
+ {{d-button
+ action="createWizard"
+ label="admin.wizard.create"
+ icon="plus"}}
+
+
+
+
+ {{d-icon 'info-circle'}}
+ {{message}}
+
+
+
+
+
+
+ {{outlet}}
+
\ No newline at end of file
diff --git a/assets/javascripts/discourse/templates/admin-wizards.hbs b/assets/javascripts/discourse/templates/admin-wizards.hbs
index 4ebb6a36..c0bd6b27 100644
--- a/assets/javascripts/discourse/templates/admin-wizards.hbs
+++ b/assets/javascripts/discourse/templates/admin-wizards.hbs
@@ -1,7 +1,10 @@
{{#admin-nav}}
- {{nav-item route='adminWizardsCustom' label='admin.wizard.custom_label'}}
- {{nav-item route='adminWizardsSubmissions' label='admin.wizard.submissions_label'}}
- {{nav-item route='adminWizardsApis' label='admin.wizard.api.nav_label'}}
+ {{nav-item route='adminWizardsWizard' label='admin.wizard.nav_label'}}
+ {{nav-item route='adminWizardsSubmissions' label='admin.wizard.submissions.nav_label'}}
+ {{#if siteSettings.wizard_apis_enabled}}
+ {{nav-item route='adminWizardsApi' label='admin.wizard.api.nav_label'}}
+ {{/if}}
+ {{nav-item route='adminWizardsLogs' label='admin.wizard.log.nav_label'}}
{{nav-item route='adminWizardsTransfer' label='admin.wizard.transfer.nav_label'}}
{{/admin-nav}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-advanced-toggle.hbs b/assets/javascripts/discourse/templates/components/wizard-advanced-toggle.hbs
new file mode 100644
index 00000000..e77e3a31
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/wizard-advanced-toggle.hbs
@@ -0,0 +1,4 @@
+{{d-button
+ action="toggleAdvanced"
+ label='admin.wizard.advanced'
+ class=toggleClass}}
\ No newline at end of file
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs
index 1350eee5..75fed890 100644
--- a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs
@@ -1,57 +1,65 @@
-
{{i18n "admin.wizard.id"}}
+
+
- {{input value=action.id placeholderKey='admin.wizard.id_placeholder' disabled=disableId}}
+ {{combo-box
+ value=action.type
+ content=actionTypes
+ onChange=(action (mut action.type))
+ options=(hash
+ none="admin.wizard.field.type"
+ )}}
-
{{i18n "admin.wizard.type"}}
+
+
{{combo-box
- value=action.type
- content=types
- none="admin.wizard.field.type"}}
+ value=action.run_after
+ content=runAfterContent
+ onChange=(action (mut action.run_after))}}
{{#if basicTopicFields}}
-
+
-
{{i18n "admin.wizard.action.title"}}
+
+
- {{combo-box
- value=action.title
- content=availableFields
- nameProperty="label"
- isDisabled=action.custom_title_enabled
- none='admin.wizard.select_field'}}
-
- {{input type='checkbox' checked=action.custom_title_enabled}}
- {{i18n 'admin.wizard.action.custom_title'}}
- {{#if action.custom_title_enabled}}
- {{input value=action.custom_title}}
- {{/if}}
-
+ {{wizard-mapper
+ inputs=action.title
+ options=(hash
+ wizardFieldSelection=true
+ userFieldSelection='key,value'
+ context='action'
+ )}}
-
{{i18n "admin.wizard.action.post"}}
+
+
{{combo-box
value=action.post
- content=availableFields
+ content=wizardFields
nameProperty='label'
- isDisabled=action.post_builder
- none='admin.wizard.select_field'}}
+ onChange=(action (mut action.post))
+ options=(hash
+ none='admin.wizard.selector.placeholder.wizard_field'
+ isDisabled=showPostBuilder
+ )}}
+
{{input type='checkbox' checked=action.post_builder}}
{{i18n 'admin.wizard.action.post_builder.checkbox'}}
@@ -62,213 +70,167 @@
{{#if action.post_builder}}
-
{{i18n 'admin.wizard.action.post_builder.label'}}
+
+
- {{d-editor value=action.post_template
- placeholder='admin.wizard.action.interpolate_fields'
- classNames='post-builder-editor'}}
-
-
-
-
+ {{wizard-text-editor
+ value=action.post_template
+ wizardFields=wizardFields}}
{{/if}}
{{/if}}
{{#if publicTopicFields}}
-
+
-
{{i18n "admin.wizard.action.create_topic.category"}}
+
+
- {{category-chooser
- value=action.category_id
- isDisabled=action.custom_category_enabled}}
-
- {{input type='checkbox' checked=action.custom_category_enabled}}
-
{{i18n 'admin.wizard.action.custom_category.label'}}
- {{#if action.custom_category_enabled}}
-
-
- {{input type='checkbox' checked=action.custom_category_wizard_field}}
- {{i18n 'admin.wizard.action.custom_category.wizard_field'}}
- {{#if action.custom_category_wizard_field}}
- {{combo-box
- value=action.category_id
- content=categoryFields
- nameProperty="label"
- none='admin.wizard.select_field'}}
- {{/if}}
-
-
- {{input type='checkbox' checked=action.custom_category_user_field}}
- {{i18n 'admin.wizard.action.custom_category.user_field'}}
- {{#if action.custom_category_user_field}}
- {{input value=action.custom_category_user_field_key}}
- {{/if}}
-
-
- {{/if}}
-
+ {{wizard-mapper
+ inputs=action.category
+ options=(hash
+ textSelection='key,value'
+ wizardFieldSelection=true
+ userFieldSelection='key,value'
+ categorySelection='output'
+ outputDefaultSelection='category'
+ context='action'
+ )}}
-
+
-
{{i18n "admin.wizard.action.create_topic.tags"}}
+
+
- {{tag-chooser
- tags=action.tags
- filterable=true
- allowCreate=true
- isDisabled=action.custom_tag_enabled}}
-
- {{input type='checkbox' checked=action.custom_tag_enabled}}
-
{{i18n 'admin.wizard.action.custom_tag.label'}}
- {{#if action.custom_tag_enabled}}
-
- {{combo-box
- value=action.custom_tag_field
- content=tagFields
- nameProperty="label"
- none='admin.wizard.select_field'}}
-
- {{/if}}
-
+ {{wizard-mapper
+ inputs=action.tags
+ options=(hash
+ tagSelection='output'
+ outputDefaultSelection='tag'
+ listSelection='output'
+ wizardFieldSelection=true
+ userFieldSelection='key,value'
+ context='action'
+ )}}
{{/if}}
-{{#if newTopicFields}}
-
+{{#if sendMessage}}
+
-
{{i18n "admin.wizard.action.skip_redirect.label"}}
+
+
- {{input type='checkbox' checked=action.skip_redirect}}
- {{i18n 'admin.wizard.action.skip_redirect.description' type='topic'}}
+ {{wizard-mapper
+ inputs=action.recipient
+ options=(hash
+ textSelection='value,output'
+ wizardFieldSelection=true
+ userFieldSelection='key,value'
+ groupSelection='key,value'
+ userSelection='output'
+ outputDefaultSelection='user'
+ context='action'
+ )}}
{{/if}}
-{{#if createTopic}}
-
-
- {{wizard-custom-inputs inputs=action.add_fields
- valueContent=availableFields
- inputKey='admin.wizard.action.topic_attr'
- noneValue='admin.wizard.select_field'
- allowCustomField=true}}
-
-{{/if}}
-
-{{#if sendMessage}}
-
-
-
{{i18n 'admin.wizard.required'}}
-
-
- {{combo-box
- value=action.required
- content=availableFields
- nameProperty='label'
- none='admin.wizard.select_field'}}
-
-
-
-
-
{{i18n "admin.wizard.action.send_message.recipient"}}
-
-
- {{user-selector single="true"
- includeMentionableGroups="true"
- usernames=action.username
- allowedUsers="true"}}
-
-
-
-
-
- {{wizard-custom-inputs inputs=action.add_fields
- keyContent=availableFields
- valuePlaceholder='admin.wizard.action.topic_attr'}}
-
-{{/if}}
-
{{#if updateProfile}}
-
-
- {{wizard-custom-inputs inputs=action.profile_updates
- valueContent=profileFields
- keyContent=availableFields
- noneValue='admin.wizard.action.update_profile.profile_field'
- allowCustomField=true
- allowUserField=true}}
+
+
+
+
+
+ {{wizard-mapper
+ inputs=action.profile_updates
+ options=(hash
+ inputTypes='association'
+ textSelection='value'
+ userFieldSelection='key'
+ wizardFieldSelection='value'
+ keyDefaultSelection='userField'
+ context='action'
+ )}}
{{/if}}
{{#if sendToApi}}
-
{{i18n "admin.wizard.action.send_to_api.api"}}
+
+
{{combo-box
value=action.api
content=availableApis
- isDisabled=action.custom_title_enabled
- none='admin.wizard.action.send_to_api.select_an_api'}}
+ onChange=(action (mut action.api))
+ options=(hash
+ isDisabled=action.custom_title_enabled
+ none='admin.wizard.action.send_to_api.select_an_api'
+ )}}
-
{{i18n "admin.wizard.action.send_to_api.endpoint"}}
+
+
{{combo-box
value=action.api_endpoint
content=availableEndpoints
- isDisabled=apiEmpty
- none='admin.wizard.action.send_to_api.select_an_endpoint'}}
+ onChange=(action (mut action.api_endpoint))
+ options=(hash
+ isDisabled=apiEmpty
+ none='admin.wizard.action.send_to_api.select_an_endpoint'
+ )}}
-
+
-
{{i18n "admin.wizard.action.send_to_api.body"}}
+
+
-
-
- {{textarea value=action.api_body
- placeholder=(i18n 'admin.wizard.action.interpolate_fields')}}
+ {{wizard-text-editor
+ value=action.api_body
+ previewEnabled=false
+ barEnabled=false
+ wizardFields=wizardFields
+ placeholder='admin.wizard.action.send_to_api.body_placeholder'}}
{{/if}}
{{#if addToGroup}}
-
+
-
{{i18n "admin.wizard.action.add_to_group.group_selection"}}
+
+
- {{combo-box
- value=action.group_id
- content=availableFields
- isDisabled=action.custom_group_enabled
- nameProperty="label"
- none='admin.wizard.select_field'}}
-
- {{input type='checkbox' checked=action.custom_group_enabled}}
- {{i18n 'admin.wizard.action.add_to_group.custom_group'}}
- {{#if action.custom_group_enabled}}
- {{input value=action.group_id}}
- {{/if}}
-
+ {{wizard-mapper
+ inputs=action.group
+ options=(hash
+ textSelection='value,output'
+ wizardFieldSelection='key,value,assignment'
+ userFieldSelection='key,value,assignment'
+ groupSelection='value,output'
+ outputDefaultSelection='group'
+ context='action'
+ )}}
{{/if}}
@@ -276,18 +238,88 @@
{{#if routeTo}}
-
{{i18n "admin.wizard.action.route_to.url"}}
+
+
{{input value=action.url}}
-
-
-
{{i18n "admin.wizard.action.route_to.code"}}
-
-
- {{input value=action.code}}
-
-
+{{/if}}
+
+{{#if hasAdvanced}}
+ {{wizard-advanced-toggle showAdvanced=action.showAdvanced}}
+
+ {{#if action.showAdvanced}}
+
+
+ {{#if hasCustomFields}}
+
+
+
+
+
+
+ {{wizard-mapper
+ inputs=action.custom_fields
+ options=(hash
+ inputTypes='association'
+ wizardFieldSelection='value'
+ userFieldSelection='value'
+ keyPlaceholder='admin.wizard.action.custom_fields.key'
+ context='action'
+ )}}
+
+
+ {{/if}}
+
+ {{#if sendMessage}}
+
+
+
+
+
+
+ {{wizard-mapper
+ inputs=action.required
+ options=(hash
+ textSelection='value'
+ wizardFieldSelection=true
+ userFieldSelection=true
+ groupSelection=true
+ context='action'
+ )}}
+
+
+ {{/if}}
+
+ {{#if showSkipRedirect}}
+
+
+
+
+
+
+ {{input type='checkbox' checked=action.skip_redirect}}
+
+
+ {{i18n 'admin.wizard.action.skip_redirect.description' type='topic'}}
+
+
+
+ {{/if}}
+
+ {{#if routeTo}}
+
+
+
+
+
+
+ {{input value=action.code}}
+
+
+ {{/if}}
+
+ {{/if}}
{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs
index aa91b615..23a745b3 100644
--- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs
@@ -1,158 +1,165 @@
-
{{i18n 'admin.wizard.id'}}
+
- {{input name="id" value=field.id placeholderKey="admin.wizard.id_placeholder" disabled=disableId}}
+ {{input name="label" value=field.label}}
-
{{i18n 'admin.wizard.key'}}
+
+
- {{input name="key" value=field.key placeholderKey="admin.wizard.key_placeholder"}}
+ {{i18n 'admin.wizard.field.required_label'}}
+ {{input type='checkbox' checked=field.required}}
-
{{i18n 'admin.wizard.field.label'}}
+
- {{input name="label" value=field.label placeholder=(i18n "admin.wizard.custom_text_placeholder")}}
+ {{textarea name="description" value=field.description}}
-
{{i18n 'admin.wizard.field.description'}}
+
- {{textarea name="description" value=field.description placeholder=(i18n "admin.wizard.custom_text_placeholder")}}
+ {{image-uploader
+ imageUrl=field.image
+ onUploadDone=(action "imageUploadDone")
+ onUploadDeleted=(action "imageUploadDeleted")
+ type="wizard-step"
+ class="no-repeat contain-image"}}
-
{{i18n 'admin.wizard.field.image'}}
-
-
- {{input name="image" value=field.image placeholderKey="admin.wizard.field.image_placeholder"}}
-
-
-
-
-
-
{{i18n 'admin.wizard.type'}}
+
+
{{combo-box
value=field.type
- content=types
- none="admin.wizard.field.type"}}
+ content=fieldTypes
+ onChange=(action (mut field.type))
+ options=(hash
+ none="admin.wizard.field.type"
+ )}}
-
-
-
{{i18n 'admin.wizard.field.required'}}
-
-
- {{input type='checkbox' checked=field.required}}
- {{i18n 'admin.wizard.field.required_label'}}
-
-
-
-{{#if isInput}}
+{{#if showMinLength}}
-
{{i18n 'admin.wizard.field.min_length'}}
+
+
- {{input type="number" name="min_length" value=field.min_length placeholder=(i18n 'admin.wizard.field.min_length_placeholder')}}
+ {{input
+ type="number"
+ name="min_length"
+ value=field.min_length
+ class="small"}}
{{/if}}
-{{#if isDropdown}}
-
-
-
- {{combo-box
- value=field.choices_type
- content=choicesTypes
- none="admin.wizard.field.choices_type"}}
-
- {{#if choicesTranslation}}
-
- {{input name="key" value=field.choices_key placeholderKey="admin.wizard.key_placeholder"}}
- {{/if}}
-
- {{#if choicesPreset}}
-
- {{combo-box
- value=field.choices_preset
- content=presetChoices
- none='admin.wizard.none'}}
-
- {{wizard-custom-inputs inputs=field.choices_filters}}
- {{/if}}
-
- {{#if choicesCustom}}
-
- {{wizard-custom-inputs inputs=field.choices}}
- {{/if}}
-
-
- {{input name="dropdown_none" value=field.dropdown_none placeholder=(i18n 'admin.wizard.field.dropdown_none_placeholder')}}
-
-{{/if}}
-
{{#if isUpload}}
-
{{i18n 'admin.wizard.field.file_types'}}
+
+
- {{input value=field.file_types}}
+ {{input value=field.file_types class="medium"}}
{{/if}}
-{{#if isCategoryOrTag}}
+{{#if showLimit}}
-
{{i18n 'admin.wizard.field.limit'}}
+
+
- {{input type="number" value=field.limit}}
+ {{input type="number" value=field.limit class="small"}}
{{/if}}
-{{#if isCategory}}
-
+{{#if showPrefill}}
+
-
{{i18n 'admin.wizard.field.property'}}
+
+
- {{combo-box
- content=categoryPropertyTypes
- value=field.property}}
+ {{wizard-mapper
+ inputs=field.prefill
+ options=prefillOptions}}
{{/if}}
+
+{{#if showContent}}
+
+
+
+
+
+
+ {{wizard-mapper
+ inputs=field.content
+ options=contentOptions}}
+
+
+{{/if}}
+
+{{wizard-advanced-toggle showAdvanced=field.showAdvanced}}
+
+{{#if field.showAdvanced}}
+
+
+ {{#if isCategory}}
+
+
+
+
+
+
+ {{combo-box
+ value=field.property
+ content=categoryPropertyTypes
+ onChange=(action (mut field.property))
+ options=(hash
+ none='admin.wizard.selector.placeholder.property'
+ )}}
+
+
+ {{/if}}
+
+
+
+
+
+
+ {{input
+ name="key"
+ value=field.key
+ class="medium"
+ placeholderKey="admin.wizard.translation_placeholder"}}
+
+
+
+
+{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-input.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-input.hbs
deleted file mode 100644
index 4e368de7..00000000
--- a/assets/javascripts/discourse/templates/components/wizard-custom-input.hbs
+++ /dev/null
@@ -1,55 +0,0 @@
-
- {{#if keyContent}}
- {{combo-box value=input.key content=keyContent nameProperty="label" none=noneKey}}
- {{else}}
- {{input type="text" value=input.key placeholder=(i18n inputKey)}}
- {{/if}}
-
-
-
- {{#if connectorContent}}
- {{combo-box value=input.connector
- content=connectorContent
- nameProperty="label"
- none=connectorNone}}
- {{/if}}
-
- {{#if connectorKey}}
- {{i18n connectorKey}}
- {{/if}}
-
-
-
- {{#if valueContent}}
- {{combo-box value=input.value
- content=valueContent
- nameProperty="label"
- none=noneValue
- isDisabled=valueDisabled}}
- {{else}}
- {{input type="text" value=input.value placeholder=(i18n valuePlaceholder)}}
- {{/if}}
-
- {{#if allowCustomField}}
-
- {{i18n 'admin.wizard.or'}}
-
-
- {{input type="text"
- value=input.value_custom
- placeholder=(i18n 'admin.wizard.custom_value_placeholder')
- disabled=customDisabled}}
- {{/if}}
-
- {{#if allowUserField}}
-
- {{i18n 'admin.wizard.or'}}
-
-
- {{combo-box value=input.user_field
- content=userFields
- none='admin.wizard.user_field_placeholder'}}
- {{/if}}
-
-
-{{d-button action=remove actionParam=input icon='times' class='remove'}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-inputs.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-inputs.hbs
deleted file mode 100644
index f41f3d73..00000000
--- a/assets/javascripts/discourse/templates/components/wizard-custom-inputs.hbs
+++ /dev/null
@@ -1,15 +0,0 @@
-{{#each inputs as |input|}}
- {{wizard-custom-input input=input
- valueContent=valueContent
- keyContent=keyContent
- connectorContent=connectorContent
- connectorKey=connectorKey
- noneValue=noneValue
- valuePlaceholder=valuePlaceholder
- allowCustomField=allowCustomField
- allowUserField=allowUserField
- remove=(action 'remove')}}
-{{/each}}
-
- {{d-button action='add' label='admin.wizard.add' icon='plus'}}
-
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs
index 48e22bbf..4380c862 100644
--- a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs
@@ -1,91 +1,108 @@
-
{{i18n 'admin.wizard.id'}}
+
- {{input name="id" value=step.id placeholderKey="admin.wizard.id_placeholder" disabled=disableId}}
-
-
-
-
-
-
{{i18n 'admin.wizard.key'}}
-
-
- {{input name="key" value=step.key placeholderKey="admin.wizard.key_placeholder"}}
-
-
-
-
-
-
{{i18n 'admin.wizard.step.title'}}
-
-
- {{input name="title" value=step.title placeholderKey="admin.wizard.custom_text_placeholder"}}
-
-
-
-
-
-
{{i18n 'admin.wizard.step.banner'}}
-
-
- {{input name="banner" value=step.banner placeholderKey="admin.wizard.step.banner_placeholder"}}
+ {{input
+ name="title"
+ value=step.title}}
-
{{i18n 'admin.wizard.step.description'}}
+
- {{d-editor value=step.raw_description placeholder="admin.wizard.custom_text_placeholder"}}
+ {{image-uploader
+ imageUrl=step.banner
+ onUploadDone=(action "bannerUploadDone")
+ onUploadDeleted=(action "bannerUploadDeleted")
+ type="wizard-banner"
+ class="no-repeat contain-image"}}
-
+
-
{{i18n 'admin.wizard.step.required_data.label'}}
+
- {{wizard-custom-inputs inputs=step.required_data
- inputKey='admin.wizard.step.required_data.key'
- valueContent=requiredContent
- connectorContent=requiredConnectorContent}}
- {{#if step.required_data}}
-
-
- {{i18n 'admin.wizard.step.required_data.not_permitted_message'}}
-
- {{input value=step.required_data_message}}
+ {{wizard-text-editor
+ value=step.raw_description
+ wizardFields=descriptionWizardFields}}
+
+
+
+{{wizard-advanced-toggle showAdvanced=step.showAdvanced}}
+
+{{#if step.showAdvanced}}
+
+
+
+
+
- {{/if}}
-
-
+
+ {{wizard-mapper
+ inputs=step.required_data
+ options=(hash
+ inputTypes='validation'
+ inputConnector='and'
+ wizardFieldSelection='value'
+ userFieldSelection='value'
+ keyPlaceholder="admin.wizard.submission_key"
+ context='step'
+ )}}
+ {{#if step.required_data}}
+
+
+ {{i18n 'admin.wizard.step.required_data.not_permitted_message'}}
+
+ {{input value=step.required_data_message}}
+
+ {{/if}}
+
+
-
-
-
{{i18n 'admin.wizard.step.permitted_params.label'}}
+
+
+
+
+
+ {{wizard-mapper
+ inputs=step.permitted_params
+ options=(hash
+ pairConnector='set'
+ inputTypes='association'
+ keyPlaceholder='admin.wizard.param_key'
+ valuePlaceholder='admin.wizard.submission_key'
+ context='step'
+ )}}
+
+
+
+
+
+
+
+
+ {{input
+ name="key"
+ value=step.key
+ placeholderKey="admin.wizard.translation_placeholder"}}
+
+
+
-
- {{wizard-custom-inputs inputs=step.permitted_params
- inputKey='admin.wizard.step.permitted_params.key'
- valuePlaceholder='admin.wizard.step.permitted_params.value'
- connectorKey='admin.wizard.step.permitted_params.connector'}}
-
-
+{{/if}}
+
+{{wizard-links itemType="field" current=currentField items=step.fields}}
-{{wizard-links type="field" current=currentField items=step.fields}}
{{#if currentField}}
- {{wizard-custom-field field=currentField types=wizard.fieldTypes removeField="removeField"}}
-{{/if}}
-
-{{wizard-links type="action" current=currentAction items=step.actions}}
-{{#if currentAction}}
- {{wizard-custom-action action=currentAction
- wizard=wizard
- removeAction="removeAction"
- availableFields=availableFields}}
-{{/if}}
-
-
+ {{wizard-custom-field
+ field=currentField
+ fieldTypes=fieldTypes
+ removeField="removeField"
+ wizardFields=wizardFields}}
+{{/if}}
\ No newline at end of file
diff --git a/assets/javascripts/discourse/templates/components/wizard-export.hbs b/assets/javascripts/discourse/templates/components/wizard-export.hbs
index 869b372a..9bbfabcf 100644
--- a/assets/javascripts/discourse/templates/components/wizard-export.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-export.hbs
@@ -3,10 +3,12 @@
{{#each wizards as |w|}}
-
- {{input type="checkbox"
- id=(dasherize w.id)
- change=(action 'checkChanged')}}
- {{#link-to "adminWizard" (dasherize w.id)}}
+ {{input
+ type="checkbox"
+ id=(dasherize w.id)
+ change=(action 'checkChanged')}}
+
+ {{#link-to "adminWizardsWizardShow" (dasherize w.id)}}
{{w.name}}
{{/link-to}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-links.hbs b/assets/javascripts/discourse/templates/components/wizard-links.hbs
index f81f1605..b24b5083 100644
--- a/assets/javascripts/discourse/templates/components/wizard-links.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-links.hbs
@@ -1,12 +1,14 @@
-
-
-
+
+
+
+ {{#if anyLinks}}
{{#each links as |l|}}
-
-
+
{{d-button action="change" actionParam=l.id translatedLabel=l.label class=l.classes}}
{{d-button action='remove' actionParam=l.id icon='times' class='remove'}}
-
+
{{/each}}
-
+ {{/if}}
{{d-button action='add' label='admin.wizard.add' icon='plus'}}
+
diff --git a/assets/javascripts/discourse/templates/components/wizard-mapper-connector.hbs b/assets/javascripts/discourse/templates/components/wizard-mapper-connector.hbs
new file mode 100644
index 00000000..2e30fbfb
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/wizard-mapper-connector.hbs
@@ -0,0 +1,12 @@
+{{#if hasMultiple}}
+ {{combo-box
+ value=connector
+ content=connectors
+ onChange=(action (mut connector))}}
+{{else}}
+ {{#if connector}}
+
+ {{connectorLabel}}
+
+ {{/if}}
+{{/if}}
\ No newline at end of file
diff --git a/assets/javascripts/discourse/templates/components/wizard-mapper-input.hbs b/assets/javascripts/discourse/templates/components/wizard-mapper-input.hbs
new file mode 100644
index 00000000..1e005fb8
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/wizard-mapper-input.hbs
@@ -0,0 +1,50 @@
+{{wizard-mapper-connector
+ connector=input.type
+ connectors=inputTypes
+ inputTypes=true
+ inputType=inputType
+ connectorType="type"
+ options=options}}
+
+{{#if hasPairs}}
+
+ {{#each input.pairs as |pair|}}
+ {{wizard-mapper-pair
+ pair=pair
+ last=pair.last
+ inputType=inputType
+ options=options
+ removePair=(action 'removePair')}}
+ {{/each}}
+
+ {{#if canAddPair}}
+
+ {{d-icon 'plus'}}
+
+ {{/if}}
+
+{{/if}}
+
+{{#if hasOutput}}
+ {{#if hasPairs}}
+ {{wizard-mapper-connector
+ connector=input.output_connector
+ connectors=connectors
+ connectorType="output"
+ inputType=inputType
+ options=options}}
+ {{/if}}
+
+
+ {{wizard-mapper-selector
+ selectorType='output'
+ inputType=input.type
+ value=input.output
+ activeType=input.output_type
+ options=options}}
+
+{{/if}}
+
+
+ {{d-icon 'times'}}
+
diff --git a/assets/javascripts/discourse/templates/components/wizard-mapper-pair.hbs b/assets/javascripts/discourse/templates/components/wizard-mapper-pair.hbs
new file mode 100644
index 00000000..d35e0fbb
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/wizard-mapper-pair.hbs
@@ -0,0 +1,32 @@
+
+ {{wizard-mapper-selector
+ selectorType='key'
+ inputType=inputType
+ value=pair.key
+ activeType=pair.key_type
+ options=options}}
+
+
+{{wizard-mapper-connector
+ connector=pair.connector
+ connectors=connectors
+ connectorType="pair"
+ inputType=inputType
+ options=options}}
+
+
+ {{wizard-mapper-selector
+ selectorType='value'
+ inputType=inputType
+ value=pair.value
+ activeType=pair.value_type
+ options=options}}
+
+
+{{#if showJoin}}
+ &
+{{/if}}
+
+{{#if showRemove}}
+ {{d-icon 'times'}}
+{{/if}}
\ No newline at end of file
diff --git a/assets/javascripts/discourse/templates/components/wizard-mapper-selector-type.hbs b/assets/javascripts/discourse/templates/components/wizard-mapper-selector-type.hbs
new file mode 100644
index 00000000..2ef7f2a3
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/wizard-mapper-selector-type.hbs
@@ -0,0 +1 @@
+{{item.label}}
\ No newline at end of file
diff --git a/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs b/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs
new file mode 100644
index 00000000..7b71756b
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs
@@ -0,0 +1,70 @@
+
+ {{#if hasTypes}}
+
+ {{activeTypeLabel}}
+
+
+ {{#if showTypes}}
+
+ {{#each selectorTypes as |item|}}
+ {{wizard-mapper-selector-type
+ activeType=activeType
+ item=item
+ toggle=(action 'toggleType')}}
+ {{/each}}
+
+ {{/if}}
+ {{else}}
+
{{activeTypeLabel}}
+ {{/if}}
+
+
+
+ {{#if showText}}
+ {{input
+ type="text"
+ value=value
+ placeholder=(i18n placeholderKey)}}
+ {{/if}}
+
+ {{#if showComboBox}}
+ {{combo-box
+ value=value
+ content=comboBoxContent
+ onChange=(action (mut value))
+ options=(hash
+ none=placeholderKey
+ )}}
+ {{/if}}
+
+ {{#if showMultiSelect}}
+ {{multi-select
+ content=multiSelectContent
+ value=value
+ onChange=(action (mut value))
+ options=multiSelectOptions}}
+ {{/if}}
+
+ {{#if showList}}
+ {{value-list
+ values=value
+ addKey=placeholderKey}}
+ {{/if}}
+
+ {{#if showTag}}
+ {{tag-chooser
+ tags=value
+ options=(hash
+ none=placeholderKey
+ filterable=true
+ )}}
+ {{/if}}
+
+ {{#if showUser}}
+ {{user-selector
+ includeMessageableGroups='true'
+ placeholderKey=placeholderKey
+ usernames=value
+ autocomplete="discourse"}}
+ {{/if}}
+
\ No newline at end of file
diff --git a/assets/javascripts/discourse/templates/components/wizard-mapper.hbs b/assets/javascripts/discourse/templates/components/wizard-mapper.hbs
new file mode 100644
index 00000000..fe71dbd1
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/wizard-mapper.hbs
@@ -0,0 +1,16 @@
+{{#each inputs as |input|}}
+ {{#if input.connector}}
+ {{wizard-mapper-connector connector=input.connector connectorType="input"}}
+ {{/if}}
+
+ {{wizard-mapper-input
+ input=input
+ options=inputOptions
+ remove=(action 'remove')}}
+{{/each}}
+
+{{#if canAdd}}
+
+ {{d-button action='add' label='admin.wizard.add' icon='plus'}}
+
+{{/if}}
\ No newline at end of file
diff --git a/assets/javascripts/discourse/templates/components/wizard-text-editor.hbs b/assets/javascripts/discourse/templates/components/wizard-text-editor.hbs
new file mode 100644
index 00000000..5638ac70
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/wizard-text-editor.hbs
@@ -0,0 +1,34 @@
+{{d-editor
+ value=value
+ forcePreview=forcePreview
+ placeholder=placeholder}}
+
+
+ {{#if previewEnabled}}
+ {{d-button
+ action="togglePreview"
+ translatedLabel=previewLabel}}
+ {{/if}}
+
+ {{#if fieldsEnabled}}
+ {{d-button
+ action="togglePopover"
+ translatedLabel=popoverLabel}}
+
+ {{#if showPopover}}
+
+
+
+ {{#if hasWizardFields}}
+
+ {{/if}}
+
+ {{/if}}
+ {{/if}}
+
\ No newline at end of file
diff --git a/assets/javascripts/wizard-custom.js b/assets/javascripts/wizard-custom.js
index 69a853b9..d67fa1b9 100644
--- a/assets/javascripts/wizard-custom.js
+++ b/assets/javascripts/wizard-custom.js
@@ -82,7 +82,7 @@
//= require discourse/components/d-button
//= require discourse/components/composer-editor
//= require discourse/components/d-editor
-//= require discourse/components/popup-input-tip
+//= require discourse/components/input-tip
//= require discourse/components/emoji-picker
//= require discourse/components/input-tip
//= require discourse/components/date-picker
@@ -92,6 +92,7 @@
//= require discourse/templates/components/d-button
//= require discourse/templates/components/d-editor
//= require discourse/templates/components/emoji-picker
+//= require discourse/templates/components/popup-input-tip
//= require discourse/templates/category-tag-autocomplete
//= require discourse/templates/emoji-selector-autocomplete
//= require discourse/templates/user-selector-autocomplete
@@ -107,7 +108,6 @@
//= require preload-store
//= require lodash.js
//= require mousetrap.js
-//= require jquery.putcursoratend.js
//= require template_include.js
//= require caret_position.js
//= require popper.js
diff --git a/assets/javascripts/wizard/components/custom-user-selector.js.es6 b/assets/javascripts/wizard/components/custom-user-selector.js.es6
index 10bfba50..143b340c 100644
--- a/assets/javascripts/wizard/components/custom-user-selector.js.es6
+++ b/assets/javascripts/wizard/components/custom-user-selector.js.es6
@@ -1,4 +1,4 @@
-import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
+import { default as computed, observes } from 'discourse-common/utils/decorators';
import { renderAvatar } from 'discourse/helpers/user-avatar';
import userSearch from '../lib/user-search';
@@ -64,7 +64,7 @@ export default Ember.TextField.extend({
return usernames;
}
- this.$().val(this.get('usernames')).autocomplete({
+ $(this.element).val(this.get('usernames')).autocomplete({
template,
disabled: this.get('disabled'),
single: this.get('single'),
@@ -121,7 +121,7 @@ export default Ember.TextField.extend({
willDestroyElement() {
this._super();
- this.$().autocomplete('destroy');
+ $(this.element).autocomplete('destroy');
},
// THIS IS A HUGE HACK TO SUPPORT CLEARING THE INPUT
@@ -129,7 +129,7 @@ export default Ember.TextField.extend({
_clearInput: function() {
if (arguments.length > 1) {
if (Em.isEmpty(this.get("usernames"))) {
- this.$().parent().find("a").click();
+ $(this.element).parent().find("a").click();
}
}
}
diff --git a/assets/javascripts/wizard/components/wizard-category-selector.js.es6 b/assets/javascripts/wizard/components/wizard-category-selector.js.es6
new file mode 100644
index 00000000..83d61566
--- /dev/null
+++ b/assets/javascripts/wizard/components/wizard-category-selector.js.es6
@@ -0,0 +1,12 @@
+import CategorySelector from 'select-kit/components/category-selector';
+import { computed } from "@ember/object";
+import { makeArray } from "discourse-common/lib/helpers";
+
+export default CategorySelector.extend({
+ content: computed("categories.[]", "blacklist.[]", "whitelist.[]", function() {
+ return this._super().filter(category => {
+ const whitelist = makeArray(this.whitelist);
+ return !whitelist.length || whitelist.indexOf(category.id) > -1;
+ });
+ })
+})
\ No newline at end of file
diff --git a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6
index 8be6ebb7..f7a89464 100644
--- a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6
+++ b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6
@@ -1,5 +1,5 @@
import ComposerEditor from 'discourse/components/composer-editor';
-import { default as computed, on } from 'ember-addons/ember-computed-decorators';
+import { default as computed, on } from 'discourse-common/utils/decorators';
import { findRawTemplate } from "discourse/lib/raw-templates";
import { throttle } from "@ember/runloop";
import { scheduleOnce } from "@ember/runloop";
@@ -30,7 +30,6 @@ export default ComposerEditor.extend({
key: "@",
transformComplete: v => v.username || v.name,
afterComplete() {
- // ensures textarea scroll position is correct
scheduleOnce("afterRender", () => $input.blur().focus());
}
});
diff --git a/assets/javascripts/wizard/components/wizard-field-category.js.es6 b/assets/javascripts/wizard/components/wizard-field-category.js.es6
index 9df3d0a4..dd82c909 100644
--- a/assets/javascripts/wizard/components/wizard-field-category.js.es6
+++ b/assets/javascripts/wizard/components/wizard-field-category.js.es6
@@ -1,29 +1,32 @@
-import { observes } from 'ember-addons/ember-computed-decorators';
+import { observes } from 'discourse-common/utils/decorators';
import Category from 'discourse/models/category';
export default Ember.Component.extend({
didInsertElement() {
- const value = this.get('field.value');
+ const property = this.field.property || 'id';
+ const value = this.field.value;
+
if (value) {
- const property = this.get('field.property') || 'id';
- const categories = [...value].map(v => {
- return property === 'id' ?
- Category.findById(v) :
- Category.findBySlug(v);
- });
- this.set('categories', categories);
+ this.set('categories', [...value].reduce((result, v) => {
+ let val = property === 'id' ? Category.findById(v) : Category.findBySlug(v);
+ if (val) result.push(val);
+ return result;
+ }, []));
}
},
-
+
@observes('categories')
setValue() {
- const categories = this.get('categories');
- if (categories.length) {
- const property = this.get('field.property') || 'id';
- let value = categories.length === 1 ?
- categories[0][property] :
- categories.map(c => c[property]);
- this.set('field.value', value);
+ const categories = (this.categories || []).filter(c => !!c);
+ const property = this.field.property || 'id';
+
+ if (categories.length) {
+ this.set('field.value', categories.reduce((result, c) => {
+ if (c && c[property]) {
+ result.push(c[property])
+ }
+ return result;
+ }, []));
}
}
});
\ No newline at end of file
diff --git a/assets/javascripts/wizard/components/wizard-field-composer.js.es6 b/assets/javascripts/wizard/components/wizard-field-composer.js.es6
index 9227faef..7ae281bd 100644
--- a/assets/javascripts/wizard/components/wizard-field-composer.js.es6
+++ b/assets/javascripts/wizard/components/wizard-field-composer.js.es6
@@ -1,4 +1,5 @@
-import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
+import { default as computed, observes } from 'discourse-common/utils/decorators';
+import EmberObject from "@ember/object";
export default Ember.Component.extend({
showPreview: false,
@@ -6,7 +7,7 @@ export default Ember.Component.extend({
classNameBindings: ["showPreview:show-preview:hide-preview"],
didInsertElement() {
- this.set('composer', Ember.Object.create({
+ this.set('composer', EmberObject.create({
loading: false,
reply: this.get('field.value')
}))
diff --git a/assets/javascripts/wizard/components/wizard-field-upload.js.es6 b/assets/javascripts/wizard/components/wizard-field-upload.js.es6
index ee969eef..58faff14 100644
--- a/assets/javascripts/wizard/components/wizard-field-upload.js.es6
+++ b/assets/javascripts/wizard/components/wizard-field-upload.js.es6
@@ -10,7 +10,7 @@ export default Ember.Component.extend({
didInsertElement() {
this._super();
- const $upload = this.$();
+ const $upload = $(this.element);
const id = this.get("field.id");
diff --git a/assets/javascripts/wizard/components/wizard-group-selector.js.es6 b/assets/javascripts/wizard/components/wizard-group-selector.js.es6
new file mode 100644
index 00000000..0029bae2
--- /dev/null
+++ b/assets/javascripts/wizard/components/wizard-group-selector.js.es6
@@ -0,0 +1,17 @@
+import ComboBox from 'select-kit/components/combo-box';
+import { computed } from "@ember/object";
+import { makeArray } from "discourse-common/lib/helpers";
+
+export default ComboBox.extend({
+ content: computed("groups.[]", "field.content.[]", function() {
+ const whitelist = makeArray(this.field.content);
+ return this.groups.filter(group => {
+ return !whitelist.length || whitelist.indexOf(group.id) > -1;
+ }).map(g => {
+ return {
+ id: g.id,
+ name: g.name
+ }
+ });
+ })
+})
\ No newline at end of file
diff --git a/assets/javascripts/wizard/components/wizard-tag-selector.js.es6 b/assets/javascripts/wizard/components/wizard-tag-selector.js.es6
new file mode 100644
index 00000000..24acf2e2
--- /dev/null
+++ b/assets/javascripts/wizard/components/wizard-tag-selector.js.es6
@@ -0,0 +1,11 @@
+import TagChooser from 'select-kit/components/tag-chooser';
+import { makeArray } from "discourse-common/lib/helpers";
+
+export default TagChooser.extend({
+ _transformJson(context, json) {
+ return this._super(context, json).filter((tag) => {
+ const whitelist = makeArray(context.whitelist);
+ return !whitelist.length || whitelist.indexOf(tag.id) > 1;
+ });
+ }
+})
\ No newline at end of file
diff --git a/assets/javascripts/wizard/components/wizard-text-field.js.es6 b/assets/javascripts/wizard/components/wizard-text-field.js.es6
index f450844d..5c62abc8 100644
--- a/assets/javascripts/wizard/components/wizard-text-field.js.es6
+++ b/assets/javascripts/wizard/components/wizard-text-field.js.es6
@@ -1,6 +1,6 @@
/* eslint no-undef: 0 */
-import computed from "ember-addons/ember-computed-decorators";
+import computed from "discourse-common/utils/decorators";
import { siteDir, isRTL, isLTR } from "discourse/lib/text-direction";
export default Ember.TextField.extend({
diff --git a/assets/javascripts/wizard/initializers/custom.js.es6 b/assets/javascripts/wizard/initializers/custom.js.es6
index 9afd9128..fbdac38f 100644
--- a/assets/javascripts/wizard/initializers/custom.js.es6
+++ b/assets/javascripts/wizard/initializers/custom.js.es6
@@ -1,4 +1,5 @@
-import { default as computed } from 'ember-addons/ember-computed-decorators';
+import { default as computed } from 'discourse-common/utils/decorators';
+import { dasherize } from "@ember/string";
export default {
name: 'custom-routes',
@@ -19,7 +20,6 @@ export default {
const autocomplete = requirejs('discourse/lib/autocomplete').default;
const cook = requirejs('discourse/plugins/discourse-custom-wizard/wizard/lib/text-lite').cook;
const Singleton = requirejs("discourse/mixins/singleton").default;
- const WizardFieldDropdown = requirejs('wizard/components/wizard-field-dropdown').default;
const Store = requirejs("discourse/models/store").default;
const registerRawHelpers = requirejs("discourse-common/lib/raw-handlebars-helpers").registerRawHelpers;
const RawHandlebars = requirejs("discourse-common/lib/raw-handlebars").default;
@@ -89,7 +89,15 @@ export default {
animateInvalidFields() {
Ember.run.scheduleOnce('afterRender', () => {
- $('.invalid input[type=text], .invalid textarea, .invalid input[type=checkbox], .invalid .select-kit').wiggle(2, 100);
+ let $element = $('.invalid input[type=text], .invalid textarea, .invalid input[type=checkbox], .invalid .select-kit');
+
+ if ($element.length) {
+ $([document.documentElement, document.body]).animate({
+ scrollTop: $element.offset().top - 200
+ }, 400, function() {
+ $element.wiggle(2, 100);
+ });
+ }
});
},
@@ -161,7 +169,7 @@ export default {
const fields = {};
this.get('fields').forEach(f => {
- if (f.type !== 'text-only') {
+ if (f.type !== 'text_only') {
fields[f.id] = f.value;
}
});
@@ -171,6 +179,7 @@ export default {
type: 'PUT',
data: { fields }
}).catch(response => {
+ console.log(response)
if (response && response.responseJSON && response.responseJSON.errors) {
let wizardErrors = [];
response.responseJSON.errors.forEach(err => {
@@ -185,6 +194,7 @@ export default {
if (wizardErrors.length) {
this.handleWizardError(wizardErrors.join('\n'));
}
+ this.animateInvalidFields();
throw response;
}
@@ -218,8 +228,8 @@ export default {
inputComponentName: function() {
const type = this.get('field.type');
const id = this.get('field.id');
- if (['text-only'].includes(type)) return false;
- return (type === 'component') ? Ember.String.dasherize(id) : `wizard-field-${type}`;
+ if (['text_only'].includes(type)) return false;
+ return dasherize((type === 'component') ? id : `wizard-field-${type}`);
}.property('field.type', 'field.id')
});
@@ -230,9 +240,11 @@ export default {
'dropdown',
'tag',
'image',
- 'user-selector',
- 'text-only',
- 'composer'
+ 'user_selector',
+ 'text_only',
+ 'composer',
+ 'category',
+ 'group'
];
FieldModel.reopen({
@@ -256,16 +268,15 @@ export default {
} else {
const val = this.get('value');
const type = this.get('type');
+
if (type === 'checkbox') {
valid = val;
- } else if (type === 'category') {
- valid = val && val.toString().length > 0;
} else if (type === 'upload') {
valid = val && val.id > 0;
} else if (StandardFieldValidation.indexOf(type) > -1) {
- valid = val && val.length > 0;
+ valid = val && val.toString().length > 0;
} else if (type === 'url') {
- valid = true
+ valid = true;
}
}
diff --git a/assets/javascripts/wizard/models/custom.js.es6 b/assets/javascripts/wizard/models/custom.js.es6
index 28ae3f89..1a78b4c4 100644
--- a/assets/javascripts/wizard/models/custom.js.es6
+++ b/assets/javascripts/wizard/models/custom.js.es6
@@ -1,17 +1,17 @@
-import { default as computed } from 'ember-addons/ember-computed-decorators';
+import { default as computed } from 'discourse-common/utils/decorators';
import getUrl from 'discourse-common/lib/get-url';
import WizardField from 'wizard/models/wizard-field';
import { ajax } from 'wizard/lib/ajax';
import Step from 'wizard/models/step';
+import EmberObject from "@ember/object";
-const CustomWizard = Ember.Object.extend({
+const CustomWizard = EmberObject.extend({
@computed('steps.length')
totalSteps: length => length,
skip() {
- if (this.get('required') && (!this.get('completed') && this.get('permitted'))) return;
- const id = this.get('id');
- CustomWizard.skip(id);
+ if (this.required && (!this.completed && this.permitted)) return;
+ CustomWizard.skip(this.id);
},
});
@@ -50,7 +50,7 @@ export function findCustomWizard(wizardId, params = {}) {
}
return ajax({ url, cache: false, dataType: 'json' }).then(result => {
- const wizard = result.custom_wizard;
+ const wizard = result;
if (!wizard) return null;
if (!wizard.completed) {
@@ -70,7 +70,7 @@ export function findCustomWizard(wizardId, params = {}) {
subcatMap[c.parent_category_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
diff --git a/assets/javascripts/wizard/routes/custom-index.js.es6 b/assets/javascripts/wizard/routes/custom-index.js.es6
index c857753d..b0c1728c 100644
--- a/assets/javascripts/wizard/routes/custom-index.js.es6
+++ b/assets/javascripts/wizard/routes/custom-index.js.es6
@@ -14,7 +14,6 @@ export default Ember.Route.extend({
if (model) {
const completed = model.get('completed');
const permitted = model.get('permitted');
- const minTrust = model.get('min_trust');
const wizardId = model.get('id');
const user = model.get('user');
const name = model.get('name');
@@ -25,7 +24,6 @@ export default Ember.Route.extend({
name,
completed,
notPermitted: !permitted,
- minTrust,
wizardId
});
} else {
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-category.hbs b/assets/javascripts/wizard/templates/components/wizard-field-category.hbs
index 904dd414..1843a277 100644
--- a/assets/javascripts/wizard/templates/components/wizard-field-category.hbs
+++ b/assets/javascripts/wizard/templates/components/wizard-field-category.hbs
@@ -1,5 +1,6 @@
-{{category-selector
+{{wizard-category-selector
categories=categories
+ whitelist=field.content
maximum=field.limit
onChange=(action (mut categories))}}
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs b/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs
index 4f411ce3..cde88a50 100644
--- a/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs
+++ b/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs
@@ -1,7 +1,7 @@
-{{combo-box elementId=field.id
- class=fieldClass
- value=field.value
- content=field.choices
- none=(hash id="__none__" label=field.dropdown_none)
- nameProperty="label"
- tabindex="9"}}
\ No newline at end of file
+{{combo-box
+ class=fieldClass
+ value=field.value
+ content=field.content
+ options=(hash
+ none="select_kit.default_header_text"
+ )}}
\ No newline at end of file
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-group.hbs b/assets/javascripts/wizard/templates/components/wizard-field-group.hbs
new file mode 100644
index 00000000..f10aae2e
--- /dev/null
+++ b/assets/javascripts/wizard/templates/components/wizard-field-group.hbs
@@ -0,0 +1,9 @@
+{{wizard-group-selector
+ groups=wizard.groups
+ field=field
+ whitelist=field.content
+ value=field.value
+ onChange=(action (mut field.value))
+ options=(hash
+ none='group.select'
+ )}}
\ No newline at end of file
diff --git a/assets/javascripts/wizard/templates/components/wizard-field.hbs b/assets/javascripts/wizard/templates/components/wizard-field.hbs
index 8173fe78..c8c785b0 100644
--- a/assets/javascripts/wizard/templates/components/wizard-field.hbs
+++ b/assets/javascripts/wizard/templates/components/wizard-field.hbs
@@ -1,7 +1,5 @@
-