commit ebd026887c5d277316033a4e9e5dcc01531d8724 Author: Angus McLeod Date: Sat Sep 23 10:34:07 2017 +0800 Init unfinshed diff --git a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 new file mode 100644 index 00000000..ac8b8526 --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 @@ -0,0 +1,11 @@ +import { default as computed } from 'ember-addons/ember-computed-decorators'; + +export default Ember.Component.extend({ + targets: ['topic', 'profile', 'email', 'badge', 'save'], + isTopic: Ember.computed.equal('targets', 'topic'), + + init() { + this._super(...arguments); + console.log(this) + }, +}); diff --git a/assets/javascripts/discourse/components/wizard-custom-field.js.es6 b/assets/javascripts/discourse/components/wizard-custom-field.js.es6 new file mode 100644 index 00000000..ba09166f --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-custom-field.js.es6 @@ -0,0 +1,21 @@ +import { observes } from 'ember-addons/ember-computed-decorators'; + +export default Ember.Component.extend({ + classNames: 'wizard-custom-field', + fieldTypes: ['dropdown', 'image', 'radio', 'text', 'textarea'], + isDropdown: Ember.computed.equal('field.type', 'dropdown'), + choices: Ember.A(), + + @observes('field.label') + setFieldId() { + const label = this.get('field.label'); + console.log('setting id') + this.set('field.id', Ember.String.underscore(label)); + }, + + actions: { + addChoice() { + + } + } +}); diff --git a/assets/javascripts/discourse/components/wizard-custom-step.js.es6 b/assets/javascripts/discourse/components/wizard-custom-step.js.es6 new file mode 100644 index 00000000..4aede4eb --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-custom-step.js.es6 @@ -0,0 +1,26 @@ +import { default as computed } from 'ember-addons/ember-computed-decorators'; + +export default Ember.Component.extend({ + classNames: 'wizard-custom-step', + + @computed('step.fields.@each.id') + allowAddAction(stepFields) { + console.log(stepFields) + return stepFields.get('firstObject.id'); + }, + + actions: { + addField() { + console.log('adding field') + this.get('step.fields').pushObject(Ember.Object.create()); + }, + + addAction() { + this.get('step.actions').pushObject(Ember.Object.create()); + }, + + removeStep() { + this.sendAction('removeStep', this.get('step.name')); + } + } +}); diff --git a/assets/javascripts/discourse/connectors/admin-menu/wizards-nav-button.hbs b/assets/javascripts/discourse/connectors/admin-menu/wizards-nav-button.hbs new file mode 100644 index 00000000..430a1b27 --- /dev/null +++ b/assets/javascripts/discourse/connectors/admin-menu/wizards-nav-button.hbs @@ -0,0 +1 @@ +{{nav-item route='adminWizards' label='admin.wizard.label'}} diff --git a/assets/javascripts/discourse/controllers/admin-wizard.js.es6 b/assets/javascripts/discourse/controllers/admin-wizard.js.es6 new file mode 100644 index 00000000..615d5dd1 --- /dev/null +++ b/assets/javascripts/discourse/controllers/admin-wizard.js.es6 @@ -0,0 +1,26 @@ +export default Ember.Controller.extend({ + actions: { + save() { + this.get('model').save().then(() => { + this.transitionToRoute('adminWizardsCustom'); + }); + }, + + remove() { + this.get('model').destroy().then(() => { + this.transitionToRoute('adminWizardsCustom'); + }); + }, + + addStep() { + this.get('model.steps').pushObject({ + fields: Ember.A(), + actions: Ember.A() + }); + }, + + removeStep(name) { + this.get('model.steps').findBy('name', name); + } + } +}); diff --git a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 new file mode 100644 index 00000000..26a3091d --- /dev/null +++ b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 @@ -0,0 +1,10 @@ +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: '/:name', resetNamespace: true }); + }); + }); + } +}; diff --git a/assets/javascripts/discourse/initializers/wizard-edits.js.es6 b/assets/javascripts/discourse/initializers/wizard-edits.js.es6 new file mode 100644 index 00000000..5781e98c --- /dev/null +++ b/assets/javascripts/discourse/initializers/wizard-edits.js.es6 @@ -0,0 +1,6 @@ +export default { + name: 'wizard-edits', + initialize() { + + } +}; diff --git a/assets/javascripts/discourse/models/custom-wizard.js.es6 b/assets/javascripts/discourse/models/custom-wizard.js.es6 new file mode 100644 index 00000000..ebb03a11 --- /dev/null +++ b/assets/javascripts/discourse/models/custom-wizard.js.es6 @@ -0,0 +1,41 @@ +import { ajax } from 'discourse/lib/ajax'; + +const CustomWizard = Discourse.Model.extend({ + steps: Ember.A(), + + save() { + const steps = JSON.stringify(this.get('steps').toArray()); + return ajax(`/admin/wizards/custom/${this.get('name')}`, { + type: 'PUT', + data: { steps } + }); + }, + + destroy() { + return ajax(`/admin/wizards/custom/${this.get('name')}`, { + type: 'DELETE' + }); + } +}); + +CustomWizard.reopenClass({ + findAll() { + return ajax("/admin/wizards/custom/all").then(result => { + return result.wizards.map(w => CustomWizard.create(w)); + }); + }, + + create() { + const wizard = this._super.apply(this, arguments); + const steps = wizard.get('steps'); + + steps.forEach((s) => { + s.fields = Ember.A(s.fields); + s.actions = Ember.A(s.actions); + }); + + return wizard; + } +}); + +export default CustomWizard; diff --git a/assets/javascripts/discourse/routes/admin-wizard.js.es6 b/assets/javascripts/discourse/routes/admin-wizard.js.es6 new file mode 100644 index 00000000..6dc2de60 --- /dev/null +++ b/assets/javascripts/discourse/routes/admin-wizard.js.es6 @@ -0,0 +1,23 @@ +import CustomWizard from '../models/custom-wizard'; + +export default Discourse.Route.extend({ + model(params) { + if (params.name === 'new') { + this.set('new', true); + return CustomWizard.create(); + } + + this.set('new', false); + + const wizard = this.modelFor('admin-wizards-custom').findBy('name', params.name ); + + if (!wizard) { return this.transitionTo('adminWizardsCustom.index'); } + + return wizard; + }, + + setupController(controller, model) { + controller.set("new", this.get('new')); + controller.set("model", model); + } +}); diff --git a/assets/javascripts/discourse/routes/admin-wizards-custom.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-custom.js.es6 new file mode 100644 index 00000000..517375ba --- /dev/null +++ b/assets/javascripts/discourse/routes/admin-wizards-custom.js.es6 @@ -0,0 +1,19 @@ +import CustomWizard from '../models/custom-wizard'; + +export default Discourse.Route.extend({ + model() { + return CustomWizard.findAll(); + }, + + setupController(controller, model){ + controller.set("model", model.toArray()); + }, + + actions: { + willTransition(transition) { + if (transition.intent.name === 'adminWizardsCustom') { + this.refresh(); + } + } + } +}); 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..149e51be --- /dev/null +++ b/assets/javascripts/discourse/routes/admin-wizards.js.es6 @@ -0,0 +1,5 @@ +export default Discourse.Route.extend({ + redirect() { + this.transitionTo('adminWizardsCustom'); + } +}); diff --git a/assets/javascripts/discourse/templates/admin-wizard.hbs b/assets/javascripts/discourse/templates/admin-wizard.hbs new file mode 100644 index 00000000..8fb77453 --- /dev/null +++ b/assets/javascripts/discourse/templates/admin-wizard.hbs @@ -0,0 +1,24 @@ +
+ +
+ + {{text-field name="name" value=model.name placeholderKey="admin.wizard.name_placeholder"}} +
+ + {{#if model.steps}} + {{#each model.steps as |s|}} + {{wizard-custom-step step=s}} + {{/each}} + {{/if}} + + {{d-button action='addStep' label='admin.wizard.add_step'}} + +
+ + {{#unless new}} + + {{/unless}} + {{savingStatus}} +
+ +
diff --git a/assets/javascripts/discourse/templates/admin-wizards-custom-index.hbs b/assets/javascripts/discourse/templates/admin-wizards-custom-index.hbs new file mode 100644 index 00000000..5ee1e574 --- /dev/null +++ b/assets/javascripts/discourse/templates/admin-wizards-custom-index.hbs @@ -0,0 +1,7 @@ +
+
+ {{#link-to 'adminWizard' 'new' class="btn"}} + {{d-icon "plus"}} {{i18n 'admin.wizard.new'}} + {{/link-to}} +
+
diff --git a/assets/javascripts/discourse/templates/admin-wizards-custom.hbs b/assets/javascripts/discourse/templates/admin-wizards-custom.hbs new file mode 100644 index 00000000..bcbdce91 --- /dev/null +++ b/assets/javascripts/discourse/templates/admin-wizards-custom.hbs @@ -0,0 +1,15 @@ +
+
+
    + {{#each model as |w|}} +
  • + {{#link-to "adminWizard" w.name}}{{w.name}}{{/link-to}} +
  • + {{/each}} +
+
+ +
+ {{outlet}} +
+
diff --git a/assets/javascripts/discourse/templates/admin-wizards.hbs b/assets/javascripts/discourse/templates/admin-wizards.hbs new file mode 100644 index 00000000..749c490c --- /dev/null +++ b/assets/javascripts/discourse/templates/admin-wizards.hbs @@ -0,0 +1,7 @@ +{{#admin-nav}} + {{nav-item route='adminWizardsCustom' label='admin.wizard.label'}} +{{/admin-nav}} + +
+ {{outlet}} +
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs new file mode 100644 index 00000000..ab7ea9e4 --- /dev/null +++ b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs @@ -0,0 +1,17 @@ +{{combo-box value=action.data content=stepFields valueAttribute='id' nameProperty='label'}} +{{combo-box value=action.target content=targets}} +{{#if isTopic}} + {{category-select-box value=action.target.category_id tabindex="3"}} +{{/if}} +{{#if isEmail}} + {{i18n 'admin.wizard.action.email'}} + {{input type='text' value=action.target.email}} +{{/if}} +{{#if isProfile}} + {{i18n 'admin.wizard.action.email'}} + {{combo-box value=action.target.profile_field content=profileFields}} +{{/if}} +{{#if isBadge}} + {{i18n 'admin.wizard.action.badge'}} + {{combo-box value=action.target.badge content=badges}} +{{/if}} diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs new file mode 100644 index 00000000..82dbe4cb --- /dev/null +++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs @@ -0,0 +1,25 @@ +
+
+ {{i18n 'admin.wizard.field.label'}} + {{text-field name="label" value=field.label}} +
+
+ {{i18n 'admin.wizard.field.description'}} + {{text-field name="description" value=field.description}} +
+
+ {{i18n 'admin.wizard.field.type'}} + {{combo-box value=field.type content=fieldTypes}} +
+ {{#if isDropdown}} + {{i18n 'admin.wizard.field.choices_label'}} + {{#each field.choices as |c|}} + {{input type='text' value=c}} + {{/each}} + {{d-button action='addChoice' label='admin.wizard.field.add_choice'}} + {{/if}} +
+ {{i18n 'admin.wizard.field.required'}} + {{input type='checkbox' checked=field.required}} +
+
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs new file mode 100644 index 00000000..b74b1488 --- /dev/null +++ b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs @@ -0,0 +1,30 @@ +
+ + {{text-field name="title" value=step.title placeholderKey="admin.wizard.step.title_placeholder"}} +
+ +
+ + {{input name="banner" value=step.banner placeholderKey="admin.wizard.step.banner_placeholder"}} +
+ +
+ + {{input name="description" value=step.description placeholderKey="admin.wizard.step.description_placeholder"}} +
+ +{{#each step.fields as |f|}} + {{wizard-custom-field field=f}} +{{/each}} + +{{d-button action='addField' label='admin.wizard.step.add_field'}} + +{{#each step.actions as |a|}} + {{wizard-custom-action action=a stepFields=step.fields}} +{{/each}} + +{{#if allowAddAction}} + {{d-button action='addAction' label='admin.wizard.step.add_action'}} +{{/if}} + +{{d-button action='removeStep' label='admin.wizard.remove_step'}} diff --git a/assets/stylesheets/custom-wizard.scss b/assets/stylesheets/custom-wizard.scss new file mode 100644 index 00000000..47b1d901 --- /dev/null +++ b/assets/stylesheets/custom-wizard.scss @@ -0,0 +1,3 @@ +.wizards-nav-button { + @extend .nav-pills; +} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml new file mode 100644 index 00000000..d6114f14 --- /dev/null +++ b/config/locales/client.en.yml @@ -0,0 +1,31 @@ +en: + admin_js: + admin: + wizard: + label: "Wizards" + new: "New" + custom_label: "Custom" + name: "Name" + name_placeholder: "name of the wizard" + add_step: "Add Step" + remove_step: "Remove Step" + save: "Save Wizard" + remove: "Delete Wizard" + step: + title: "Step Title" + title_placeholder: "This will appear at the top of the step" + banner: "Step Banner" + banner_placeholder: "This image will appear under the title" + description: "Step Description" + description_placeholder: "This will appear underneath the title and / or title" + add_field: "Add Field" + add_action: "Add Action" + field: + label: "Field Name" + description: "Field Description" + type: "Field Type" + choices_label: "Dropdown Choices" + add_choice: "Add Choice" + required: "Field Required" + action: + email: "Email" diff --git a/controllers/admin.rb b/controllers/admin.rb new file mode 100644 index 00000000..1c0df935 --- /dev/null +++ b/controllers/admin.rb @@ -0,0 +1,53 @@ +class CustomWizard::AdminController < ::ApplicationController + before_filter :ensure_logged_in + before_filter :ensure_admin + + def index + render nothing: true + end + + def save + params.require(:name) + params.permit(:steps) + + wizard = { name: params[:name] } + + wizard['steps'] = params[:steps] if params[:steps] + + key = params[:name].downcase + + PluginStore.set('custom_wizards', key, wizard) + + render json: success_json + end + + def remove + params.require(:name) + + key = params[:name].downcase + + PluginStore.remove('custom_wizards', key) + + render json: success_json + end + + def find + params.require(:name) + + key = params[:name].downcase + + wizard = PluginStore.get('custom_wizards', key) + + render json: success_json.merge(wizard: wizard) + end + + def all + rows = PluginStoreRow.where(plugin_name: 'custom_wizards') + + wizards = rows ? [*rows].map do |r| + CustomWizard::Wizard.new(r.value) + end : [] + + render json: success_json.merge(wizards: wizards) + end +end diff --git a/controllers/steps.rb b/controllers/steps.rb new file mode 100644 index 00000000..2c73901d --- /dev/null +++ b/controllers/steps.rb @@ -0,0 +1,11 @@ +class CustomWizard::StepsController < ::ApplicationController + def all + respond_to do |format| + format.json do + wizard = CustomWizard::Builder.new(current_user, params[:wizard_id]).build + render_serialized(wizard, WizardSerializer) + end + format.html {} + end + end +end diff --git a/lib/builder.rb b/lib/builder.rb new file mode 100644 index 00000000..13b75f3a --- /dev/null +++ b/lib/builder.rb @@ -0,0 +1,35 @@ +class CustomWizard::Builder + def initialize(user, wizard_id) + @wizard = Wizard.new(user) + @template = PluginStore.get('custom_wizard', wizard_id) + end + + def build + @template.each do |s| + @wizard.append_step(s.title) do |step| + + step.banner = s.banner if s.banner + + s.fields.each do |f| + field = step.add_field(id: f.id, + type: f.type, + required: f.required, + value: f.value) + + if f.type == 'dropdown' + f.choices.each do |c| + field.add_choice(c) + end + end + end + + step.on_update do |updater| + puts "UPDATER: #{updater}" + ## do stuff + end + end + end + + @wizard + end +end diff --git a/lib/wizard.rb b/lib/wizard.rb new file mode 100644 index 00000000..2ed526d1 --- /dev/null +++ b/lib/wizard.rb @@ -0,0 +1,10 @@ +class CustomWizard::Wizard + + attr_reader :name, :steps + + def initialize(data) + parsed = ::JSON.parse(data) + @name = parsed['name'] + @steps = JSON.parse(parsed['steps']) + end +end diff --git a/plugin.rb b/plugin.rb new file mode 100644 index 00000000..66b9a287 --- /dev/null +++ b/plugin.rb @@ -0,0 +1,38 @@ +# name: discourse-custom-wizard +# about: Allows the admins to create custom user input forms +# version: 0.1 +# authors: Angus McLeod + +register_asset 'stylesheets/custom-wizard.scss' + +after_initialize do + require_dependency "application_controller" + module ::CustomWizard + class Engine < ::Rails::Engine + engine_name "custom_wizard" + isolate_namespace CustomWizard + end + end + + CustomWizard::Engine.routes.draw do + get 'custom' => 'admin#index' + get 'custom/new' => 'admin#index' + get 'custom/all' => "admin#all" + get 'custom/:name' => "admin#find" + put 'custom/:name' => "admin#save" + delete 'custom/:name' => "admin#remove" + end + + require_dependency 'admin_constraint' + Discourse::Application.routes.append do + + namespace :admin, constraints: AdminConstraint.new do + mount ::CustomWizard::Engine, at: 'wizards' + end + end + + load File.expand_path('../lib/builder.rb', __FILE__) + load File.expand_path('../lib/wizard.rb', __FILE__) + load File.expand_path('../controllers/steps.rb', __FILE__) + load File.expand_path('../controllers/admin.rb', __FILE__) +end