Angus McLeod 7 years ago
parent
commit
7b09410a26

+ 33 - 7
app/controllers/admin.rb

@@ -6,6 +6,10 @@ class CustomWizard::AdminController < ::ApplicationController
     render nothing: true
   end
 
+  def field_types
+    render json: { types: CustomWizard::FieldTypes.all }
+  end
+
   def save
     params.require(:wizard)
 
@@ -37,21 +41,43 @@ class CustomWizard::AdminController < ::ApplicationController
     render json: success_json
   end
 
-  def find
-    params.require(:id)
+  def find_wizard
+    params.require(:wizard_id)
 
-    wizard = PluginStore.get('custom_wizard', params[:id])
+    wizard = PluginStore.get('custom_wizard', params[:wizard_id])
 
     render json: success_json.merge(wizard: wizard)
   end
 
-  def all
+  def custom_wizards
     rows = PluginStoreRow.where(plugin_name: 'custom_wizard').order(:id)
 
-    wizards = rows ? [*rows].map do |r|
-      CustomWizard::Wizard.new(r.value)
-    end : []
+    wizards = [*rows].map { |r| CustomWizard::Wizard.new(r.value) }
 
     render json: success_json.merge(wizards: wizards)
   end
+
+  def find_submissions
+    params.require(:wizard_id)
+
+    wizard = PluginStore.get('custom_wizard_submissions', params[:wizard_id])
+
+    render json: success_json.merge(submissions: submissions)
+  end
+
+  def submissions
+    rows = PluginStoreRow.where(plugin_name: 'custom_wizard_submissions').order(:id)
+
+    all = [*rows].map do |r|
+      wizard = PluginStore.get('custom_wizard', r.key)
+      name = wizard ? wizard['name'] : r.key
+      {
+        id: r.key,
+        name: name,
+        submissions: ::JSON.parse(r.value)
+      }
+    end
+
+    render json: success_json.merge(submissions: all)
+  end
 end

+ 2 - 1
app/controllers/steps.rb

@@ -7,7 +7,8 @@ class CustomWizard::StepsController < ApplicationController
     updater.update
 
     if updater.success?
-      result = { success: 'OK' }
+      result = success_json
+      result.merge!(updater.result) if updater.result
       result[:refresh_required] = true if updater.refresh_required?
       render json: result
     else

+ 9 - 2
assets/javascripts/discourse/components/wizard-custom-action.js.es6

@@ -1,4 +1,11 @@
 export default Ember.Component.extend({
-  targets: ['topic', 'profile', 'email', 'badge', 'save'],
-  isTopic: Ember.computed.equal('targets', 'topic')
+  types: ['create_topic', 'update_profile', 'send_message'],
+  profileFields: ['name', 'username', 'email'],
+  createTopic: Ember.computed.equal('action.type', 'create_topic'),
+  updateProfile: Ember.computed.equal('action.type', 'update_profile'),
+  sendMessage: Ember.computed.equal('action.type', 'send_message'),
+
+  test: function() {
+    console.log(this.get('stepFields'));
+  }.observes('stepFields.[]')
 });

+ 0 - 1
assets/javascripts/discourse/components/wizard-custom-field.js.es6

@@ -2,7 +2,6 @@ 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'),
 
   init() {

+ 4 - 0
assets/javascripts/discourse/components/wizard-custom-step.js.es6

@@ -17,6 +17,10 @@ export default Ember.Component.extend({
 
     removeField(field) {
       this.get('step.fields').removeObject(field);
+    },
+
+    removeAction(action) {
+      this.get('step.actions').removeObject(action);
     }
   }
 });

+ 3 - 0
assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6

@@ -5,6 +5,9 @@ export default {
       this.route('adminWizardsCustom', { path: '/custom', resetNamespace: true }, function() {
         this.route('adminWizard', { path: '/:wizard_id', resetNamespace: true });
       });
+      this.route('adminWizardsSubmissions', { path: '/submissions', resetNamespace: true }, function() {
+        this.route('adminWizardSubmissions', { path: '/:wizard_id', resetNamespace: true });
+      });
     });
   }
 };

