0
0
Fork 1
Spiegel von https://github.com/paviliondev/discourse-custom-wizard.git synchronisiert 2024-11-25 02:30:28 +01:00
Dieser Commit ist enthalten in:
Angus McLeod 2017-10-05 08:36:46 +08:00
Ursprung f8848d2b93
Commit 7b09410a26
23 geänderte Dateien mit 325 neuen und 53 gelöschten Zeilen

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -1,4 +1,11 @@
export default Ember.Component.extend({ export default Ember.Component.extend({
targets: ['topic', 'profile', 'email', 'badge', 'save'], types: ['create_topic', 'update_profile', 'send_message'],
isTopic: Ember.computed.equal('targets', 'topic') 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.[]')
}); });

Datei anzeigen

@ -2,7 +2,6 @@ import { observes } from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({ export default Ember.Component.extend({
classNames: 'wizard-custom-field', classNames: 'wizard-custom-field',
fieldTypes: ['dropdown', 'image', 'radio', 'text', 'textarea'],
isDropdown: Ember.computed.equal('field.type', 'dropdown'), isDropdown: Ember.computed.equal('field.type', 'dropdown'),
init() { init() {

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -29,6 +29,13 @@ const CustomWizard = Discourse.Model.extend({
const fields = s.get('fields'); const fields = s.get('fields');
fields.forEach((f) => { fields.forEach((f) => {
f.set('id', Ember.String.dasherize(f.get('label'))); 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); step['fields'].push(f);
}); });
@ -42,7 +49,8 @@ const CustomWizard = Discourse.Model.extend({
const id = this.get('id'); const id = this.get('id');
const name = this.get('name'); 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'); const existingId = this.get('existingId');
if (existingId && existingId !== id) { 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) { create(w) {
const wizard = this._super.apply(this); const wizard = this._super.apply(this);
@ -83,20 +99,24 @@ CustomWizard.reopenClass({
let props = { steps }; let props = { steps };
if (w) { if (w) {
props['id'] = w.id; props['name'] = w.name; props['id'] = w.id;
props['name'] = w.name;
if (w.steps) { if (w.steps) {
w.steps.forEach((s) => { w.steps.forEach((s) => {
let fields = Ember.A(); let fields = Ember.A();
s.fields.forEach((f) => { s.fields.forEach((f) => {
let field = Ember.Object.create(f);
let choices = Ember.A(); let choices = Ember.A();
f.choices.forEach((c) => { f.choices.forEach((c) => {
choices.pushObject(Ember.Object.create(c)); choices.pushObject(Ember.Object.create(c));
}); });
fields.pushObject(Ember.Object.create(f)); field.set('choices', choices);
fields.pushObject(field);
}); });
let actions = Ember.A(); let actions = Ember.A();
@ -112,7 +132,9 @@ CustomWizard.reopenClass({
actions actions
})); }));
}); });
} };
} else {
props['save_submissions'] = true;
}; };
wizard.setProperties(props); wizard.setProperties(props);

Datei anzeigen

@ -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);
}
});

Datei anzeigen

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

Datei anzeigen

@ -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);
}
});

Datei anzeigen

@ -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>

Datei anzeigen

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

Datei anzeigen

@ -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>

Datei anzeigen

@ -1,5 +1,6 @@
{{#admin-nav}} {{#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}} {{/admin-nav}}
<div class="admin-container"> <div class="admin-container">

Datei anzeigen

@ -1,17 +1,26 @@
{{combo-box value=action.data content=stepFields valueAttribute='id' nameProperty='label'}} {{combo-box value=action.type content=types}}
{{combo-box value=action.target content=targets}} {{#if createTopic}}
{{#if isTopic}} <label>{{i18n "admin.wizard.action.create_topic.category"}}</label>
{{category-select-box value=action.target.category_id tabindex="3"}} {{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}}
{{#if isEmail}} {{#if sendMessage}}
<span>{{i18n 'admin.wizard.action.email'}}</span> <label>{{i18n "admin.wizard.action.send_message.title"}}</label>
{{input type='text' value=action.target.email}} {{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}}
{{#if isProfile}} {{#if updateProfile}}
<span>{{i18n 'admin.wizard.action.email'}}</span> <label>{{i18n "admin.wizard.action.source"}}</label>
{{combo-box value=action.target.profile_field content=profileFields}} {{combo-box value=action.source content=stepFields nameProperty="label"}}
{{/if}} <label>{{i18n "admin.wizard.action.profile_field"}}</label>
{{#if isBadge}} {{combo-box value=action.profile_field content=profileFields}}
<span>{{i18n 'admin.wizard.action.badge'}}</span>
{{combo-box value=action.target.badge content=badges}}
{{/if}} {{/if}}

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -1,9 +1,8 @@
export default { export default {
name: 'custom-routes', name: 'custom-routes',
initialize(container, app) { initialize(app) {
if (app.get('rootElement') !== '#custom-wizard-main') return; if (app.constructor.name !== 'Class' || app.get('rootElement') !== '#custom-wizard-main') return;
const WizardApplicationRoute = requirejs('wizard/routes/application').default; const WizardApplicationRoute = requirejs('wizard/routes/application').default;
const findCustomWizard = requirejs('discourse/plugins/discourse-custom-wizard/wizard/models/custom').findCustomWizard; 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 ajax = requirejs('wizard/lib/ajax').ajax;
const StepRoute = requirejs('wizard/routes/step').default; const StepRoute = requirejs('wizard/routes/step').default;
const StepModel = requirejs('wizard/models/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() { Router.map(function() {
this.route('custom', { path: '/custom/:id' }, function() { this.route('custom', { path: '/custom/:id' }, function() {
@ -62,5 +63,28 @@ export default {
return model.set("wizardId", wizard.id); 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');
}
}
});
} }
}; };

Datei anzeigen

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

Datei anzeigen

@ -5,8 +5,10 @@ en:
label: "Wizards" label: "Wizards"
new: "New" new: "New"
custom_label: "Custom" custom_label: "Custom"
submissions_label: "Submissions"
name: "Name" name: "Name"
name_placeholder: "name of the wizard" name_placeholder: "name of the wizard"
save_submissions: "Save wizard submissions"
save: "Save Wizard" save: "Save Wizard"
remove: "Delete Wizard" remove: "Delete Wizard"
step: step:
@ -18,15 +20,31 @@ en:
description_placeholder: "This will appear underneath the title and / or title" description_placeholder: "This will appear underneath the title and / or title"
add: "Add Step" add: "Add Step"
remove: "Remove Step" remove: "Remove Step"
add_field: "Add Field"
add_action: "Add Action"
field: field:
add: "Add Field"
remove: "Remove Field"
label: "Field Name" label: "Field Name"
description: "Field Description" description: "Field Description"
type: "Field Type" type: "Field Type"
choices_label: "Dropdown Choices" choices_label: "Dropdown Choices"
add_choice: "Add Choice" add_choice: "Add Choice"
required: "Field Required" required: "Field Required"
remove: "Remove Field"
action: 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"

Datei anzeigen

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

Datei anzeigen

@ -1,9 +1,24 @@
class CustomWizard::Builder class CustomWizard::Builder
def initialize(user, wizard_id) def initialize(user, wizard_id)
data = PluginStore.get('custom_wizard', wizard_id) data = PluginStore.get('custom_wizard', wizard_id)
@custom_wizard = CustomWizard::Wizard.new(data) @custom_wizard = CustomWizard::Wizard.new(data)
@wizard = Wizard.new(user) @wizard = Wizard.new(user)
@wizard.id = wizard_id @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 end
def build def build
@ -27,8 +42,73 @@ class CustomWizard::Builder
end end
step.on_update do |updater| 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 end
end end

Datei anzeigen

@ -1,5 +1,5 @@
# name: discourse-custom-wizard # name: discourse-custom-wizard
# about: Allows the admins to create custom user input forms # about: Create custom wizards
# version: 0.1 # version: 0.1
# authors: Angus McLeod # authors: Angus McLeod
@ -37,24 +37,44 @@ after_initialize do
end end
scope module: 'custom_wizard', constraints: AdminConstraint.new 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' => 'admin#index'
get 'admin/wizards/custom/new' => 'admin#index' get 'admin/wizards/custom/new' => 'admin#index'
get 'admin/wizards/custom/all' => 'admin#all' get 'admin/wizards/custom/all' => 'admin#custom_wizards'
get 'admin/wizards/custom/:wizard_id' => 'admin#find' get 'admin/wizards/custom/:wizard_id' => 'admin#find_wizard'
put 'admin/wizards/custom/save' => 'admin#save' put 'admin/wizards/custom/save' => 'admin#save'
delete 'admin/wizards/custom/remove' => 'admin#remove' 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
end end
class ::Wizard class ::Wizard
attr_accessor :id attr_accessor :id, :save_submissions
end end
class ::Wizard::Step class ::Wizard::Step
attr_accessor :title attr_accessor :title
end 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 attr_reader :label, :description
def initialize(attrs) def initialize(attrs)
@ -84,7 +104,6 @@ after_initialize do
::WizardFieldSerializer.class_eval do ::WizardFieldSerializer.class_eval do
def label def label
puts "LABEL: #{object.label}"
if object.label if object.label
object.label object.label
else else