Browse Source

Init unfinshed

Angus McLeod 7 years ago
commit
ebd026887c
25 changed files with 495 additions and 0 deletions
  1. 11 0
      assets/javascripts/discourse/components/wizard-custom-action.js.es6
  2. 21 0
      assets/javascripts/discourse/components/wizard-custom-field.js.es6
  3. 26 0
      assets/javascripts/discourse/components/wizard-custom-step.js.es6
  4. 1 0
      assets/javascripts/discourse/connectors/admin-menu/wizards-nav-button.hbs
  5. 26 0
      assets/javascripts/discourse/controllers/admin-wizard.js.es6
  6. 10 0
      assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6
  7. 6 0
      assets/javascripts/discourse/initializers/wizard-edits.js.es6
  8. 41 0
      assets/javascripts/discourse/models/custom-wizard.js.es6
  9. 23 0
      assets/javascripts/discourse/routes/admin-wizard.js.es6
  10. 19 0
      assets/javascripts/discourse/routes/admin-wizards-custom.js.es6
  11. 5 0
      assets/javascripts/discourse/routes/admin-wizards.js.es6
  12. 24 0
      assets/javascripts/discourse/templates/admin-wizard.hbs
  13. 7 0
      assets/javascripts/discourse/templates/admin-wizards-custom-index.hbs
  14. 15 0
      assets/javascripts/discourse/templates/admin-wizards-custom.hbs
  15. 7 0
      assets/javascripts/discourse/templates/admin-wizards.hbs
  16. 17 0
      assets/javascripts/discourse/templates/components/wizard-custom-action.hbs
  17. 25 0
      assets/javascripts/discourse/templates/components/wizard-custom-field.hbs
  18. 30 0
      assets/javascripts/discourse/templates/components/wizard-custom-step.hbs
  19. 3 0
      assets/stylesheets/custom-wizard.scss
  20. 31 0
      config/locales/client.en.yml
  21. 53 0
      controllers/admin.rb
  22. 11 0
      controllers/steps.rb
  23. 35 0
      lib/builder.rb
  24. 10 0
      lib/wizard.rb
  25. 38 0
      plugin.rb

+ 11 - 0
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)
+  },
+});

+ 21 - 0
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() {
+
+    }
+  }
+});

+ 26 - 0
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'));
+    }
+  }
+});

+ 1 - 0
assets/javascripts/discourse/connectors/admin-menu/wizards-nav-button.hbs

@@ -0,0 +1 @@
+{{nav-item route='adminWizards' label='admin.wizard.label'}}

+ 26 - 0
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);
+    }
+  }
+});

+ 10 - 0
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 });
+      });
+    });
+  }
+};

+ 6 - 0
assets/javascripts/discourse/initializers/wizard-edits.js.es6

@@ -0,0 +1,6 @@
+export default {
+  name: 'wizard-edits',
+  initialize() {
+
+  }
+};

+ 41 - 0
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;

+ 23 - 0
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);
+  }
+});

+ 19 - 0
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();
+      }
+    }
+  }
+});

+ 5 - 0
assets/javascripts/discourse/routes/admin-wizards.js.es6

@@ -0,0 +1,5 @@
+export default Discourse.Route.extend({
+  redirect() {
+    this.transitionTo('adminWizardsCustom');
+  }
+});

+ 24 - 0
assets/javascripts/discourse/templates/admin-wizard.hbs