+ 26 - 4
assets/javascripts/discourse/models/custom-wizard.js.es6

@@ -29,6 +29,13 @@ const CustomWizard = Discourse.Model.extend({
       const fields = s.get('fields');
       fields.forEach((f) => {
         f.set('id', Ember.String.dasherize(f.get('label')));
+
+        if (f.get('type') === 'dropdown') {
+          const choices = f.get('choices');
+          choices.forEach((c) => {
+            c.set('id', c.get('label'));
+          });
+        }
         step['fields'].push(f);
       });
 
@@ -42,7 +49,8 @@ const CustomWizard = Discourse.Model.extend({
 
     const id = this.get('id');
     const name = this.get('name');
-    let wizard = { id, name, steps };
+    const save_submissions = this.get('save_submissions');
+    let wizard = { id, name, save_submissions, steps };
 
     const existingId = this.get('existingId');
     if (existingId && existingId !== id) {
@@ -76,6 +84,14 @@ CustomWizard.reopenClass({
     });
   },
 
+  findAllSubmissions() {
+    return ajax("/admin/wizards/submissions/all", {
+      type: "GET"
+    }).then(result => {
+      return result.submissions;
+    });
+  },
+
   create(w) {
     const wizard = this._super.apply(this);
 
@@ -83,20 +99,24 @@ CustomWizard.reopenClass({
     let props = { steps };
 
     if (w) {
-      props['id'] = w.id; props['name'] = w.name;
+      props['id'] = w.id;
+      props['name'] = w.name;
 
       if (w.steps) {
         w.steps.forEach((s) => {
           let fields = Ember.A();
 
           s.fields.forEach((f) => {
+            let field = Ember.Object.create(f);
             let choices = Ember.A();
 
             f.choices.forEach((c) => {
               choices.pushObject(Ember.Object.create(c));
             });
 
-            fields.pushObject(Ember.Object.create(f));
+            field.set('choices', choices);
+
+            fields.pushObject(field);
           });
 
           let actions = Ember.A();
@@ -112,7 +132,9 @@ CustomWizard.reopenClass({
             actions
           }));
         });
-      }
+      };
+    } else {
+      props['save_submissions'] = true;
     };
 
     wizard.setProperties(props);

+ 9 - 0
assets/javascripts/discourse/routes/admin-wizard-submissions.js.es6

@@ -0,0 +1,9 @@
+export default Discourse.Route.extend({
+  model(params) {
+    return this.modelFor('admin-wizards-submissions').findBy('id', params.wizard_id);
+  },
+
+  setupController(controller, model) {
+    controller.set("model", model);
+  }
+});

+ 6 - 0
assets/javascripts/discourse/routes/admin-wizard.js.es6

@@ -1,4 +1,5 @@
 import CustomWizard from '../models/custom-wizard';
+import { ajax } from 'discourse/lib/ajax';
 
 export default Discourse.Route.extend({
   model(params) {
@@ -14,6 +15,11 @@ export default Discourse.Route.extend({
     return wizard;
   },
 
+  afterModel(model) {
+    return ajax('/admin/wizards/field-types')
+      .then((result) => model.set('fieldTypes', result.types));
+  },
+
   setupController(controller, model) {
     controller.set("new", this.get('new'));
     controller.set("model", model);

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

@@ -0,0 +1,11 @@
+import CustomWizard from '../models/custom-wizard';
+
+export default Discourse.Route.extend({
+  model() {
+    return CustomWizard.findAllSubmissions();
+  },
+
+  setupController(controller, model){
+    controller.set("model", model);
+  }
+});

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

@@ -0,0 +1,14 @@
+<table>
+  {{#each model.submissions as |s|}}
+    <tr>
+      {{#each-in s as |k v|}}
+        <th>{{k}}</th>
+      {{/each-in}}
+    </tr>
+    <tr>
+      {{#each-in s as |k v|}}
+        <td>{{v}}</td>
+      {{/each-in}}
+    </tr>
+  {{/each}}
+</table>

+ 6 - 1
assets/javascripts/discourse/templates/admin-wizard.hbs

@@ -4,9 +4,14 @@
     {{text-field name="name" value=model.name placeholderKey="admin.wizard.name_placeholder"}}
   </div>
 
+  <div>
+    {{input type='checkbox' checked=model.save_submissions}}
+    <span for="save">{{i18n 'admin.wizard.save_submissions'}}</span>
+  </div>
+
   {{#if model.steps}}
     {{#each model.steps as |s|}}
-      {{wizard-custom-step step=s}}
+      {{wizard-custom-step step=s fieldTypes=model.fieldTypes}}
       {{d-button action='removeStep' actionParam=s label='admin.wizard.step.remove'}}
     {{/each}}
   {{/if}}

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

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

+ 2 - 1
assets/javascripts/discourse/templates/admin-wizards.hbs

@@ -1,5 +1,6 @@
 {{#admin-nav}}
-  {{nav-item route='adminWizardsCustom' label='admin.wizard.label'}}
+  {{nav-item route='adminWizardsCustom' label='admin.wizard.custom_label'}}
+  {{nav-item route='adminWizardsSubmissions' label='admin.wizard.submissions_label'}}
 {{/admin-nav}}
 
 <div class="admin-container">

+ 23 - 14
assets/javascripts/discourse/templates/components/wizard-custom-action.hbs

@@ -1,17 +1,26 @@
-{{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"}}
+{{combo-box value=action.type content=types}}
+{{#if createTopic}}
+  <label>{{i18n "admin.wizard.action.create_topic.category"}}</label>
+  {{category-select-box value=action.category_id}}
+  <label>{{i18n "admin.wizard.action.create_topic.title"}}</label>
+  {{combo-box value=action.title content=stepFields nameProperty="label"}}
+  <label>{{i18n "admin.wizard.action.create_topic.post"}}</label>
+  {{combo-box value=action.post content=stepFields nameProperty="label"}}
 {{/if}}
-{{#if isEmail}}
-  <span>{{i18n 'admin.wizard.action.email'}}</span>
-  {{input type='text' value=action.target.email}}
+{{#if sendMessage}}
+  <label>{{i18n "admin.wizard.action.send_message.title"}}</label>
+  {{combo-box value=action.title content=stepFields nameProperty="label"}}
+  <label>{{i18n "admin.wizard.action.send_message.post"}}</label>
+  {{combo-box value=action.post content=stepFields nameProperty="label"}}
+  <label>{{i18n "admin.wizard.action.send_message.recipient"}}</label>
+  {{user-selector single="true"
+                  includeMentionableGroups="true"
+                  usernames=action.username
+                  allowedUsers="true"}}
 {{/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 updateProfile}}
+  <label>{{i18n "admin.wizard.action.source"}}</label>
+  {{combo-box value=action.source content=stepFields nameProperty="label"}}
+  <label>{{i18n "admin.wizard.action.profile_field"}}</label>
+  {{combo-box value=action.profile_field content=profileFields}}
 {{/if}}

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

@@ -9,7 +9,7 @@
   </div>
   <div>
     <span>{{i18n 'admin.wizard.field.type'}}</span>
-    {{combo-box value=field.type content=fieldTypes}}
+    {{combo-box value=field.type content=types}}
   </div>
   {{#if isDropdown}}
     <span>{{i18n 'admin.wizard.field.choices_label'}}</span>

+ 4 - 3
assets/javascripts/discourse/templates/components/wizard-custom-step.hbs

@@ -14,16 +14,17 @@
 </div>
 
 {{#each step.fields as |f|}}
-  {{wizard-custom-field field=f}}
+  {{wizard-custom-field field=f types=fieldTypes}}
   {{d-button action='removeField' actionParam=f label="admin.wizard.field.remove"}}
 {{/each}}
 
-{{d-button action='addField' label='admin.wizard.step.add_field'}}
+{{d-button action='addField' label='admin.wizard.field.add'}}
 
 {{#each step.actions as |a|}}
   {{wizard-custom-action action=a stepFields=step.fields}}
+  {{d-button action='removeAction' actionParam=a label="admin.wizard.action.remove"}}
 {{/each}}
 
 {{#if allowAddAction}}
-  {{d-button action='addAction' label='admin.wizard.step.add_action'}}
+  {{d-button action='addAction' label='admin.wizard.action.add'}}
 {{/if}}

+ 27 - 3
assets/javascripts/wizard/initializers/custom.js.es6

@@ -1,9 +1,8 @@
-
 export default {
   name: 'custom-routes',
 
-  initialize(container, app) {
-    if (app.get('rootElement') !== '#custom-wizard-main') return;
+  initialize(app) {
+    if (app.constructor.name !== 'Class' || app.get('rootElement') !== '#custom-wizard-main') return;
 
     const WizardApplicationRoute = requirejs('wizard/routes/application').default;
     const findCustomWizard = requirejs('discourse/plugins/discourse-custom-wizard/wizard/models/custom').findCustomWizard;
@@ -11,6 +10,8 @@ export default {
     const ajax = requirejs('wizard/lib/ajax').ajax;
     const StepRoute = requirejs('wizard/routes/step').default;
     const StepModel = requirejs('wizard/models/step').default;
+    const WizardStep = requirejs('wizard/components/wizard-step').default;
+    const getUrl = requirejs('discourse-common/lib/get-url').default;
 
     Router.map(function() {
       this.route('custom', { path: '/custom/:id' }, function() {
@@ -62,5 +63,28 @@ export default {
         return model.set("wizardId", wizard.id);
       }
     });
+
+    WizardStep.reopen({
+      advance() {
+        this.set('saving', true);
+        this.get('step').save()
+          .then(response => {
+            if (this.get('finalStep')) {
+              document.location = getUrl("/");
+            } else {
+              this.sendAction('goNext', response);
+            }
+          })
+          .catch(() => this.animateInvalidFields())
+          .finally(() => this.set('saving', false));
+      },
+
+      actions: {
+        quit() {
+          this.set('finalStep', true);
+          this.send('nextStep');
+        }
+      }
+    });
   }
 };

+ 0 - 2
assets/javascripts/wizard/models/custom.js.es6

@@ -17,8 +17,6 @@ export function findCustomWizard(wizardId) {
       return stepObj;
     });
 
-    console.log(wizard)
-
     return CustomWizard.create(wizard);
   });
 };

+ 22 - 4
config/locales/client.en.yml

@@ -5,8 +5,10 @@ en:
         label: "Wizards"
         new: "New"
         custom_label: "Custom"
+        submissions_label: "Submissions"
         name: "Name"
         name_placeholder: "name of the wizard"
+        save_submissions: "Save wizard submissions"
         save: "Save Wizard"
         remove: "Delete Wizard"
         step:
@@ -18,15 +20,31 @@ en:
           description_placeholder: "This will appear underneath the title and / or title"
           add: "Add Step"
           remove: "Remove Step"
-          add_field: "Add Field"
-          add_action: "Add Action"
         field:
+          add: "Add Field"
+          remove: "Remove Field"
           label: "Field Name"
           description: "Field Description"
           type: "Field Type"
           choices_label: "Dropdown Choices"
           add_choice: "Add Choice"
           required: "Field Required"
-          remove: "Remove Field"
         action:
-          email: "Email"
+          add: "Add Action"
+          remove: "Remove Action"
+          source: "Source"
+          send_message:
+            label: "Send Message"
+            title: "Message Title"
+            post: "Message Post"
+            recipient: "Message Recipient"
+          create_topic:
+            label: "Create Topic"
+            title: "Topic Title"
+            post: "Topic Post"
+            category: "Topic Category"
+          update_profile:
+            label: "Update Profile"
+            field: "Profile Field"
+          save_input:
+            label: "Save Input"

+ 1 - 1
config/locales/server.en.yml

@@ -1,3 +1,3 @@
 en:
-  wizard:
+  custom_wizard:
     title: "Wizard"

+ 82 - 2
lib/builder.rb

@@ -1,9 +1,24 @@
 class CustomWizard::Builder
+
   def initialize(user, wizard_id)
     data = PluginStore.get('custom_wizard', wizard_id)
     @custom_wizard = CustomWizard::Wizard.new(data)
     @wizard = Wizard.new(user)
     @wizard.id = wizard_id
+    @wizard.save_submissions = data['save_submissions']
+  end
+
+  def self.sorted_handlers
+    @sorted_handlers ||= []
+  end
+
+  def self.step_handlers
+    sorted_handlers.map { |h| { wizard_id: h[:wizard_id], block: h[:block] } }
+  end
+
+  def self.add_step_handler(priority = 0, wizard_id, &block)
+    sorted_handlers << { priority: priority, wizard_id: wizard_id, block: block }
+    @sorted_handlers.sort_by! { |h| -h[:priority] }
   end
 
   def build
@@ -27,8 +42,73 @@ class CustomWizard::Builder
         end
 
         step.on_update do |updater|
-          puts "UPDATER: #{updater}"
-          ## do stuff
+
+          @updater = updater
+          input = updater.fields
+          user = @wizard.user
+
+          if @wizard.save_submissions
+            store_key = @wizard.id
+            submissions = Array.wrap(PluginStore.get("custom_wizard_submissions", store_key))
+            submission = {}
+
+            if submissions.last && submissions.last['completed'] === false
+              submission = submissions.last
+              submissions.pop(1)
+            end
+
+            submission['user_id'] = @wizard.user.id
+            submission['completed'] = updater.step.next.nil?
+
+            input.each do |key, value|
+              submission[key] = value
+            end
+
+            submissions.push(submission)
+
+            PluginStore.set('custom_wizard_submissions', store_key, submissions)
+          end
+
+          if s['actions'] && s['actions'].length
+            s['actions'].each do |a|
+              if a['type'] === 'create_topic'
+                creator = PostCreator.new(user,
+                                title: input[a['title']],
+                                raw: input[a['post']],
+                                category: a['category_id'],
+                                skip_validations: true)
+
+                post = creator.create
+                if creator.errors.present?
+                  raise StandardError, creator.errors.full_messages.join(" ")
+                end
+
+                updater.result = { topic_id: post.topic.id }
+              end
+
+              if a['type'] === 'send_message'
+                creator = PostCreator.new(user,
+                                title: input[a['title']],
+                                raw: input[a['post']],
+                                archetype: Archetype.private_message,
+                                target_usernames: a['username'])
+
+                post = creator.create
+
+                if creator.errors.present?
+                  raise StandardError, creator.errors.full_messages.join(" ")
+                end
+
+                updater.result = { topic_id: post.topic.id }
+              end
+            end
+          end
+
+          CustomWizard::Builder.step_handlers.each do |handler|
+            if handler[:wizard_id] == @wizard.id
+              handler[:block].call(self)
+            end
+          end
         end
       end
     end

+ 25 - 6
plugin.rb

@@ -1,5 +1,5 @@
 # name: discourse-custom-wizard
-# about: Allows the admins to create custom user input forms
+# about: Create custom wizards
 # version: 0.1
 # authors: Angus McLeod
 
@@ -37,24 +37,44 @@ after_initialize do
     end
 
     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#all'
-      get 'admin/wizards/custom/:wizard_id' => 'admin#find'
+      get 'admin/wizards/custom/all' => 'admin#custom_wizards'
+      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/all' => 'admin#submissions'
+      get 'admin/wizards/submissions/:wizard_id' => 'admin#find_submissions'
+    end
+  end
+
+  class CustomWizard::FieldTypes
+    def self.all
+      @types ||= ['dropdown', 'image', 'radio', 'text', 'textarea']
+    end
+
+    def self.add(type)
+      all.push(*type)
     end
   end
 
   class ::Wizard
-    attr_accessor :id
+    attr_accessor :id, :save_submissions
   end
 
   class ::Wizard::Step
     attr_accessor :title
   end
 
-  ::Wizard::Field.class_eval do
+  class ::Wizard::StepUpdater
+    attr_accessor :result, :step
+  end
+
+  require_dependency 'wizard/field'
+  Wizard::Field.class_eval do
     attr_reader :label, :description
 
     def initialize(attrs)
@@ -84,7 +104,6 @@ after_initialize do
 
   ::WizardFieldSerializer.class_eval do
     def label
-      puts "LABEL: #{object.label}"
       if object.label
         object.label
       else