diff --git a/assets/javascripts/discourse/components/wizard-mapper-connector.js.es6 b/assets/javascripts/discourse/components/wizard-mapper-connector.js.es6 index aaf555b4..ee6c38b2 100644 --- a/assets/javascripts/discourse/components/wizard-mapper-connector.js.es6 +++ b/assets/javascripts/discourse/components/wizard-mapper-connector.js.es6 @@ -1,7 +1,6 @@ import Component from "@ember/component"; import { gt } from '@ember/object/computed'; import { computed } from "@ember/object"; -import { removeMapperClasses } from '../lib/wizard-mapper'; export default Component.extend({ classNameBindings: [':mapper-connector', ':mapper-block', 'hasMultiple::single'], @@ -10,11 +9,5 @@ export default Component.extend({ let key = this.connector; let path = this.inputTypes ? `input.${key}.name` : `connector.${key}`; return I18n.t(`admin.wizard.${path}`); - }), - - actions: { - onOpen() { - removeMapperClasses(this); - } - } + }) }); \ 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 index 22ad9ef2..b77f80f3 100644 --- a/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6 +++ b/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6 @@ -2,7 +2,7 @@ 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, removeMapperClasses } from '../lib/wizard-mapper'; +import { defaultSelectionType, selectionTypes } from '../lib/wizard-mapper'; import { snakeCase } from '../lib/wizard'; import Component from "@ember/component"; import { bind } from "@ember/runloop"; @@ -30,6 +30,7 @@ export default Component.extend({ 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() { $(document).on("click", bind(this, this.documentClick)); @@ -41,15 +42,10 @@ export default Component.extend({ documentClick(e) { if (this._state == "destroying") return; - let $target = $(e.target); - - if (!$target.parents('.wizard-mapper .input').length) { - this.send('disableActive'); - } - - if (!$target.parents('.type-selector').length) { - this.send('hideTypes'); + + if (!$target.parents('.type-selector').length && this.showTypes) { + this.set('showTypes', false); } }, @@ -80,9 +76,9 @@ export default Component.extend({ @discourseComputed('activeType') comboBoxContent(activeType) { - const controller = getOwner(this).lookup('controller:admin-wizard'); + 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); @@ -148,34 +144,14 @@ export default Component.extend({ return this.activeType === type && this[`${type}Enabled`]; }, - removeClasses() { - removeMapperClasses(this); - }, - actions: { toggleType(type) { this.set('activeType', type); - this.send('hideTypes'); + this.set('showTypes', false); }, - // jquery is used here to ensure other selectors and types disable properly - - showTypes() { - this.removeClasses(); - $(this.element).find('.selector-types').addClass('show'); - }, - - hideTypes() { - $(this.element).find('.selector-types').removeClass('show'); - }, - - enableActive() { - this.removeClasses(); - $(this.element).addClass('active'); - }, - - disableActive() { - $(this.element).removeClass('active'); + toggleTypes() { + this.toggleProperty('showTypes'); } } }) \ No newline at end of file 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 100% rename from assets/javascripts/discourse/controllers/admin-wizards-api.js.es6 rename to assets/javascripts/discourse/controllers/admin-wizards-api-show.js.es6 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 7b087af3..00000000 --- a/assets/javascripts/discourse/controllers/admin-wizards-apis.js.es6 +++ /dev/null @@ -1,5 +0,0 @@ -import Controller from "@ember/controller"; - -export default Controller.extend({ - queryParams: ['refresh'] -}); diff --git a/assets/javascripts/discourse/controllers/admin-wizard-submissions.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 similarity index 100% rename from assets/javascripts/discourse/controllers/admin-wizard-submissions.js.es6 rename to assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 diff --git a/assets/javascripts/discourse/controllers/admin-wizard.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 similarity index 70% rename from assets/javascripts/discourse/controllers/admin-wizard.js.es6 rename to assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 index 42c3d534..e2aaec10 100644 --- a/assets/javascripts/discourse/controllers/admin-wizard.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 @@ -8,10 +8,10 @@ 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('model.name'), - userFields: alias('model.userFields'), + hasName: notEmpty('wizard.name'), @observes('currentStep') resetCurrentObjects() { @@ -25,28 +25,29 @@ export default Controller.extend({ scheduleOnce('afterRender', () => ($("body").addClass('admin-wizard'))); }, - @observes('model.name') + @observes('wizard.name') setId() { - if (!this.model.existingId) { - this.set('model.id', generateId(this.model.name)); + const wizard = this.wizard; + if (wizard && !wizard.existingId) { + this.set('wizard.id', generateId(wizard.name)); } }, - @discourseComputed('model.id') + @discourseComputed('wizard.id') wizardUrl(wizardId) { return window.location.origin + '/w/' + dasherize(wizardId); }, - @discourseComputed('model.after_time_scheduled') + @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', 'model.save_submissions', 'model.steps.@each.fields[]') + @discourseComputed('currentStep.id', 'wizard.save_submissions', 'wizard.steps.@each.fields[]') wizardFields(currentStepId, saveSubmissions) { - const allSteps = this.get('model.steps'); + const allSteps = this.get('wizard.steps'); let steps = allSteps; let fields = []; @@ -71,31 +72,24 @@ export default Controller.extend({ return fields; }, - actions: { + actions: { save() { this.setProperties({ saving: true, error: null }); - const wizard = this.model; + const wizard = this.wizard; + const creating = this.creating; + let opts = {}; - wizard.save().then((result) => { - - this.model.setProperties( - buildProperties(result.wizard) - ); + if (creating) { + opts.create = true; + } - this.set('saving', false); - - if (this.get('newWizard')) { - this.send("refreshAllWizards"); - } else { - this.send("refreshWizard"); - } - }).catch((result) => { - this.set('saving', false); - + wizard.save(opts).then((result) => { + this.send('afterSave', result.wizard_id); + }).catch((result) => { let errorType = 'failed'; let errorParams = {}; @@ -107,21 +101,18 @@ export default Controller.extend({ this.set('error', I18n.t(`admin.wizard.error.${errorType}`, errorParams)); later(() => this.set('error', null), 10000); - }); + }).finally(() => this.set('saving', false)); }, remove() { - const wizard = this.get('model'); - wizard.remove().then(() => { - this.send("refreshAllWizards"); - }); + this.wizard.remove().then(() => this.send('afterDestroy')); }, setNextSessionScheduled() { let controller = showModal('next-session-scheduled', { model: { - dateTime: this.get('model.after_time_scheduled'), - update: (dateTime) => this.set('model.after_time_scheduled', dateTime) + dateTime: this.wizard.after_time_scheduled, + update: (dateTime) => this.set('wizard.after_time_scheduled', dateTime) } }); @@ -129,7 +120,7 @@ export default Controller.extend({ }, toggleAdvanced() { - this.toggleProperty('model.showAdvanced'); + this.toggleProperty('wizard.showAdvanced'); }, copyUrl() { 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/custom-wizard-admin-route-map.js.es6 b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 index 205e2454..66cdacea 100644 --- a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 +++ b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 @@ -2,14 +2,17 @@ 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('adminWizardsTransfer', { path: '/transfer', resetNamespace: true }); diff --git a/assets/javascripts/discourse/lib/wizard-mapper.js.es6 b/assets/javascripts/discourse/lib/wizard-mapper.js.es6 index 533d852d..3d3ad034 100644 --- a/assets/javascripts/discourse/lib/wizard-mapper.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-mapper.js.es6 @@ -22,12 +22,6 @@ function inputTypesContent(options = {}) { mapInputTypes(selectableInputTypes); } -function removeMapperClasses(ctx) { - const $mapper = $(ctx.element).parents('.wizard-mapper'); - $mapper.find('.selector-types').removeClass('show'); - $mapper.find('.mapper-selector').removeClass('active'); -} - // Connectors const connectors = { @@ -160,7 +154,6 @@ export { defaultInputType, defaultSelectionType, defaultConnector, - removeMapperClasses, connectorContent, inputTypesContent, selectionTypes, diff --git a/assets/javascripts/discourse/models/custom-wizard.js.es6 b/assets/javascripts/discourse/models/custom-wizard.js.es6 index 2e1decb4..e65ad169 100644 --- a/assets/javascripts/discourse/models/custom-wizard.js.es6 +++ b/assets/javascripts/discourse/models/custom-wizard.js.es6 @@ -5,19 +5,26 @@ import { schema, listProperties, camelCase, snakeCase } from '../lib/wizard'; import { Promise } from "rsvp"; const CustomWizard = EmberObject.extend({ - save() { + save(opts) { return new Promise((resolve, reject) => { - let json = this.buildJson(this, 'wizard'); + let wizard = this.buildJson(this, 'wizard'); - if (json.error) { - reject({ error: json.error }); + if (wizard.error) { + reject(wizard); } - ajax("/admin/wizards/custom/save", { + let data = { + wizard + }; + + if (opts.create) { + data.create = true; + } + + ajax(`/admin/wizards/wizard/${wizard.id}`, { type: 'PUT', - data: { - wizard: JSON.stringify(json) - } + contentType: "application/json", + data: JSON.stringify(data) }).then((result) => { if (result.error) { reject(result); @@ -40,7 +47,7 @@ const CustomWizard = EmberObject.extend({ return result; } } - + for (let property of listProperties(type, objectType)) { let value = object.get(property); @@ -175,23 +182,18 @@ const CustomWizard = EmberObject.extend({ }, 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(wizard => { - return CustomWizard.create(wizard); - }); + return result.wizard_list; }); }, 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 826fbf7b..00000000 --- a/assets/javascripts/discourse/routes/admin-wizard.js.es6 +++ /dev/null @@ -1,108 +0,0 @@ -import CustomWizard from '../models/custom-wizard'; -import { ajax } from 'discourse/lib/ajax'; -import { selectKitContent, userProperties, generateName } from '../lib/wizard'; -import DiscourseRoute from "discourse/routes/discourse"; -import { all } from "rsvp"; - -export default DiscourseRoute.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; - this.set('newWizard', wizardId === 'new'); - - if (this.newWizard) { - return CustomWizard.create(); - } else { - const wizard = this.modelFor('admin-wizards-custom') - .findBy('id', wizardId.underscore()); - - if (!wizard) { - return this.transitionTo('adminWizard', 'new'); - } else { - return wizard; - } - } - }, - - afterModel(model) { - return all([ - this._getFieldTypes(model), - this._getThemes(model), - this._getApis(model), - this._getUserFields(model) - ]); - }, - - _getFieldTypes(model) { - return ajax('/admin/wizards/field-types') - .then((result) => { - model.set( - 'fieldTypes', - selectKitContent([...result.types]) - ) - }); - }, - - _getThemes(model) { - return ajax('/admin/themes') - .then((result) => { - model.set('themes', result.themes.map(t => { - return { - id: t.id, - name: t.name - } - })); - }); - }, - - _getApis(model) { - return ajax('/admin/wizards/apis') - .then((result) => model.set('apis', result)); - }, - - _getUserFields(model) { - return this.store.findAll('user-field').then((result) => { - if (result && result.content) { - model.set('userFields', - result.content.map((f) => ({ - id: `user_field_${f.id}`, - name: f.name - })).concat( - userProperties.map((f) => ({ - id: f, - name: generateName(f) - })) - ) - ); - } - }); - }, - - setupController(controller, model) { - const newWizard = this.get('newWizard'); - - controller.setProperties({ - newWizard, - model, - currentStep: model.steps[0], - currentAction: model.actions[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 6d710c41..62ecd8fe 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-api.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-api.js.es6 @@ -1,22 +1,7 @@ -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); + 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 37c594c9..00000000 --- a/assets/javascripts/discourse/routes/admin-wizards-apis.js.es6 +++ /dev/null @@ -1,32 +0,0 @@ -import CustomWizardApi from '../models/custom-wizard-api'; -import DiscourseRoute from "discourse/routes/discourse"; - -export default DiscourseRoute.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.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-custom.js.es6 deleted file mode 100644 index 716b92bb..00000000 --- a/assets/javascripts/discourse/routes/admin-wizards-custom.js.es6 +++ /dev/null @@ -1,27 +0,0 @@ -import CustomWizard from '../models/custom-wizard'; -import DiscourseRoute from "discourse/routes/discourse"; - -export default DiscourseRoute.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 bcfd1251..00000000 --- a/assets/javascripts/discourse/routes/admin-wizards-index.js.es6 +++ /dev/null @@ -1,7 +0,0 @@ -import DiscourseRoute from "discourse/routes/discourse"; - -export default DiscourseRoute.extend({ - redirect() { - this.transitionTo('adminWizardsCustom'); - } -}); diff --git a/assets/javascripts/discourse/routes/admin-wizards-submissions-index.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-submissions-index.js.es6 deleted file mode 100644 index 6bfca635..00000000 --- a/assets/javascripts/discourse/routes/admin-wizards-submissions-index.js.es6 +++ /dev/null @@ -1,7 +0,0 @@ -import DiscourseRoute from "discourse/routes/discourse"; - -export default DiscourseRoute.extend({ - redirect() { - this.transitionTo('adminWizardSubmissions', 'first'); - } -}); diff --git a/assets/javascripts/discourse/routes/admin-wizard-submissions.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 similarity index 57% rename from assets/javascripts/discourse/routes/admin-wizard-submissions.js.es6 rename to assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 index b1f6b226..d4818024 100644 --- a/assets/javascripts/discourse/routes/admin-wizard-submissions.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 @@ -2,25 +2,8 @@ import CustomWizard from '../models/custom-wizard'; import DiscourseRoute from "discourse/routes/discourse"; export default DiscourseRoute.extend({ - beforeModel() { - const param = this.paramsFor('adminWizardSubmissions').wizard_id; - const wizards = this.modelFor('admin-wizards-submissions'); - - if (wizards.length && (param === 'first')) { - const wizard = wizards.get(`${param}Object`); - if (wizard) { - this.transitionTo('adminWizardSubmissions', wizard.id.dasherize()); - } - } - }, - model(params) { - const wizardId = params.wizard_id; - if (wizardId && wizardId !== 'new') { - return CustomWizard.submissions(params.wizard_id); - } else { - return {}; - } + return CustomWizard.submissions(params.wizardId); }, setupController(controller, model) { @@ -43,8 +26,6 @@ export default DiscourseRoute.extend({ submissions.push(submission); }); - console.log(model.id) - controller.setProperties({ wizard: model.wizard, submissions, diff --git a/assets/javascripts/discourse/routes/admin-wizards-submissions.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-submissions.js.es6 index 9dc509fa..6dff576d 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-submissions.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-submissions.js.es6 @@ -1,12 +1,24 @@ -import CustomWizard from '../models/custom-wizard'; import DiscourseRoute from "discourse/routes/discourse"; +import { ajax } from 'discourse/lib/ajax'; 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-wizard-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 new file mode 100644 index 00000000..22b4c0f3 --- /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..5b2c88e3 --- /dev/null +++ b/assets/javascripts/discourse/routes/admin-wizards-wizard.js.es6 @@ -0,0 +1,92 @@ +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/apis') + .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) + })) + ) + ); + } + }); + }, + + setupController(controller, model) { + let props = { + wizardList: model.wizard_list + } + + const params = this.paramsFor('adminWizardsWizardShow'); + if (params && params.wizardId) { + props.wizardId = params.wizardId; + } + + 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 1265db8a..00000000 --- a/assets/javascripts/discourse/templates/admin-wizard-submissions.hbs +++ /dev/null @@ -1,27 +0,0 @@ -
- - - - {{d-icon 'download'}} - - {{i18n "admin.wizard.submissions.download"}} - - -
- -
- - - {{#each fields as |f|}} - - {{/each}} - - {{#each submissions as |s|}} - - {{#each-in s as |k v|}} - - {{/each-in}} - - {{/each}} -
{{f}}
{{v}}
-
diff --git a/assets/javascripts/discourse/templates/admin-wizards-api.hbs b/assets/javascripts/discourse/templates/admin-wizards-api-show.hbs similarity index 100% rename from assets/javascripts/discourse/templates/admin-wizards-api.hbs rename to assets/javascripts/discourse/templates/admin-wizards-api-show.hbs 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 @@ -
-
- -
- {{#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 @@ -
-
- -
- {{#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-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}} +
+ + + + {{d-icon 'download'}} + + {{i18n "admin.wizard.submissions.download"}} + + +
+ +
+ + + {{#each fields as |f|}} + + {{/each}} + + {{#each submissions as |s|}} + + {{#each-in s as |k v|}} + + {{/each-in}} + + {{/each}} +
{{f}}
{{v}}
+
+{{/if}} diff --git a/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs b/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs index 1637b6bf..ca0b835e 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs @@ -1,15 +1,13 @@ -
-
- -
- -
- {{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-wizard.hbs b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs similarity index 83% rename from assets/javascripts/discourse/templates/admin-wizard.hbs rename to assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs index c89eac36..e0d511c1 100644 --- a/assets/javascripts/discourse/templates/admin-wizard.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs @@ -1,22 +1,22 @@ -
+{{#if wizard}}
{{input name="name" - value=model.name + value=wizard.name placeholderKey="admin.wizard.name_placeholder"}}
- {{#if model.name}} - {{wizardUrl}} + {{#if wizard.name}} {{#if copiedUrl}} {{d-button class="btn-hover pull-right" icon="copy" label="ip_lookup.copied"}} {{else}} {{d-button action=(action "copyUrl") class="pull-right no-text" icon="copy"}} {{/if}} + {{wizardUrl}} {{/if}}
- +
@@ -25,7 +25,7 @@
{{input name="background" - value=model.background + value=wizard.background placeholderKey="admin.wizard.background_placeholder" class="small"}}
@@ -37,28 +37,28 @@
{{combo-box - content=model.themes + content=themes valueProperty='id' - value=model.theme_id - onChange=(action (mut model.theme_id)) + value=wizard.theme_id + onChange=(action (mut wizard.theme_id)) options=(hash none='admin.wizard.no_theme' )}}
- +
{{i18n 'admin.wizard.label'}}
- +
- {{input type='checkbox' checked=model.required}} + {{input type='checkbox' checked=wizard.required}} {{i18n 'admin.wizard.required_label'}}
@@ -68,7 +68,7 @@
- {{input type='checkbox' checked=model.after_signup}} + {{input type='checkbox' checked=wizard.after_signup}} {{i18n 'admin.wizard.after_signup_label'}}
@@ -78,7 +78,7 @@
- {{input type='checkbox' checked=model.multiple_submissions}} + {{input type='checkbox' checked=wizard.multiple_submissions}} {{i18n 'admin.wizard.multiple_submissions_label'}}
@@ -88,7 +88,7 @@
- {{input type='checkbox' checked=model.prompt_completion}} + {{input type='checkbox' checked=wizard.prompt_completion}} {{i18n 'admin.wizard.prompt_completion_label'}}
@@ -98,7 +98,7 @@
- {{input type='checkbox' checked=model.after_time}} + {{input type='checkbox' checked=wizard.after_time}} {{i18n 'admin.wizard.after_time_label'}} {{d-button action='setNextSessionScheduled' @@ -114,7 +114,7 @@
{{wizard-mapper - inputs=model.permitted + inputs=wizard.permitted options=(hash context='wizard' inputTypes='assignment,validation' @@ -126,9 +126,9 @@
- {{wizard-advanced-toggle showAdvanced=model.showAdvanced}} + {{wizard-advanced-toggle showAdvanced=wizard.showAdvanced}} - {{#if model.showAdvanced}} + {{#if wizard.showAdvanced}}
@@ -136,7 +136,7 @@
- {{input type='checkbox' checked=model.save_submissions}} + {{input type='checkbox' checked=wizard.save_submissions}} {{i18n 'admin.wizard.save_submissions_label'}}
@@ -146,7 +146,7 @@
- {{input type='checkbox' checked=model.restart_on_revisit}} + {{input type='checkbox' checked=wizard.restart_on_revisit}} {{i18n 'admin.wizard.restart_on_revisit_label'}}
@@ -158,26 +158,27 @@ {{wizard-links itemType="step" current=currentStep - items=model.steps}} - + items=wizard.steps}} + {{#if currentStep}} {{wizard-custom-step step=currentStep - wizard=model + wizard=wizard currentField=currentField - wizardFields=wizardFields}} + wizardFields=wizardFields + fieldTypes=fieldTypes}} {{/if}} - + {{wizard-links itemType="action" current=currentAction - items=model.actions + items=wizard.actions generateLabels=true}} {{#if currentAction}} {{wizard-custom-action action=currentAction - wizard=model + wizard=wizard removeAction="removeAction" wizardFields=wizardFields}} {{/if}} @@ -187,7 +188,7 @@ {{i18n 'admin.wizard.save'}} - {{#unless newWizard}} + {{#unless creating}} @@ -199,4 +200,4 @@ {{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..08c33fbf --- /dev/null +++ b/assets/javascripts/discourse/templates/admin-wizards-wizard.hbs @@ -0,0 +1,32 @@ +
+ {{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}} +
+ +
+ {{d-icon 'question-circle'}} + + {{i18n 'admin.wizard.message.help'}} + +
+
+ +
+ {{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 578e4380..102ffcde 100644 --- a/assets/javascripts/discourse/templates/admin-wizards.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards.hbs @@ -1,5 +1,5 @@ {{#admin-nav}} - {{nav-item route='adminWizardsCustom' label='admin.wizard.nav_label'}} + {{nav-item route='adminWizardsWizard' label='admin.wizard.nav_label'}} {{nav-item route='adminWizardsSubmissions' label='admin.wizard.submissions.nav_label'}} {{#if siteSettings.wizard_api_features}} {{nav-item route='adminWizardsApis' label='admin.wizard.api.nav_label'}} @@ -7,6 +7,6 @@ {{nav-item route='adminWizardsTransfer' label='admin.wizard.transfer.nav_label'}} {{/admin-nav}} -
+
{{outlet}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs index b2ba27e9..016100b5 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs @@ -64,7 +64,11 @@
- {{input type="number" name="min_length" value=field.min_length}} + {{input + type="number" + name="min_length" + value=field.min_length + class="small"}}
{{/if}} @@ -88,7 +92,7 @@
- {{input type="number" value=field.limit}} + {{input type="number" value=field.limit class="small"}}
{{/if}} @@ -148,8 +152,12 @@
-
- {{input name="key" value=field.key placeholderKey="admin.wizard.translation_placeholder"}} +
+ {{input + name="key" + value=field.key + class="small" + placeholderKey="admin.wizard.translation_placeholder"}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs index 6752f6af..c2f0b073 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs @@ -104,7 +104,7 @@ {{#if currentField}} {{wizard-custom-field field=currentField - fieldTypes=wizard.fieldTypes + 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 6f4e0c33..9bbfabcf 100644 --- a/assets/javascripts/discourse/templates/components/wizard-export.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-export.hbs @@ -8,7 +8,7 @@ id=(dasherize w.id) change=(action 'checkChanged')}} - {{#link-to "adminWizard" (dasherize w.id)}} + {{#link-to "adminWizardsWizardShow" (dasherize w.id)}} {{w.name}} {{/link-to}} diff --git a/assets/javascripts/discourse/templates/components/wizard-mapper-connector.hbs b/assets/javascripts/discourse/templates/components/wizard-mapper-connector.hbs index ea1a365d..9ac7ddfe 100644 --- a/assets/javascripts/discourse/templates/components/wizard-mapper-connector.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-mapper-connector.hbs @@ -2,8 +2,7 @@ {{combo-box value=connector content=connectors - onChange=(action (mut connector)) - onOpen=(action "onOpen")}} + onChange=(action (mut connector))}} {{else}} {{connectorLabel}} diff --git a/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs b/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs index dd4e06df..b0c0d8fd 100644 --- a/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs @@ -1,17 +1,19 @@
{{#if hasTypes}} - + {{activeTypeLabel}} -
- {{#each selectorTypes as |item|}} - {{wizard-mapper-selector-type - activeType=activeType - item=item - toggle=(action 'toggleType')}} - {{/each}} -
+ {{#if showTypes}} +
+ {{#each selectorTypes as |item|}} + {{wizard-mapper-selector-type + activeType=activeType + item=item + toggle=(action 'toggleType')}} + {{/each}} +
+ {{/if}} {{else}} {{activeTypeLabel}} {{/if}} @@ -22,7 +24,6 @@ {{input type="text" value=value - click=(action 'enableActive') placeholder=(i18n placeholderKey)}} {{/if}} @@ -31,8 +32,6 @@ value=value content=comboBoxContent onChange=(action (mut value)) - onOpen=(action "enableActive") - onClick=(action 'enableActive') options=(hash none=placeholderKey )}} @@ -43,8 +42,6 @@ content=multiSelectContent value=value onChange=(action (mut value)) - onOpen=(action "enableActive") - onClose=(action "disableActive") options=multiSelectOptions}} {{/if}} @@ -58,8 +55,6 @@ {{tag-chooser tags=value filterable=true - onOpen=(action "enableActive") - onClose=(action "disableActive") options=(hash none=placeholderKey )}} @@ -70,7 +65,6 @@ includeMessageableGroups='true' placeholderKey=placeholderKey usernames=value - autocomplete="discourse" - click=(action "enableActive")}} + autocomplete="discourse"}} {{/if}}
\ No newline at end of file diff --git a/assets/javascripts/wizard-custom.js b/assets/javascripts/wizard-custom.js index 0bb16dba..d67fa1b9 100644 --- a/assets/javascripts/wizard-custom.js +++ b/assets/javascripts/wizard-custom.js @@ -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/models/custom.js.es6 b/assets/javascripts/wizard/models/custom.js.es6 index 9477e813..1a78b4c4 100644 --- a/assets/javascripts/wizard/models/custom.js.es6 +++ b/assets/javascripts/wizard/models/custom.js.es6 @@ -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) { diff --git a/assets/stylesheets/common/wizard-admin.scss b/assets/stylesheets/common/wizard-admin.scss index 9794fba7..e4b25e12 100644 --- a/assets/stylesheets/common/wizard-admin.scss +++ b/assets/stylesheets/common/wizard-admin.scss @@ -2,35 +2,30 @@ @import 'wizard-transfer'; @import 'wizard-api'; +.admin-wizard-controls { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 20px; +} + +.admin-wizard-message { + background-color: $primary-low; + padding: 10px; + display: flex; + justify-content: space-between; +} + .admin-wizard-container { - .row { - display: flex; - - > .content { - flex: 1; - margin-top: 10px; - margin-left: 30px; - - table { - margin-top: 0; - min-width: 100%; - width: auto; - } - } + margin-top: 20px; + + .row > .content table { + margin-top: 0; + min-width: 100%; + width: auto; } } -.wizard-submissions { - overflow: scroll; -} - -.wizard-list { - width: 250px; - min-width: 250px; - margin-top: 10px; - float: none; -} - .wizard-settings-parent { margin-bottom: 30px; padding: 20px; @@ -65,25 +60,7 @@ padding: 20px; } -// style workdarounds for wizard_step_advanced site setting - to be refactored - -.wizard-custom-step .wizard-advanced-toggle + .wizard-links.field, -.wizard-custom-step .advanced-settings + .wizard-links.field, -.wizard-links.action { - margin-top: 40px; -} - -.wizard-links.field { - margin-top: 20px; -} - -// end workdarounds // - -.wizard-settings > .advanced-settings > div.setting { - margin-bottom: 0; -} - -.admin-wizard.settings .wizard-basic-details { +.admin-wizard-container.settings .wizard-basic-details { justify-content: initial; .setting { @@ -97,23 +74,20 @@ } } -.new-wizard { - margin-top: 15px; -} - .wizard-header { margin-bottom: 20px; &.large { font-size: 1.5em; min-height: 31px; - margin-bottom: 30px; + margin-bottom: 20px; display: flex; justify-content: space-between; + align-items: flex-start; input { margin-bottom: 0; - width: 350px; + width: 400px; } label { @@ -124,6 +98,22 @@ font-size: 1rem; line-height: 20px; } + + .wizard-url { + display: inline-flex; + margin-left: 20px; + max-width: 50%; + + a { + padding: 6px 12px; + font-size: 1rem; + background-color: $primary-low; + } + + button { + font-size: 1rem; + } + } } &.medium { @@ -138,28 +128,13 @@ &.underline { text-decoration: underline; } - - .wizard-url { - display: inline-flex; - margin-left: 20px; - - a { - padding: 6px 12px; - font-size: 1rem; - background-color: $primary-low; - } - - button { - font-size: 1rem; - } - } } .admin-wizard-buttons { margin-top: 20px; } -.admin-wizard.settings { +.admin-wizard-container.settings { [class~='setting'] { display: inline-flex; @@ -208,15 +183,14 @@ cursor: not-allowed; } - input[type="number"] { - width: 70px; - margin-bottom: 0; - } - input.medium { width: 200px; } + input.small { + width: 100px; + } + .uploaded-image-preview { width: 100%; max-height: 100px; @@ -239,6 +213,11 @@ float: left; margin: 5px 7px 0 0; } + + .input .select-kit, > .select-kit { + max-width: 250px !important; + min-width: 250px !important; + } } &.full, &.full-inline { @@ -255,7 +234,7 @@ } .uploaded-image-preview { - max-height: 170px; + max-height: 200px; } } } @@ -293,7 +272,7 @@ } } - .wizard-custom-action > [class~='setting']:last-of-type { + .wizard-custom-action > [class~='setting']:first-of-type { margin-bottom: 0; } @@ -328,7 +307,7 @@ width: 100%; .d-editor-input { - min-height: 130px; + min-height: 150px; } .d-editor-container { diff --git a/assets/stylesheets/common/wizard-mapper.scss b/assets/stylesheets/common/wizard-mapper.scss index 2903aeb6..97f8b2aa 100644 --- a/assets/stylesheets/common/wizard-mapper.scss +++ b/assets/stylesheets/common/wizard-mapper.scss @@ -100,41 +100,16 @@ } } -.mapper-input.assignment, -.mapper-input.validation, -.mapper-input.association { - .mapper-selector { - max-width: 250px; - min-width: 250px; - - > input, .select-kit, .ac-wrap, .autocomplete.ac-user { - width: 250px !important; - } - } -} - -.mapper-input.conditional { - .mapper-selector { - max-width: 170px; - min-width: 170px; - - &:not(.text).active .input { - width: 250px; - box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.2); - position: absolute; - z-index: 300; - - .select-kit, .ac-wrap, .autocomplete.ac-user, .select-kit-wrapper { - width: 250px !important; - } - } - } -} - .mapper-selector { width: 100%; + max-width: 250px; + min-width: 250px; position: relative; + > input, .select-kit, .ac-wrap, .autocomplete.ac-user { + width: 250px !important; + } + .input { width: 100%; transition: all 0.1s; @@ -170,16 +145,12 @@ .selector-types { box-shadow: shadow('dropdown'); position: absolute; - display: none; + display: flex; background: $secondary; z-index: 200; padding: 5px 7px; flex-direction: column; border: 1px solid $primary-low; - - &.show { - display: flex; - } } .value-list .remove-value-btn { diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 4214e5a7..b3e729b8 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -8,11 +8,12 @@ en: wizard: label: "Wizard" nav_label: "Wizards" - new: "New" + select: "Select a wizard" + create: "Create Wizard" name: "Name" name_placeholder: "wizard name" background: "Background" - background_placeholder: "#hex" + background_placeholder: "color" save_submissions: "Save" save_submissions_label: "Save wizard submissions." multiple_submissions: "Multiple" @@ -53,6 +54,12 @@ en: permitted: "Permitted" advanced: "Advanced" + message: + select: "Select a wizard, or create a new one" + edit: "You're editing a wizard" + create: "You're creating a new wizard" + help: "Stuck? Check out the documentation" + editor: show: "Show" hide: "Hide" @@ -97,6 +104,7 @@ en: required: "{{type}} requires {{property}}" invalid: "{{property}} is invalid" dependent: "{{property}} is dependent on {{dependent}}" + conflict: "{{type}} with {{property}} '{{value}}' already exists" step: header: "Steps" diff --git a/config/routes.rb b/config/routes.rb index b586e55b..539d8b92 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -12,25 +12,26 @@ Discourse::Application.routes.append do scope module: 'custom_wizard', constraints: AdminConstraint.new do get 'admin/wizards' => 'admin#index' - get 'admin/wizards/field-types' => 'admin#field_types' - get 'admin/wizards/custom' => 'admin#index' - get 'admin/wizards/custom/new' => 'admin#index' - get 'admin/wizards/custom/all' => 'admin#custom_wizards' - get 'admin/wizards/custom/:wizard_id' => 'admin#find_wizard' - get 'admin/wizards/custom/:wizard_id' => 'admin#find_wizard' - put 'admin/wizards/custom/save' => 'admin#save' - delete 'admin/wizards/custom/remove' => 'admin#remove' - get 'admin/wizards/submissions' => 'admin#index' - get 'admin/wizards/submissions/:wizard_id' => 'admin#submissions' - get 'admin/wizards/submissions/:wizard_id/download' => 'admin#download_submissions' - get 'admin/wizards/apis' => 'api#list' - get 'admin/wizards/apis/new' => 'api#index' - get 'admin/wizards/apis/:name' => 'api#find' - put 'admin/wizards/apis/:name' => 'api#save' - delete 'admin/wizards/apis/:name' => 'api#remove' - delete 'admin/wizards/apis/logs/:name' => 'api#clearlogs' - get 'admin/wizards/apis/:name/redirect' => 'api#redirect' - get 'admin/wizards/apis/:name/authorize' => 'api#authorize' + + get 'admin/wizards/wizard' => 'admin_wizard#index' + get 'admin/wizards/wizard/create' => 'admin#index' + get 'admin/wizards/wizard/:wizard_id' => 'admin_wizard#show' + put 'admin/wizards/wizard/:wizard_id' => 'admin_wizard#save' + delete 'admin/wizards/wizard/:wizard_id' => 'admin_wizard#remove' + + get 'admin/wizards/submissions' => 'admin_submissions#index' + get 'admin/wizards/submissions/:wizard_id' => 'admin_submissions#show' + get 'admin/wizards/submissions/:wizard_id/download' => 'admin_submissions#download' + + get 'admin/wizards/apis' => 'admin_api#list' + get 'admin/wizards/apis/new' => 'admin_api#index' + get 'admin/wizards/apis/:name' => 'admin_api#find' + put 'admin/wizards/apis/:name' => 'admin_api#save' + delete 'admin/wizards/apis/:name' => 'admin_api#remove' + delete 'admin/wizards/apis/logs/:name' => 'admin_api#clearlogs' + get 'admin/wizards/apis/:name/redirect' => 'admin_api#redirect' + get 'admin/wizards/apis/:name/authorize' => 'admin_api#authorize' + get 'admin/wizards/transfer' => 'transfer#index' get 'admin/wizards/transfer/export' => 'transfer#export' post 'admin/wizards/transfer/import' => 'transfer#import' diff --git a/controllers/custom_wizard/admin.rb b/controllers/custom_wizard/admin.rb deleted file mode 100644 index 01df5167..00000000 --- a/controllers/custom_wizard/admin.rb +++ /dev/null @@ -1,271 +0,0 @@ -class CustomWizard::AdminController < ::Admin::AdminController - skip_before_action :check_xhr, only: [:download_submissions] - before_action :ensure_admin - - def index - render nothing: true - end - - def field_types - render json: { types: CustomWizard::Field.types } - end - - def save - result = build_wizard - - if result[:error] - render json: { error: result[:error] } - else - wizard = result[:wizard] - existing_wizard = result[:existing_wizard] - after_time = result[:after_time] - - ActiveRecord::Base.transaction do - PluginStore.set('custom_wizard', wizard["id"], wizard) - - if after_time[:enabled] - Jobs.cancel_scheduled_job(:set_after_time_wizard, wizard_id: wizard['id']) - Jobs.enqueue_at(after_time[:scheduled], :set_after_time_wizard, wizard_id: wizard['id']) - end - - if existing_wizard && existing_wizard['after_time'] && !after_time[:enabled] - Jobs.cancel_scheduled_job(:set_after_time_wizard, wizard_id: wizard['id']) - Jobs.enqueue(:clear_after_time_wizard, wizard_id: wizard['id']) - end - end - - render json: success_json.merge(wizard: wizard) - end - end - - def remove - params.require(:id) - - wizard = PluginStore.get('custom_wizard', params[:id]) - - if wizard['after_time'] - Jobs.cancel_scheduled_job(:set_after_time_wizard) - Jobs.enqueue(:clear_after_time_wizard, wizard_id: wizard['id']) - end - - PluginStore.remove('custom_wizard', params[:id]) - - render json: success_json - end - - def find_wizard - params.require(:wizard_id) - wizard = PluginStore.get('custom_wizard', params[:wizard_id].underscore) - render json: success_json.merge(wizard: wizard) - end - - def custom_wizards - rows = PluginStoreRow.where(plugin_name: 'custom_wizard').order(:id) - wizards = [*rows].map { |r| CustomWizard::Template.new(r.value) } - render json: success_json.merge(wizards: wizards) - end - - def submissions - params.require(:wizard_id) - - wizard_id = params[:wizard_id].underscore - wizard = PluginStore.get('custom_wizard', wizard_id) - - if wizard.present? - render json: success_json.merge( - submissions: build_submissions(wizard_id), - wizard: wizard.slice(:id, :name) - ) - else - head :ok - end - end - - def download_submissions - params.require(:wizard_id) - wizard_id = params[:wizard_id].underscore - - wizard = PluginStore.get('custom_wizard', wizard_id) - submissions = build_submissions(wizard_id).to_json - - send_data submissions, - filename: "#{Discourse.current_hostname}-wizard-submissions-#{wizard['name']}.json", - content_type: "application/json" - end - - private - - def wizard_params - params.require(:wizard) - params[:wizard] - end - - def required_properties - { - wizard: ['id', 'name', 'steps'], - step: ['id'], - field: ['id', 'type'], - action: ['id', 'type'] - } - end - - def dependent_properties - { - wizard: { - after_time: 'after_time_scheduled' - }, - step: {}, - field: {}, - action: {} - } - end - - def check_required(object, type, error) - required_properties[type].each do |property| - if object[property].blank? - error = { - type: 'required', - params: { type: type, property: property } - } - end - end - - error - end - - def check_depdendent(object, type, error) - dependent_properties[type].each do |property, dependent| - if object[property] && object[dependent].blank? - error = { - type: 'dependent', - params: { property: property, dependent: dependent } - } - end - end - - error - end - - def validate_wizard(wizard) - error = nil - - error = check_required(wizard, :wizard, error) - error = check_depdendent(wizard, :wizard, error) - - if !error - wizard['steps'].each do |step| - error = check_required(step, :step, error) - error = check_depdendent(step, :step, error) - break if error.present? - - if step['fields'].present? - step['fields'].each do |field| - error = check_required(field, :field, error) - error = check_depdendent(field, :field, error) - break if error.present? - end - end - end - - if wizard['actions'].present? - wizard['actions'].each do |action| - error = check_required(action, :action, error) - error = check_depdendent(action, :action, error) - break if error.present? - end - end - end - - if error - { error: error } - else - { success: true } - end - end - - def validate_after_time(wizard, existing_wizard) - new = false - error = nil - enabled = false - scheduled = nil - - if wizard["after_time"] - enabled = true - - if !wizard["after_time_scheduled"] && !existing_wizard["after_time_scheduled"] - error = 'after_time_need_time' - else - scheduled = Time.parse(wizard["after_time_scheduled"]).utc - new = false - - if existing_wizard['after_time_scheduled'] - new = scheduled != Time.parse(existing_wizard['after_time_scheduled']).utc - end - - begin - error = 'after_time_invalid' if new && scheduled < Time.now.utc - rescue ArgumentError - error = 'after_time_invalid' - end - end - end - - if error - { error: { type: error } } - else - { - new: new, - scheduled: scheduled, - enabled: enabled - } - end - end - - def build_wizard - wizard = ::JSON.parse(wizard_params) - existing_wizard = PluginStore.get('custom_wizard', wizard['id']) || {} - - validation = validate_wizard(wizard) - return validation if validation[:error] - - after_time = validate_after_time(wizard, existing_wizard) - return after_time if after_time[:error] - - wizard['steps'].each do |step| - if step['raw_description'] - step['description'] = PrettyText.cook(step['raw_description']) - end - end - - result = { - wizard: wizard, - existing_wizard: existing_wizard - } - - if after_time[:enabled] - result[:after_time] = after_time - end - - result - end - - def build_submissions(wizard_id) - rows = PluginStoreRow.where(plugin_name: "#{wizard_id}_submissions").order('id DESC') - - submissions = [*rows].map do |row| - value = ::JSON.parse(row.value) - - if user = User.find_by(id: row.key) - username = user.username - else - username = I18n.t('admin.wizard.submissions.no_user', id: row.key) - end - - value.map do |submission| - { - username: username - }.merge!(submission.except("redirect_to")) - end - end.flatten - end -end diff --git a/controllers/custom_wizard/admin/admin.rb b/controllers/custom_wizard/admin/admin.rb new file mode 100644 index 00000000..2dd87e86 --- /dev/null +++ b/controllers/custom_wizard/admin/admin.rb @@ -0,0 +1,13 @@ +class CustomWizard::AdminController < ::Admin::AdminController + before_action :ensure_admin + + def index + end + + private + + def find_wizard + params.require(:wizard_id) + @wizard = CustomWizard::Wizard.create(params[:wizard_id].underscore) + end +end \ No newline at end of file diff --git a/controllers/custom_wizard/api.rb b/controllers/custom_wizard/admin/api.rb similarity index 96% rename from controllers/custom_wizard/api.rb rename to controllers/custom_wizard/admin/api.rb index a4ed8e6d..1e811061 100644 --- a/controllers/custom_wizard/api.rb +++ b/controllers/custom_wizard/admin/api.rb @@ -1,6 +1,4 @@ -class CustomWizard::ApiController < ::ApplicationController - before_action :ensure_logged_in - before_action :ensure_admin +class CustomWizard::AdminApiController < CustomWizard::AdminController skip_before_action :check_xhr, only: [:redirect] def index diff --git a/controllers/custom_wizard/admin/submissions.rb b/controllers/custom_wizard/admin/submissions.rb new file mode 100644 index 00000000..28dbe553 --- /dev/null +++ b/controllers/custom_wizard/admin/submissions.rb @@ -0,0 +1,51 @@ +class CustomWizard::AdminSubmissionsController < CustomWizard::AdminController + skip_before_action :check_xhr, only: [:download_submissions] + before_action :find_wizard + + def index + render json: ActiveModel::ArraySerializer.new( + CustomWizard::Wizard.list, + each_serializer: CustomWizard::BasicWizardSerializer + ) + end + + def show + result = {} + + if wizard = @wizard + submissions = build_submissions(wizard.id) + result[:wizard] = CustomWizard::BasicWizardSerializer.new(wizard, root: false) + result[:submissions] = submissions.as_json + end + + render_json_dump(result) + end + + def download + send_data build_submissions(@wizard.id).to_json, + filename: "#{Discourse.current_hostname}-wizard-submissions-#{wizard['name']}.json", + content_type: "application/json" + end + + private + + def build_submissions(wizard_id) + rows = PluginStoreRow.where(plugin_name: "#{wizard_id}_submissions").order('id DESC') + + submissions = [*rows].map do |row| + value = ::JSON.parse(row.value) + + if user = User.find_by(id: row.key) + username = user.username + else + username = I18n.t('admin.wizard.submissions.no_user', id: row.key) + end + + value.map do |submission| + { + username: username + }.merge!(submission.except("redirect_to")) + end + end.flatten + end +end \ No newline at end of file diff --git a/controllers/custom_wizard/admin/wizard.rb b/controllers/custom_wizard/admin/wizard.rb new file mode 100644 index 00000000..e090314c --- /dev/null +++ b/controllers/custom_wizard/admin/wizard.rb @@ -0,0 +1,133 @@ +class CustomWizard::AdminWizardController < CustomWizard::AdminController + before_action :find_wizard, only: [:show, :remove] + + def index + render_json_dump( + wizard_list: ActiveModel::ArraySerializer.new( + CustomWizard::Wizard.list, + each_serializer: CustomWizard::BasicWizardSerializer + ), + field_types: CustomWizard::Field.types + ) + end + + def show + params.require(:wizard_id) + + if data = CustomWizard::Wizard.find(params[:wizard_id].underscore) + render json: data.as_json + else + render json: { none: true } + end + end + + def remove + CustomWizard::Wizard.remove(@wizard.id) + render json: success_json + end + + def save + opts = {} + opts[:create] = params[:create] if params[:create] + + validator = CustomWizard::Validator.new(save_wizard_params.to_h, opts) + validation = validator.perform + + if validation[:error] + render json: { error: validation[:error] } + else + params = validation[:wizard] + + if wizard_id = CustomWizard::Wizard.save(params) + render json: success_json.merge(wizard_id: wizard_id) + else + render json: failed_json + end + end + end + + private + + def mapped_params + [ + :type, + :connector, + :output, + :output_type, + :output_connector, + pairs: [ + :index, + :key, + :key_type, + :value, + :value_type, + :connector, + value: [], + key: [], + ], + output: [], + ] + end + + def save_wizard_params + params.require(:wizard).permit( + :id, + :name, + :background, + :save_submissions, + :multiple_submissions, + :after_signup, + :after_time, + :after_time_scheduled, + :required, + :prompt_completion, + :restart_on_revisit, + :theme_id, + permitted: mapped_params, + steps: [ + :id, + :title, + :key, + :banner, + :raw_description, + :required_data_message, + required_data: mapped_params, + permitted_params: mapped_params, + fields: [ + :id, + :label, + :image, + :description, + :required, + :key, + :type, + :min_length, + :file_types, + :limit, + :property, + prefill: mapped_params, + content: mapped_params + ] + ], + actions: [ + :id, + :run_after, + :type, + :code, + :skip_redirect, + :url, + title: mapped_params, + post: mapped_params, + post_builder: mapped_params, + post_template: mapped_params, + category: mapped_params, + tags: mapped_params, + custom_fields: mapped_params, + required: mapped_params, + recipient: mapped_params, + profile_updates: mapped_params, + group: mapped_params + ] + ) + end +end diff --git a/controllers/custom_wizard/steps.rb b/controllers/custom_wizard/steps.rb index a627b960..8707f3ed 100644 --- a/controllers/custom_wizard/steps.rb +++ b/controllers/custom_wizard/steps.rb @@ -12,7 +12,7 @@ class CustomWizard::StepsController < ::ApplicationController permitted.permit! end - wizard = CustomWizard::Builder.new(current_user, permitted[:wizard_id].underscore).build + wizard = CustomWizard::Builder.new(permitted[:wizard_id].underscore, current_user).build updater = wizard.create_updater(permitted[:step_id], permitted[:fields]) updater.update diff --git a/controllers/custom_wizard/transfer.rb b/controllers/custom_wizard/transfer.rb index 4da52bb9..f1bf0bba 100644 --- a/controllers/custom_wizard/transfer.rb +++ b/controllers/custom_wizard/transfer.rb @@ -53,7 +53,7 @@ class CustomWizard::TransferController < ::ApplicationController failed_ids = [] jsonObject.each do |o| - if !CustomWizard::Template.new(o) + if !CustomWizard::Wizard.new(o) failed_ids.push o['id'] next end diff --git a/controllers/custom_wizard/wizard.rb b/controllers/custom_wizard/wizard.rb index ebe6cd55..914d3cea 100644 --- a/controllers/custom_wizard/wizard.rb +++ b/controllers/custom_wizard/wizard.rb @@ -6,7 +6,7 @@ class CustomWizard::WizardController < ::ApplicationController helper_method :theme_ids def wizard - CustomWizard::Template.new(PluginStore.get('custom_wizard', params[:wizard_id].underscore)) + CustomWizard::Wizard.create(params[:wizard_id].underscore, current_user) end def wizard_page_title @@ -20,12 +20,14 @@ class CustomWizard::WizardController < ::ApplicationController def index respond_to do |format| format.json do - builder = CustomWizard::Builder.new(current_user, params[:wizard_id].underscore) - builder_opts = {} - builder_opts[:reset] = params[:reset] || builder.wizard.restart_on_revisit - + builder = CustomWizard::Builder.new(params[:wizard_id].underscore, current_user) + if builder.wizard.present? - render_serialized(builder.build(builder_opts, params), ::CustomWizardSerializer) + builder_opts = {} + builder_opts[:reset] = params[:reset] || builder.wizard.restart_on_revisit + built_wizard = builder.build(builder_opts, params) + + render_serialized(built_wizard, ::CustomWizard::WizardSerializer, root: false) else render json: { error: I18n.t('wizard.none') } end @@ -34,34 +36,28 @@ class CustomWizard::WizardController < ::ApplicationController end end - ## clean up if user skips wizard def skip params.require(:wizard_id) - wizard_id = params[:wizard_id] - - user = current_user - wizard_template = PluginStore.get('custom_wizard', wizard_id.underscore) - wizard = CustomWizard::Wizard.new(user, wizard_template) - if wizard.required && !wizard.completed? && wizard.permitted? return render json: { error: I18n.t('wizard.no_skip') } end result = success_json - + user = current_user + if user - submission = Array.wrap(PluginStore.get("#{wizard_id}_submissions", user.id)).last + submission = wizard.submissions.last if submission && submission['redirect_to'] result.merge!(redirect_to: submission['redirect_to']) end if submission && !wizard.save_submissions - PluginStore.remove("#{wizard_id}_submissions", user.id) + PluginStore.remove("#{wizard.id}_submissions", user.id) end - if user.custom_fields['redirect_to_wizard'] === wizard_id + if user.custom_fields['redirect_to_wizard'] === wizard.id user.custom_fields.delete('redirect_to_wizard') user.save_custom_fields(true) end diff --git a/jobs/set_after_time_wizard.rb b/jobs/set_after_time_wizard.rb index 76d408d4..e94d2d92 100644 --- a/jobs/set_after_time_wizard.rb +++ b/jobs/set_after_time_wizard.rb @@ -5,7 +5,7 @@ module Jobs user_ids = [] User.human_users.each do |user| - if CustomWizard::Wizard.set_wizard_redirect(user, args[:wizard_id]) + if CustomWizard::Wizard.set_wizard_redirect(args[:wizard_id], user) user_ids.push(user.id) end end diff --git a/lib/custom_wizard/builder.rb b/lib/custom_wizard/builder.rb index fa5d8c96..aca414cf 100644 --- a/lib/custom_wizard/builder.rb +++ b/lib/custom_wizard/builder.rb @@ -1,14 +1,14 @@ class CustomWizard::Builder attr_accessor :wizard, :updater, :submissions - def initialize(user=nil, wizard_id) - template = PluginStore.get('custom_wizard', wizard_id) - return if template.blank? - - @steps = template['steps'] - @actions = template['actions'] - @wizard = CustomWizard::Wizard.new(user, template) - @submissions = Array.wrap(PluginStore.get("#{wizard_id}_submissions", user.id)) if user + def initialize(wizard_id, user=nil) + params = CustomWizard::Wizard.find(wizard_id) + return nil if params.blank? + + @wizard = CustomWizard::Wizard.new(params, user) + @steps = params['steps'] || [] + @actions = params['actions'] || [] + @submissions = @wizard.submissions if user && @wizard end def self.sorted_handlers @@ -38,13 +38,8 @@ class CustomWizard::Builder end def build(build_opts = {}, params = {}) - - return @wizard if !SiteSetting.custom_wizard_enabled || - (!@wizard.multiple_submissions && - @wizard.completed? && - !@wizard.user.admin) || - !@steps || - !@wizard.permitted? + return nil if !SiteSetting.custom_wizard_enabled || !@wizard + return @wizard if !@wizard.can_access? reset_submissions if build_opts[:reset] @@ -150,10 +145,10 @@ class CustomWizard::Builder if @actions.present? @actions.each do |action| - + if (action['run_after'] === updater.step.id) || (final_step && (!action['run_after'] || (action['run_after'] === 'wizard_completion'))) - + CustomWizard::Action.new( action: action, user: user, @@ -189,7 +184,7 @@ class CustomWizard::Builder end end end - + @wizard end @@ -213,7 +208,7 @@ class CustomWizard::Builder params[:value] = prefill_field(field_template, step_template) || params[:value] - if field_template['type'] === 'group' + if field_template['type'] === 'group' && params[:value].present? params[:value] = params[:value].first end @@ -262,7 +257,7 @@ class CustomWizard::Builder params[:content] = content[:result] end - + field = step.add_field(params) end diff --git a/lib/custom_wizard/mapper.rb b/lib/custom_wizard/mapper.rb index 43573102..8f2be414 100644 --- a/lib/custom_wizard/mapper.rb +++ b/lib/custom_wizard/mapper.rb @@ -160,6 +160,10 @@ class CustomWizard::Mapper end end + def map_text(value) + interpolate(value) + end + def map_wizard_field(value) data && !data.key?("submitted_at") && data[value] end diff --git a/lib/custom_wizard/template.rb b/lib/custom_wizard/template.rb deleted file mode 100644 index 51536dfa..00000000 --- a/lib/custom_wizard/template.rb +++ /dev/null @@ -1,46 +0,0 @@ -class CustomWizard::Template - - attr_reader :id, - :name, - :background, - :save_submissions, - :multiple_submissions, - :prompt_completion, - :restart_on_revisit, - :after_signup, - :after_time, - :after_time_scheduled, - :required, - :theme_id, - :permitted, - :steps, - :actions - - def initialize(data) - data = data.is_a?(String) ? ::JSON.parse(data) : data - - return nil if data.blank? - - @id = data['id'] - @name = data['name'] - @background = data['background'] - @save_submissions = data['save_submissions'] || false - @multiple_submissions = data['multiple_submissions'] || false - @prompt_completion = data['prompt_completion'] || false - @restart_on_revisit = data['restart_on_revisit'] || false - @after_signup = data['after_signup'] - @after_time = data['after_time'] - @after_time_scheduled = data['after_time_scheduled'] - @required = data['required'] || false - @theme_id = data['theme_id'] - @permitted = data['permitted'] || nil - - if data['theme'] - theme = Theme.find_by(name: data['theme']) - @theme_id = theme.id if theme - end - - @steps = data['steps'] - @actions = data['actions'] - end -end diff --git a/lib/custom_wizard/validator.rb b/lib/custom_wizard/validator.rb new file mode 100644 index 00000000..df9f26ca --- /dev/null +++ b/lib/custom_wizard/validator.rb @@ -0,0 +1,143 @@ +class CustomWizard::Validator + + def initialize(params, opts={}) + @params = params + @opts = opts + @error = nil + end + + def perform + params = @params + + check_id(params, :wizard) + check_required(params, :wizard) + check_depdendent(params, :wizard) + + after_time = nil + + if !@error && @params[:after_time] + after_time = validate_after_time + end + + if !@error + params[:steps].each do |step| + check_required(step, :step) + check_depdendent(step, :step) + break if @error.present? + + if params[:fields].present? + params[:fields].each do |field| + check_required(field, :field) + check_depdendent(field, :field) + break if @error.present? + end + end + end + + if params[:actions].present? + params[:actions].each do |action| + check_required(action, :action) + check_depdendent(action, :action) + break if @error.present? + end + end + end + + if @error + { error: @error } + else + result = { wizard: params } + result[:after_time] = after_time if after_time + result + end + end + + def self.required + { + wizard: ['id', 'name', 'steps'], + step: ['id'], + field: ['id', 'type'], + action: ['id', 'type'] + } + end + + def self.dependent + { + wizard: { + after_time: 'after_time_scheduled' + }, + step: {}, + field: {}, + action: {} + } + end + + private + + def check_required(object, type) + CustomWizard::Validator.required[type].each do |property| + if object[property].blank? + @error = { + type: 'required', + params: { type: type, property: property } + } + end + end + end + + def check_depdendent(object, type) + CustomWizard::Validator.dependent[type].each do |property, dependent| + if object[property] && object[dependent].blank? + @error = { + type: 'dependent', + params: { property: property, dependent: dependent } + } + end + end + end + + def check_id(object, type) + if type === :wizard && @opts[:create] && CustomWizard::Wizard.exists?(object[:id]) + @error = { + type: 'conflict', + params: { type: type, property: 'id', value: object[:id] } + } + end + end + + def validate_after_time + if !@opts[:create] + wizard = CustomWizard::Wizard.create(params) + end + + new = false + error = nil + scheduled = nil + + if !@params[:after_time_scheduled] && !wizard[:after_time_scheduled] + error = 'after_time_need_time' + else + scheduled = Time.parse(@params[:after_time_scheduled]).utc + new = false + + if wizard[:after_time_scheduled] + new = scheduled != Time.parse(wizard[:after_time_scheduled]).utc + end + + begin + error = 'after_time_invalid' if new && scheduled < Time.now.utc + rescue ArgumentError + error = 'after_time_invalid' + end + end + + if error + @error = { type: error } + else + { + new: new, + scheduled: scheduled + } + end + end +end \ No newline at end of file diff --git a/lib/custom_wizard/wizard.rb b/lib/custom_wizard/wizard.rb index 35657494..049786da 100644 --- a/lib/custom_wizard/wizard.rb +++ b/lib/custom_wizard/wizard.rb @@ -8,10 +8,10 @@ UserHistory.actions[:custom_wizard_step] = 1000 class CustomWizard::Wizard include ActiveModel::SerializerSupport - attr_reader :steps, :user attr_accessor :id, :name, :background, + :theme_id, :save_submissions, :multiple_submissions, :after_time, @@ -22,20 +22,38 @@ class CustomWizard::Wizard :restart_on_revisit, :permitted, :needs_categories, - :needs_groups + :needs_groups, + :steps, + :actions, + :user - def initialize(user=nil, attrs = {}) - @steps = [] + def initialize(attrs = {}, user=nil) @user = user - @first_step = nil - @required = false + + @id = attrs['id'] + @name = attrs['name'] + @background = attrs['background'] + @save_submissions = attrs['save_submissions'] || false + @multiple_submissions = attrs['multiple_submissions'] || false + @prompt_completion = attrs['prompt_completion'] || false + @restart_on_revisit = attrs['restart_on_revisit'] || false + @after_signup = attrs['after_signup'] + @after_time = attrs['after_time'] + @after_time_scheduled = attrs['after_time_scheduled'] + @required = attrs['required'] || false + @permitted = attrs['permitted'] || nil @needs_categories = false @needs_groups = false - - attrs.each do |key, value| - setter = "#{key}=" - send(setter, value) if respond_to?(setter.to_sym, false) + @theme_id = attrs['theme_id'] + + if attrs['theme'] + theme = Theme.find_by(name: attrs['theme']) + @theme_id = theme.id if theme end + + @first_step = nil + @steps = [] + @actions = [] end def create_step(step_name) @@ -44,13 +62,13 @@ class CustomWizard::Wizard def append_step(step) step = create_step(step) if step.is_a?(String) - + yield step if block_given? last_step = @steps.last @steps << step - + # If it's the first step if @steps.size == 1 @first_step = step @@ -108,7 +126,7 @@ class CustomWizard::Wizard def completed? return nil if !@user - + steps = CustomWizard::Wizard.step_ids(@id) history = ::UserHistory.where( @@ -151,6 +169,13 @@ class CustomWizard::Wizard end end end + + def can_access? + return true if user.admin + return false if multiple_submissions && completed? + return false if !permitted? + return true + end def reset ::UserHistory.create( @@ -169,6 +194,10 @@ class CustomWizard::Wizard @groups ||= ::Site.new(Guardian.new(@user)).groups end + def submissions + Array.wrap(PluginStore.get("#{id}_submissions", @user.id)) + end + def self.filter_records(filter) PluginStoreRow.where(" plugin_name = 'custom_wizard' AND @@ -183,7 +212,7 @@ class CustomWizard::Wizard records .sort_by { |record| record.value['permitted'].present? ? 0 : 1 } .each do |record| - wizard = CustomWizard::Wizard.new(user, JSON.parse(record.value)) + wizard = CustomWizard::Wizard.new(JSON.parse(record.value), user) if wizard.permitted? result = wizard @@ -200,7 +229,7 @@ class CustomWizard::Wizard def self.prompt_completion(user) if (records = filter_records('prompt_completion')).any? records.reduce([]) do |result, record| - wizard = CustomWizard::Wizard.new(user, ::JSON.parse(record.value)) + wizard = CustomWizard::Wizard.new(::JSON.parse(record.value), user) result.push(id: wizard.id, name: wizard.name) if !wizard.completed? result end @@ -247,21 +276,69 @@ class CustomWizard::Wizard def self.find(wizard_id) PluginStore.get('custom_wizard', wizard_id) end + + def self.list(user=nil) + PluginStoreRow.where(plugin_name: 'custom_wizard').order(:id) + .map { |record| self.new(JSON.parse(record.value), user) } + end + + def self.save(wizard) + existing_wizard = self.create(wizard[:id]) + + wizard[:steps].each do |step| + if step[:raw_description] + step[:description] = PrettyText.cook(step[:raw_description]) + end + end + + wizard = wizard.slice!(:create) + + ActiveRecord::Base.transaction do + PluginStore.set('custom_wizard', wizard[:id], wizard) + + if wizard[:after_time] + Jobs.cancel_scheduled_job(:set_after_time_wizard, wizard_id: wizard[:id]) + Jobs.enqueue_at(wizard[:after_time_scheduled], :set_after_time_wizard, wizard_id: wizard[:id]) + end + + if existing_wizard && existing_wizard.after_time && !wizard[:after_time] + Jobs.cancel_scheduled_job(:set_after_time_wizard, wizard_id: wizard[:id]) + Jobs.enqueue(:clear_after_time_wizard, wizard_id: wizard[:id]) + end + end + + wizard[:id] + end + + def self.remove(wizard_id) + wizard = self.create(wizard_id) + + ActiveRecord::Base.transaction do + if wizard.after_time + Jobs.cancel_scheduled_job(:set_after_time_wizard) + Jobs.enqueue(:clear_after_time_wizard, wizard_id: wizard.id) + end + + PluginStore.remove('custom_wizard', wizard.id) + end + end def self.exists?(wizard_id) PluginStoreRow.exists?(plugin_name: 'custom_wizard', key: wizard_id) end - def self.create(user, wizard_id) - CustomWizard::Wizard.new(user, self.find(wizard_id).to_h) + def self.create(wizard_id, user = nil) + if wizard = self.find(wizard_id) + CustomWizard::Wizard.new(wizard.to_h, user) + end end def self.set_submission_redirect(user, wizard_id, url) PluginStore.set("#{wizard_id.underscore}_submissions", user.id, [{ redirect_to: url }]) end - def self.set_wizard_redirect(user, wizard_id) - wizard = CustomWizard::Wizard.create(user, wizard_id) + def self.set_wizard_redirect(wizard_id, user) + wizard = CustomWizard::Wizard.create(wizard_id, user) if wizard.permitted? user.custom_fields['redirect_to_wizard'] = wizard_id diff --git a/plugin.rb b/plugin.rb index c48ed998..0fc88744 100644 --- a/plugin.rb +++ b/plugin.rb @@ -44,11 +44,13 @@ after_initialize do %w[ ../lib/custom_wizard/engine.rb ../config/routes.rb + ../controllers/custom_wizard/admin/admin.rb + ../controllers/custom_wizard/admin/wizard.rb + ../controllers/custom_wizard/admin/submissions.rb + ../controllers/custom_wizard/admin/api.rb ../controllers/custom_wizard/wizard.rb ../controllers/custom_wizard/steps.rb - ../controllers/custom_wizard/admin.rb ../controllers/custom_wizard/transfer.rb - ../controllers/custom_wizard/api.rb ../controllers/application_controller.rb ../controllers/extra_locales_controller.rb ../controllers/invites_controller.rb @@ -60,7 +62,7 @@ after_initialize do ../lib/custom_wizard/field.rb ../lib/custom_wizard/mapper.rb ../lib/custom_wizard/step_updater.rb - ../lib/custom_wizard/template.rb + ../lib/custom_wizard/validator.rb ../lib/custom_wizard/wizard.rb ../lib/custom_wizard/api/api.rb ../lib/custom_wizard/api/authorization.rb @@ -74,6 +76,7 @@ after_initialize do ../serializers/custom_wizard/api/log_serializer.rb ../serializers/custom_wizard/api_serializer.rb ../serializers/custom_wizard/basic_api_serializer.rb + ../serializers/custom_wizard/basic_wizard_serializer.rb ../serializers/custom_wizard/wizard_field_serializer.rb ../serializers/custom_wizard/wizard_step_serializer.rb ../serializers/custom_wizard/wizard_serializer.rb @@ -94,7 +97,7 @@ after_initialize do if !wizard.completed? custom_redirect = true - CustomWizard::Wizard.set_wizard_redirect(user, wizard.id) + CustomWizard::Wizard.set_wizard_redirect(wizard.id, user) end end @@ -115,7 +118,7 @@ after_initialize do on(:user_approved) do |user| if wizard_id = CustomWizard::Wizard.after_signup - CustomWizard::Wizard.set_wizard_redirect(user, wizard_id) + CustomWizard::Wizard.set_wizard_redirect(wizard_id, user) end end diff --git a/serializers/custom_wizard/basic_wizard_serializer.rb b/serializers/custom_wizard/basic_wizard_serializer.rb new file mode 100644 index 00000000..76c94614 --- /dev/null +++ b/serializers/custom_wizard/basic_wizard_serializer.rb @@ -0,0 +1,3 @@ +class CustomWizard::BasicWizardSerializer < ::ApplicationSerializer + attributes :id, :name +end \ No newline at end of file diff --git a/serializers/custom_wizard/wizard_field_serializer.rb b/serializers/custom_wizard/wizard_field_serializer.rb index 62022c05..c03628db 100644 --- a/serializers/custom_wizard/wizard_field_serializer.rb +++ b/serializers/custom_wizard/wizard_field_serializer.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class CustomWizardFieldSerializer < ::WizardFieldSerializer +class CustomWizard::FieldSerializer < ::WizardFieldSerializer attributes :image, :file_types, diff --git a/serializers/custom_wizard/wizard_serializer.rb b/serializers/custom_wizard/wizard_serializer.rb index afb7a2c7..93596e24 100644 --- a/serializers/custom_wizard/wizard_serializer.rb +++ b/serializers/custom_wizard/wizard_serializer.rb @@ -1,17 +1,17 @@ # frozen_string_literal: true -class CustomWizardSerializer < ::WizardSerializer +class CustomWizard::WizardSerializer < CustomWizard::BasicWizardSerializer - attributes :id, - :name, + attributes :start, :background, + :theme_id, :completed, :required, :permitted, :uncategorized_category_id - has_one :user, serializer: ::BasicUserSerializer, embed: :objects - has_many :steps, serializer: ::CustomWizardStepSerializer, embed: :objects + has_many :steps, serializer: ::CustomWizard::StepSerializer, embed: :objects + has_one :user, serializer: ::BasicUserSerializer, embed: :objects has_many :categories, serializer: ::BasicCategorySerializer, embed: :objects has_many :groups, serializer: ::BasicGroupSerializer, embed: :objects @@ -28,6 +28,10 @@ class CustomWizardSerializer < ::WizardSerializer def permitted object.permitted? end + + def start + object.start.id + end def include_start? object.start && include_steps? diff --git a/serializers/custom_wizard/wizard_step_serializer.rb b/serializers/custom_wizard/wizard_step_serializer.rb index 05f48efe..2cd74393 100644 --- a/serializers/custom_wizard/wizard_step_serializer.rb +++ b/serializers/custom_wizard/wizard_step_serializer.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true -class ::CustomWizardStepSerializer < ::WizardStepSerializer +class CustomWizard::StepSerializer < ::WizardStepSerializer attributes :permitted, :permitted_message - has_many :fields, serializer: ::CustomWizardFieldSerializer, embed: :objects + has_many :fields, serializer: ::CustomWizard::FieldSerializer, embed: :objects def title return PrettyText.cook(object.title) if object.title diff --git a/spec/components/custom_wizard/builder_spec.rb b/spec/components/custom_wizard/builder_spec.rb index cbba78ae..ba8da001 100644 --- a/spec/components/custom_wizard/builder_spec.rb +++ b/spec/components/custom_wizard/builder_spec.rb @@ -17,7 +17,7 @@ describe CustomWizard::Builder do def build_wizard(t = template, u = user, build_opts = {}, params = {}) CustomWizard::Wizard.add_wizard(t) - CustomWizard::Builder.new(u, 'welcome').build(build_opts, params) + CustomWizard::Builder.new('welcome', u).build(build_opts, params) end def add_submission_data(data = {}) diff --git a/spec/serializers/custom_wizard/wizard_serializer_spec.rb b/spec/serializers/custom_wizard/wizard_serializer_spec.rb index eabe26e4..a470bccd 100644 --- a/spec/serializers/custom_wizard/wizard_serializer_spec.rb +++ b/spec/serializers/custom_wizard/wizard_serializer_spec.rb @@ -16,7 +16,7 @@ describe CustomWizardSerializer do def build_wizard(t = template, u = user, build_opts = {}, params = {}) CustomWizard::Wizard.add_wizard(t) - CustomWizard::Builder.new(u, 'welcome').build(build_opts, params) + CustomWizard::Builder.new('welcome', u).build(build_opts, params) end it 'should return the wizard attributes' do diff --git a/views/layouts/wizard.html.erb b/views/layouts/wizard.html.erb index 4fa985f1..47d54309 100644 --- a/views/layouts/wizard.html.erb +++ b/views/layouts/wizard.html.erb @@ -8,7 +8,7 @@ <%= stylesheet_link_tag "wizard_custom_mobile" %> <%= stylesheet_link_tag "wizard_locations"%> <%= stylesheet_link_tag "wizard_events"%> - <%- if theme_ids %> + <%- if theme_ids.present? %> <%= discourse_stylesheet_link_tag (mobile_view? ? :mobile_theme : :desktop_theme) %> <%- end %>