@@ -0,0 +1,24 @@
+<div class="form-horizontal">
+
+  <div>
+    <label for="name">{{i18n 'admin.wizard.name'}}</label>
+    {{text-field name="name" value=model.name placeholderKey="admin.wizard.name_placeholder"}}
+  </div>
+
+  {{#if model.steps}}
+    {{#each model.steps as |s|}}
+      {{wizard-custom-step step=s}}
+    {{/each}}
+  {{/if}}
+
+  {{d-button action='addStep' label='admin.wizard.add_step'}}
+
+  <div class='buttons'>
+    <button {{action "save"}} disabled={{disableSave}} class='btn btn-primary'>{{i18n 'admin.wizard.save'}}</button>
+    {{#unless new}}
+      <button {{action "remove"}} class='btn btn-danger'>{{d-icon "trash-o"}}{{i18n 'admin.wizard.remove'}}</button>
+    {{/unless}}
+    <span class="saving {{unless savingStatus 'hidden'}}">{{savingStatus}}</span>
+  </div>
+
+</div>

+ 7 - 0
assets/javascripts/discourse/templates/admin-wizards-custom-index.hbs

@@ -0,0 +1,7 @@
+<div class="groups-type-index">
+  <div>
+    {{#link-to 'adminWizard' 'new' class="btn"}}
+      {{d-icon "plus"}} {{i18n 'admin.wizard.new'}}
+    {{/link-to}}
+  </div>
+</div>

+ 15 - 0
assets/javascripts/discourse/templates/admin-wizards-custom.hbs

@@ -0,0 +1,15 @@
+<div class='row'>
+  <div class='content-list span6'>
+    <ul>
+      {{#each model as |w|}}
+        <li>
+          {{#link-to "adminWizard" w.name}}{{w.name}}{{/link-to}}
+        </li>
+      {{/each}}
+    </ul>
+  </div>
+
+  <div class="span13">
+    {{outlet}}
+  </div>
+</div>

+ 7 - 0
assets/javascripts/discourse/templates/admin-wizards.hbs

@@ -0,0 +1,7 @@
+{{#admin-nav}}
+  {{nav-item route='adminWizardsCustom' label='admin.wizard.label'}}
+{{/admin-nav}}
+
+<div class="admin-container">
+  {{outlet}}
+</div>

+ 17 - 0
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}}
+  <span>{{i18n 'admin.wizard.action.email'}}</span>
+  {{input type='text' value=action.target.email}}
+{{/if}}
+{{#if isProfile}}
+  <span>{{i18n 'admin.wizard.action.email'}}</span>
+  {{combo-box value=action.target.profile_field content=profileFields}}
+{{/if}}
+{{#if isBadge}}
+  <span>{{i18n 'admin.wizard.action.badge'}}</span>
+  {{combo-box value=action.target.badge content=badges}}
+{{/if}}

+ 25 - 0
assets/javascripts/discourse/templates/components/wizard-custom-field.hbs

@@ -0,0 +1,25 @@
+<div for={{field.id}}>
+  <div>
+    <span>{{i18n 'admin.wizard.field.label'}}</span>
+    {{text-field name="label" value=field.label}}
+  </div>
+  <div>
+    <span>{{i18n 'admin.wizard.field.description'}}</span>
+    {{text-field name="description" value=field.description}}
+  </div>
+  <div>
+    <span>{{i18n 'admin.wizard.field.type'}}</span>
+    {{combo-box value=field.type content=fieldTypes}}
+  </div>
+  {{#if isDropdown}}
+    <span>{{i18n 'admin.wizard.field.choices_label'}}</span>
+    {{#each field.choices as |c|}}
+      {{input type='text' value=c}}
+    {{/each}}
+    {{d-button action='addChoice' label='admin.wizard.field.add_choice'}}
+  {{/if}}
+  <div>
+    <span>{{i18n 'admin.wizard.field.required'}}</span>
+    {{input type='checkbox' checked=field.required}}
+  </div>
+</div>

+ 30 - 0
assets/javascripts/discourse/templates/components/wizard-custom-step.hbs

@@ -0,0 +1,30 @@
+<div>
+  <label for="title">{{i18n 'admin.wizard.step.title'}}</label>
+  {{text-field name="title" value=step.title placeholderKey="admin.wizard.step.title_placeholder"}}
+</div>
+
+<div>
+  <label for="banner">{{i18n 'admin.wizard.step.banner'}}</label>
+  {{input name="banner" value=step.banner placeholderKey="admin.wizard.step.banner_placeholder"}}
+</div>
+
+<div>
+  <label for="description">{{i18n 'admin.wizard.step.description'}}</label>
+  {{input name="description" value=step.description placeholderKey="admin.wizard.step.description_placeholder"}}
+</div>
+
+{{#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'}}

+ 3 - 0
assets/stylesheets/custom-wizard.scss

@@ -0,0 +1,3 @@
+.wizards-nav-button {
+  @extend .nav-pills;
+}

+ 31 - 0
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"

+ 53 - 0
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

+ 11 - 0
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

+ 35 - 0
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

+ 10 - 0
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

+ 38 - 0
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