Spiegel von
https://github.com/paviliondev/discourse-custom-wizard.git
synchronisiert 2024-11-25 18:50:27 +01:00
various
Dieser Commit ist enthalten in:
Ursprung
dd26ac63af
Commit
e859e3efa2
39 geänderte Dateien mit 892 neuen und 439 gelöschten Zeilen
|
@ -15,20 +15,7 @@ class CustomWizard::AdminController < ::ApplicationController
|
||||||
|
|
||||||
wizard = ::JSON.parse(params[:wizard])
|
wizard = ::JSON.parse(params[:wizard])
|
||||||
|
|
||||||
saved = false
|
|
||||||
if wizard["existing_id"] && rows = PluginStoreRow.where(plugin_name: 'custom_wizard').order(:id)
|
|
||||||
rows.each do |r, i|
|
|
||||||
wizard = CustomWizard::Wizard.new(r.value)
|
|
||||||
if wizard.id = wizard["existing_id"]
|
|
||||||
r.update_all(key: wizard['id'], value: wizard)
|
|
||||||
saved = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
unless saved
|
|
||||||
PluginStore.set('custom_wizard', wizard["id"], wizard)
|
PluginStore.set('custom_wizard', wizard["id"], wizard)
|
||||||
end
|
|
||||||
|
|
||||||
render json: success_json
|
render json: success_json
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<link href="<%= Discourse.base_uri %>/plugins/discourse-custom-wizard/desktop.css" media="all" rel="stylesheet" data-target="desktop" type="text/css" />
|
|
||||||
<%= discourse_stylesheet_link_tag :wizard, theme_key: nil %>
|
<%= discourse_stylesheet_link_tag :wizard, theme_key: nil %>
|
||||||
<%= discourse_stylesheet_link_tag(mobile_view? ? :mobile : :desktop) %>
|
<%= stylesheet_link_tag "wizard_custom", media: "all", "data-turbolinks-track" => "reload" %>
|
||||||
<%= preload_script "ember_jquery" %>
|
<%= preload_script "ember_jquery" %>
|
||||||
<%= preload_script "wizard-vendor" %>
|
<%= preload_script "wizard-vendor" %>
|
||||||
<%= preload_script "wizard-application" %>
|
<%= preload_script "wizard-application" %>
|
||||||
|
@ -18,7 +17,7 @@
|
||||||
<meta name="discourse-base-uri" content="<%= Discourse.base_uri %>">
|
<meta name="discourse-base-uri" content="<%= Discourse.base_uri %>">
|
||||||
|
|
||||||
<%= render partial: "layouts/head" %>
|
<%= render partial: "layouts/head" %>
|
||||||
<title><%= t 'custom_wizard.title' %></title>
|
<title><%= t 'wizard.custom_title' %></title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class='custom-wizard'>
|
<body class='custom-wizard'>
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
|
import { on, observes } from 'ember-addons/ember-computed-decorators';
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
classNames: 'wizard-custom-action',
|
classNames: 'wizard-custom-action',
|
||||||
types: ['create_topic', 'update_profile', 'send_message'],
|
types: ['create_topic', 'update_profile', 'send_message'],
|
||||||
profileFields: ['name', 'username', 'email'],
|
profileFields: ['name', 'username', 'email'],
|
||||||
createTopic: Ember.computed.equal('action.type', 'create_topic'),
|
createTopic: Ember.computed.equal('action.type', 'create_topic'),
|
||||||
updateProfile: Ember.computed.equal('action.type', 'update_profile'),
|
updateProfile: Ember.computed.equal('action.type', 'update_profile'),
|
||||||
sendMessage: Ember.computed.equal('action.type', 'send_message')
|
sendMessage: Ember.computed.equal('action.type', 'send_message'),
|
||||||
|
|
||||||
|
@on('init')
|
||||||
|
@observes('action')
|
||||||
|
setup() {
|
||||||
|
this.set('existingId', this.get('action.id'));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,19 +5,43 @@ export default Ember.Component.extend({
|
||||||
isDropdown: Ember.computed.equal('field.type', 'dropdown'),
|
isDropdown: Ember.computed.equal('field.type', 'dropdown'),
|
||||||
|
|
||||||
@on('init')
|
@on('init')
|
||||||
@observes('field.id')
|
@observes('field')
|
||||||
init() {
|
setup() {
|
||||||
this._super(...arguments);
|
this.set('existingId', this.get('field.id'));
|
||||||
if (!this.get('field.choices')) {
|
|
||||||
this.set('field.choices', Ember.A());
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@computed('field.type')
|
||||||
|
isInput: (type) => type === 'text' || type === 'textarea',
|
||||||
|
|
||||||
@computed('field.choices.[]')
|
@computed('field.choices.[]')
|
||||||
dropdownChoices: choices => choices,
|
dropdownChoices: choices => choices,
|
||||||
|
|
||||||
|
@computed('field.choices_filters.[]')
|
||||||
|
presetFilters: filters => filters,
|
||||||
|
|
||||||
|
@computed()
|
||||||
|
presetChoices() {
|
||||||
|
return [
|
||||||
|
{ id: 'categories', name: I18n.t('admin.wizard.field.choices_preset.categories') }
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
addFilter() {
|
||||||
|
if (!this.get('field.choices_filters')) {
|
||||||
|
this.set('field.choices_filters', Ember.A());
|
||||||
|
}
|
||||||
|
this.get('field.choices_filters').pushObject(Ember.Object.create());
|
||||||
|
},
|
||||||
|
|
||||||
|
removeFilter(f) {
|
||||||
|
this.get('field.choices_filters').removeObject(f);
|
||||||
|
},
|
||||||
|
|
||||||
addChoice() {
|
addChoice() {
|
||||||
|
if (!this.get('field.choices')) {
|
||||||
|
this.set('field.choices', Ember.A());
|
||||||
|
}
|
||||||
this.get('field.choices').pushObject(Ember.Object.create());
|
this.get('field.choices').pushObject(Ember.Object.create());
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -7,15 +7,15 @@ export default Ember.Component.extend({
|
||||||
|
|
||||||
@on('init')
|
@on('init')
|
||||||
@observes('step')
|
@observes('step')
|
||||||
setup() {
|
setCurrent() {
|
||||||
this._super(...arguments);
|
this.set('existingId', this.get('step.id'));
|
||||||
const fields = this.get('step.fields') || [];
|
const fields = this.get('step.fields') || [];
|
||||||
const actions = this.get('step.actions') || [];
|
const actions = this.get('step.actions') || [];
|
||||||
this.set('currentField', fields[0]);
|
this.set('currentField', fields[0]);
|
||||||
this.set('currentAction', actions[0]);
|
this.set('currentAction', actions[0]);
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed('step.fields.[]', 'currentField')
|
@computed('step.fields.@each.id', 'currentField')
|
||||||
fieldLinks(fields, current) {
|
fieldLinks(fields, current) {
|
||||||
if (!fields) return;
|
if (!fields) return;
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ export default Ember.Component.extend({
|
||||||
const id = f.get('id');
|
const id = f.get('id');
|
||||||
const label = f.get('label');
|
const label = f.get('label');
|
||||||
|
|
||||||
let link = { id, label: label || id };
|
let link = { id, label: label || id || 'new' };
|
||||||
|
|
||||||
let classes = 'btn';
|
let classes = 'btn';
|
||||||
if (current && f.get('id') === current.get('id')) {
|
if (current && f.get('id') === current.get('id')) {
|
||||||
|
@ -38,7 +38,7 @@ export default Ember.Component.extend({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed('step.actions.[]', 'currentAction')
|
@computed('step.actions.@each.id', 'currentAction')
|
||||||
actionLinks(actions, current) {
|
actionLinks(actions, current) {
|
||||||
if (!actions) return;
|
if (!actions) return;
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ export default Ember.Component.extend({
|
||||||
const id = a.get('id');
|
const id = a.get('id');
|
||||||
const label = a.get('label');
|
const label = a.get('label');
|
||||||
|
|
||||||
let link = { id, label: label || id };
|
let link = { id, label: label || id || 'new' };
|
||||||
|
|
||||||
let classes = 'btn';
|
let classes = 'btn';
|
||||||
if (current && a.get('id') === current.get('id')) {
|
if (current && a.get('id') === current.get('id')) {
|
||||||
|
@ -64,20 +64,14 @@ export default Ember.Component.extend({
|
||||||
actions: {
|
actions: {
|
||||||
addField() {
|
addField() {
|
||||||
const fields = this.get('step.fields');
|
const fields = this.get('step.fields');
|
||||||
const newNum = fields.length + 1;
|
const field = Ember.Object.create();
|
||||||
const field = Ember.Object.create({
|
|
||||||
id: `field-${newNum}`
|
|
||||||
});
|
|
||||||
fields.pushObject(field);
|
fields.pushObject(field);
|
||||||
this.set('currentField', field);
|
this.set('currentField', field);
|
||||||
},
|
},
|
||||||
|
|
||||||
addAction() {
|
addAction() {
|
||||||
const actions = this.get('step.actions');
|
const actions = this.get('step.actions');
|
||||||
const newNum = actions.length + 1;
|
const action = Ember.Object.create();
|
||||||
const action = Ember.Object.create({
|
|
||||||
id: `action-${newNum}`
|
|
||||||
});
|
|
||||||
actions.pushObject(action);
|
actions.pushObject(action);
|
||||||
this.set('currentAction', action);
|
this.set('currentAction', action);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
import { default as computed } from 'ember-addons/ember-computed-decorators';
|
import { default as computed } from 'ember-addons/ember-computed-decorators';
|
||||||
|
|
||||||
export default Ember.Controller.extend({
|
export default Ember.Controller.extend({
|
||||||
|
@computed('model.steps.@each.id', 'currentStep')
|
||||||
@computed('model.steps.[]', 'currentStep')
|
|
||||||
stepLinks(steps, currentStep) {
|
stepLinks(steps, currentStep) {
|
||||||
return steps.map((s) => {
|
return steps.map((s) => {
|
||||||
if (s) {
|
if (s) {
|
||||||
const id = s.get('id');
|
const id = s.get('id');
|
||||||
const title = s.get('title');
|
const title = s.get('title');
|
||||||
|
|
||||||
let link = { id, title: title || id };
|
let link = { id, title: title || id || 'new' };
|
||||||
|
|
||||||
let classes = 'btn';
|
let classes = 'btn';
|
||||||
if (currentStep && id === currentStep.get('id')) {
|
if (currentStep && id === currentStep.get('id')) {
|
||||||
|
@ -25,17 +24,27 @@ export default Ember.Controller.extend({
|
||||||
|
|
||||||
@computed('model.id', 'model.name')
|
@computed('model.id', 'model.name')
|
||||||
wizardUrl(wizardId) {
|
wizardUrl(wizardId) {
|
||||||
return window.location.origin + '/wizard/custom/' + Ember.String.dasherize(wizardId);
|
return window.location.origin + '/w/' + Ember.String.dasherize(wizardId);
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
save() {
|
save() {
|
||||||
this.get('model').save().then(() => {
|
this.setProperties({
|
||||||
|
saving: true,
|
||||||
|
error: null
|
||||||
|
});
|
||||||
|
const wizard = this.get('model');
|
||||||
|
wizard.save().then(() => {
|
||||||
|
this.set('saving', false);
|
||||||
if (this.get('newWizard')) {
|
if (this.get('newWizard')) {
|
||||||
this.send("refreshAllWizards");
|
this.send("refreshAllWizards");
|
||||||
} else {
|
} else {
|
||||||
this.send("refreshWizard");
|
this.send("refreshWizard");
|
||||||
}
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
this.set('saving', false);
|
||||||
|
this.set('error', I18n.t(`admin.wizard.error.${error}`));
|
||||||
|
Ember.run.later(() => this.set('error', null), 10000);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -47,11 +56,9 @@ export default Ember.Controller.extend({
|
||||||
|
|
||||||
addStep() {
|
addStep() {
|
||||||
const steps = this.get('model.steps');
|
const steps = this.get('model.steps');
|
||||||
const newNum = steps.length + 1;
|
|
||||||
const step = Ember.Object.create({
|
const step = Ember.Object.create({
|
||||||
fields: Ember.A(),
|
fields: Ember.A(),
|
||||||
actions: Ember.A(),
|
actions: Ember.A()
|
||||||
id: `step-${newNum}`
|
|
||||||
});
|
});
|
||||||
steps.pushObject(step);
|
steps.pushObject(step);
|
||||||
this.set('currentStep', step);
|
this.set('currentStep', step);
|
||||||
|
|
|
@ -1,92 +1,92 @@
|
||||||
import { observes, on } from 'ember-addons/ember-computed-decorators';
|
|
||||||
import { ajax } from 'discourse/lib/ajax';
|
import { ajax } from 'discourse/lib/ajax';
|
||||||
|
|
||||||
const CustomWizard = Discourse.Model.extend({
|
const CustomWizard = Discourse.Model.extend({
|
||||||
@on('init')
|
|
||||||
setup() {
|
|
||||||
const id = this.get('id');
|
|
||||||
if (id) this.set('existingId', id);
|
|
||||||
},
|
|
||||||
|
|
||||||
@observes('name')
|
|
||||||
updateId() {
|
|
||||||
const name = this.get('name');
|
|
||||||
this.set('id', name.underscore());
|
|
||||||
},
|
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
const stepsObj = this.get('steps');
|
return new Ember.RSVP.Promise((resolve, reject) => {
|
||||||
let steps = [];
|
|
||||||
|
|
||||||
stepsObj.forEach((s) => {
|
|
||||||
|
|
||||||
if (!s.title && !s.translation_key) return;
|
|
||||||
|
|
||||||
let step = {
|
|
||||||
id: (s.title || s.translation_key.split('.').pop()).underscore(),
|
|
||||||
fields: [],
|
|
||||||
actions: []
|
|
||||||
};
|
|
||||||
|
|
||||||
if (s.title) step['title'] = s.title;
|
|
||||||
if (s.translation_key) step['translation_key'] = s.translation_key;
|
|
||||||
if (s.banner) step['banner'] = s.banner;
|
|
||||||
if (s.description) step['description'] = s.description;
|
|
||||||
|
|
||||||
const fields = s.get('fields');
|
|
||||||
fields.forEach((f) => {
|
|
||||||
const fl = f.get('label');
|
|
||||||
const fkey = f.get('translation_key');
|
|
||||||
|
|
||||||
if (!fl && !fkey) return;
|
|
||||||
|
|
||||||
f.set('id', (fl || fkey.split('.').pop()).underscore());
|
|
||||||
|
|
||||||
if (f.get('type') === 'dropdown') {
|
|
||||||
const choices = f.get('choices');
|
|
||||||
|
|
||||||
choices.forEach((c) => {
|
|
||||||
const cl = c.get('label');
|
|
||||||
const ckey = c.get('translation_key');
|
|
||||||
|
|
||||||
if (!cl && !ckey) return;
|
|
||||||
|
|
||||||
c.set('id', (cl || ckey.split('.').pop()).underscore());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
step['fields'].push(f);
|
|
||||||
});
|
|
||||||
|
|
||||||
s.actions.forEach((a) => {
|
|
||||||
const al = a.get('label');
|
|
||||||
if (!al) return;
|
|
||||||
a.set('id', al.underscore());
|
|
||||||
step['actions'].push(a);
|
|
||||||
});
|
|
||||||
|
|
||||||
steps.push(step);
|
|
||||||
});
|
|
||||||
|
|
||||||
const id = this.get('id');
|
const id = this.get('id');
|
||||||
|
if (!id || !id.underscore()) reject('id_required');
|
||||||
|
|
||||||
|
let wizard = { id: id.underscore() };
|
||||||
|
|
||||||
|
const steps = this.get('steps');
|
||||||
|
if (steps.length) wizard['steps'] = this.buildSteps(steps, reject);
|
||||||
|
|
||||||
const name = this.get('name');
|
const name = this.get('name');
|
||||||
|
if (name) wizard['name'] = name;
|
||||||
|
|
||||||
const background = this.get('background');
|
const background = this.get('background');
|
||||||
|
if (background) wizard['background'] = background;
|
||||||
|
|
||||||
const save_submissions = this.get('save_submissions');
|
const save_submissions = this.get('save_submissions');
|
||||||
let wizard = { id, name, background, save_submissions, steps };
|
if (save_submissions) wizard['save_submissions'] = save_submissions;
|
||||||
|
|
||||||
const existingId = this.get('existingId');
|
const multiple_submissions = this.get('multiple_submissions');
|
||||||
if (existingId && existingId !== id) {
|
if (multiple_submissions) wizard['multiple_submissions'] = multiple_submissions;
|
||||||
wizard['existing_id'] = existingId;
|
|
||||||
};
|
|
||||||
|
|
||||||
return ajax("/admin/wizards/custom/save", {
|
ajax("/admin/wizards/custom/save", {
|
||||||
type: 'PUT',
|
type: 'PUT',
|
||||||
data: {
|
data: {
|
||||||
wizard: JSON.stringify(wizard)
|
wizard: JSON.stringify(wizard)
|
||||||
}
|
}
|
||||||
|
}).then((result) => resolve(result));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
buildSteps(stepsObj, reject) {
|
||||||
|
let steps = [];
|
||||||
|
|
||||||
|
stepsObj.some((s) => {
|
||||||
|
if (!s.id || !s.id.underscore()) reject('id_required');
|
||||||
|
|
||||||
|
let step = { id: s.id.underscore() };
|
||||||
|
|
||||||
|
if (s.title) step['title'] = s.title;
|
||||||
|
if (s.key) step['key'] = s.key;
|
||||||
|
if (s.banner) step['banner'] = s.banner;
|
||||||
|
if (s.description) step['description'] = s.description;
|
||||||
|
|
||||||
|
const fields = s.get('fields');
|
||||||
|
if (fields.length) {
|
||||||
|
step['fields'] = [];
|
||||||
|
|
||||||
|
fields.some((f) => {
|
||||||
|
let id = f.get('id');
|
||||||
|
|
||||||
|
if (!id || !id.underscore()) reject('id_required');
|
||||||
|
f.set('id', id.underscore());
|
||||||
|
|
||||||
|
if (f.get('type') === 'dropdown') {
|
||||||
|
const choices = f.get('choices');
|
||||||
|
if (choices && choices.length < 1 && !f.get('choices_key') && !f.get('choices_categories')) {
|
||||||
|
reject('field.need_choices');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
step['fields'].push(f);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions = s.actions;
|
||||||
|
if (actions.length) {
|
||||||
|
step['actions'] = [];
|
||||||
|
|
||||||
|
actions.some((a) => {
|
||||||
|
let id = a.get('id');
|
||||||
|
if (!id || !id.underscore()) reject('id_required');
|
||||||
|
|
||||||
|
a.set('id', id.underscore());
|
||||||
|
|
||||||
|
step['actions'].push(a);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
steps.push(step);
|
||||||
|
});
|
||||||
|
|
||||||
|
return steps;
|
||||||
|
},
|
||||||
|
|
||||||
remove() {
|
remove() {
|
||||||
return ajax("/admin/wizards/custom/remove", {
|
return ajax("/admin/wizards/custom/remove", {
|
||||||
type: 'DELETE',
|
type: 'DELETE',
|
||||||
|
@ -121,16 +121,26 @@ CustomWizard.reopenClass({
|
||||||
|
|
||||||
if (w) {
|
if (w) {
|
||||||
props['id'] = w.id;
|
props['id'] = w.id;
|
||||||
|
props['existingId'] = true;
|
||||||
props['name'] = w.name;
|
props['name'] = w.name;
|
||||||
props['background'] = w.background;
|
props['background'] = w.background;
|
||||||
props['save_submissions'] = w.save_submissions;
|
props['save_submissions'] = w.save_submissions;
|
||||||
|
props['multiple_submissions'] = w.multiple_submissions;
|
||||||
|
|
||||||
if (w.steps) {
|
if (w.steps && w.steps.length) {
|
||||||
w.steps.forEach((s) => {
|
w.steps.forEach((s) => {
|
||||||
|
// clean empty strings
|
||||||
|
Object.keys(s).forEach((key) => (s[key] === '') && delete s[key]);
|
||||||
|
|
||||||
let fields = Ember.A();
|
let fields = Ember.A();
|
||||||
|
|
||||||
|
if (s.fields && s.fields.length) {
|
||||||
s.fields.forEach((f) => {
|
s.fields.forEach((f) => {
|
||||||
|
Object.keys(f).forEach((key) => (f[key] === '') && delete f[key]);
|
||||||
|
|
||||||
let field = Ember.Object.create(f);
|
let field = Ember.Object.create(f);
|
||||||
|
|
||||||
|
if (f.choices) {
|
||||||
let choices = Ember.A();
|
let choices = Ember.A();
|
||||||
|
|
||||||
f.choices.forEach((c) => {
|
f.choices.forEach((c) => {
|
||||||
|
@ -138,18 +148,22 @@ CustomWizard.reopenClass({
|
||||||
});
|
});
|
||||||
|
|
||||||
field.set('choices', choices);
|
field.set('choices', choices);
|
||||||
|
}
|
||||||
|
|
||||||
fields.pushObject(field);
|
fields.pushObject(field);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let actions = Ember.A();
|
let actions = Ember.A();
|
||||||
|
if (s.actions && s.actions.length) {
|
||||||
s.actions.forEach((a) => {
|
s.actions.forEach((a) => {
|
||||||
actions.pushObject(Ember.Object.create(a));
|
actions.pushObject(Ember.Object.create(a));
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
steps.pushObject(Ember.Object.create({
|
steps.pushObject(Ember.Object.create({
|
||||||
id: s.id,
|
id: s.id,
|
||||||
translation_key: s.translation_key,
|
key: s.key,
|
||||||
title: s.title,
|
title: s.title,
|
||||||
description: s.description,
|
description: s.description,
|
||||||
banner: s.banner,
|
banner: s.banner,
|
||||||
|
@ -163,6 +177,7 @@ CustomWizard.reopenClass({
|
||||||
props['name'] = '';
|
props['name'] = '';
|
||||||
props['background'] = '';
|
props['background'] = '';
|
||||||
props['save_submissions'] = true;
|
props['save_submissions'] = true;
|
||||||
|
props['multiple_submissions'] = false;
|
||||||
props['steps'] = Ember.A();
|
props['steps'] = Ember.A();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,15 +2,30 @@ import CustomWizard from '../models/custom-wizard';
|
||||||
import { ajax } from 'discourse/lib/ajax';
|
import { ajax } from 'discourse/lib/ajax';
|
||||||
|
|
||||||
export default Discourse.Route.extend({
|
export default Discourse.Route.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) {
|
model(params) {
|
||||||
if (params.wizard_id === 'new') {
|
const wizardId = params.wizard_id;
|
||||||
|
|
||||||
|
if (wizardId === 'new') {
|
||||||
this.set('newWizard', true);
|
this.set('newWizard', true);
|
||||||
return CustomWizard.create();
|
return CustomWizard.create();
|
||||||
};
|
};
|
||||||
this.set('newWizard', false);
|
this.set('newWizard', false);
|
||||||
|
|
||||||
const wizard = this.modelFor('admin-wizards-custom').findBy('id', params.wizard_id.underscore());
|
const wizard = this.modelFor('admin-wizards-custom').findBy('id', wizardId.underscore());
|
||||||
if (!wizard) return this.transitionTo('adminWizardsCustom.index');
|
|
||||||
|
if (!wizard) return this.transitionTo('adminWizard', 'new');
|
||||||
|
|
||||||
return wizard;
|
return wizard;
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
export default Discourse.Route.extend({
|
||||||
|
redirect() {
|
||||||
|
this.transitionTo('adminWizard', 'first');
|
||||||
|
}
|
||||||
|
});
|
|
@ -7,8 +7,9 @@ export default Discourse.Route.extend({
|
||||||
|
|
||||||
afterModel(model) {
|
afterModel(model) {
|
||||||
const transitionToWizard = this.get('transitionToWizard');
|
const transitionToWizard = this.get('transitionToWizard');
|
||||||
if (transitionToWizard === 'last' && model.length) {
|
if (transitionToWizard && model.length) {
|
||||||
this.transitionTo('adminWizard', model[model.length - 1].id);
|
this.set('transitionToWizard', null);
|
||||||
|
this.transitionTo('adminWizard', transitionToWizard);
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
5
assets/javascripts/discourse/routes/admin-wizards-index.js.es6
Normale Datei
5
assets/javascripts/discourse/routes/admin-wizards-index.js.es6
Normale Datei
|
@ -0,0 +1,5 @@
|
||||||
|
export default Discourse.Route.extend({
|
||||||
|
redirect() {
|
||||||
|
this.transitionTo('adminWizardsCustom');
|
||||||
|
}
|
||||||
|
});
|
|
@ -1 +0,0 @@
|
||||||
export default Discourse.Route.extend();
|
|
|
@ -6,10 +6,10 @@
|
||||||
|
|
||||||
<div class="setting">
|
<div class="setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<h3>{{i18n 'admin.wizard.background'}}</h3>
|
<h3>{{i18n 'admin.wizard.id'}}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-value">
|
<div class="setting-value">
|
||||||
{{input name="background" value=model.background placeholderKey="admin.wizard.background_placeholder"}}
|
{{input name="name" value=model.id placeholderKey="admin.wizard.id_placeholder" disabled=model.existingId}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -22,6 +22,15 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="setting">
|
||||||
|
<div class="setting-label">
|
||||||
|
<h3>{{i18n 'admin.wizard.background'}}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="setting-value">
|
||||||
|
{{input name="background" value=model.background placeholderKey="admin.wizard.background_placeholder"}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="setting">
|
<div class="setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<h3>{{i18n 'admin.wizard.save_submissions'}}</h3>
|
<h3>{{i18n 'admin.wizard.save_submissions'}}</h3>
|
||||||
|
@ -32,6 +41,16 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="setting full">
|
||||||
|
<div class="setting-label">
|
||||||
|
<h3>{{i18n 'admin.wizard.multiple_submissions'}}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="setting-value">
|
||||||
|
{{input type='checkbox' checked=model.multiple_submissions}}
|
||||||
|
<span for="save">{{i18n 'admin.wizard.multiple_submissions_label'}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="setting full">
|
<div class="setting full">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<h3>{{i18n 'admin.wizard.url'}}</h3>
|
<h3>{{i18n 'admin.wizard.url'}}</h3>
|
||||||
|
@ -55,8 +74,11 @@
|
||||||
<div class='buttons'>
|
<div class='buttons'>
|
||||||
<button {{action "save"}} disabled={{disableSave}} class='btn btn-primary'>{{i18n 'admin.wizard.save'}}</button>
|
<button {{action "save"}} disabled={{disableSave}} class='btn btn-primary'>{{i18n 'admin.wizard.save'}}</button>
|
||||||
{{#unless newWizard}}
|
{{#unless newWizard}}
|
||||||
<button {{action "remove"}} class='btn btn-danger'>{{d-icon "trash-o"}}{{i18n 'admin.wizard.remove'}}</button>
|
<button {{action "remove"}} class='btn btn-danger remove'>{{d-icon "trash-o"}}{{i18n 'admin.wizard.remove'}}</button>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
<span class="saving {{unless savingStatus 'hidden'}}">{{savingStatus}}</span>
|
{{conditional-loading-spinner condition=saving size='small'}}
|
||||||
|
{{#if error}}
|
||||||
|
<span class="error">{{d-icon "times"}}{{error}}</span>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<div class='row'>
|
<div class='row'>
|
||||||
<div class='wizard-list'>
|
<div class='content-list wizard-list'>
|
||||||
<ul>
|
<ul>
|
||||||
{{#each model as |s|}}
|
{{#each model as |s|}}
|
||||||
<li>
|
<li>
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
<div class="setting">
|
<div class="setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<h3>{{i18n "admin.wizard.action.label"}}</h3>
|
<h3>{{i18n "admin.wizard.action.id"}}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-value">
|
<div class="setting-value">
|
||||||
{{input value=action.label}}
|
{{input value=action.id placeholderKey='admin.wizard.id_placeholder' disabled=existingId}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="setting">
|
<div class="setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<h3>{{i18n "admin.wizard.action.type"}}</h3>
|
<h3>{{i18n "admin.wizard.type"}}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-value">
|
<div class="setting-value">
|
||||||
{{combo-box value=action.type content=types}}
|
{{combo-box value=action.type content=types}}
|
||||||
|
|
|
@ -1,9 +1,18 @@
|
||||||
<div class="setting">
|
<div class="setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<h3>{{i18n 'admin.wizard.translation'}}</h3>
|
<h3>{{i18n 'admin.wizard.id'}}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-value">
|
<div class="setting-value">
|
||||||
{{input name="translation_key" value=field.translation_key placeholderKey="admin.wizard.field.translation_placeholder"}}
|
{{input name="id" value=field.id placeholderKey="admin.wizard.id_placeholder" disabled=existingId}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting">
|
||||||
|
<div class="setting-label">
|
||||||
|
<h3>{{i18n 'admin.wizard.key'}}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="setting-value">
|
||||||
|
{{input name="key" value=field.key placeholderKey="admin.wizard.key_placeholder"}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -12,7 +21,7 @@
|
||||||
<h3>{{i18n 'admin.wizard.field.label'}}</h3>
|
<h3>{{i18n 'admin.wizard.field.label'}}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-value">
|
<div class="setting-value">
|
||||||
{{input name="label" value=field.label}}
|
{{input name="label" value=field.label placeholder=(i18n "admin.wizard.custom_text_placeholder")}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -21,13 +30,13 @@
|
||||||
<h3>{{i18n 'admin.wizard.field.description'}}</h3>
|
<h3>{{i18n 'admin.wizard.field.description'}}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-value">
|
<div class="setting-value">
|
||||||
{{textarea name="description" value=field.description}}
|
{{textarea name="description" value=field.description placeholder=(i18n "admin.wizard.custom_text_placeholder")}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="setting">
|
<div class="setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<h3>{{i18n 'admin.wizard.field.type'}}</h3>
|
<h3>{{i18n 'admin.wizard.type'}}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-value">
|
<div class="setting-value">
|
||||||
{{combo-box value=field.type content=types}}
|
{{combo-box value=field.type content=types}}
|
||||||
|
@ -44,17 +53,57 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{#if isInput}}
|
||||||
|
<div class="setting">
|
||||||
|
<div class="setting-label">
|
||||||
|
<h3>{{i18n 'admin.wizard.field.min_length'}}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="setting-value">
|
||||||
|
{{input type="number" name="min_length" value=field.min_length placeholder=(i18n 'admin.wizard.field.min_length_placeholder')}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{#if isDropdown}}
|
{{#if isDropdown}}
|
||||||
<div class="wizard-dropdown-choices">
|
<div class="wizard-dropdown-choices">
|
||||||
<div class="wizard-header small">
|
<div class="wizard-header medium">
|
||||||
{{i18n 'admin.wizard.field.choices_label'}}
|
{{i18n 'admin.wizard.field.choices_label'}}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="setting">
|
||||||
|
<div class="wizard-header small">
|
||||||
|
{{i18n 'admin.wizard.field.choices_translation'}}
|
||||||
|
</div>
|
||||||
|
<div class="setting-value">
|
||||||
|
{{input name="key" value=field.choices_key placeholderKey="admin.wizard.key_placeholder"}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting full">
|
||||||
|
<div class="wizard-header small">
|
||||||
|
{{i18n 'admin.wizard.field.choices_preset.label'}}
|
||||||
|
</div>
|
||||||
|
{{combo-box value=field.choices_preset content=presetChoices none='admin.wizard.field.choices_preset.none'}}
|
||||||
|
<label>{{i18n 'admin.wizard.field.choices_preset.filter'}}</label>
|
||||||
|
{{#each presetFilters as |f|}}
|
||||||
|
<span class='custom-input'>
|
||||||
|
{{input type="text" value=f.key placeholder=(i18n 'admin.wizard.field.choices_preset.key')}}
|
||||||
|
{{input type="text" value=f.value placeholder=(i18n 'admin.wizard.field.choices_preset.value')}}
|
||||||
|
</span>
|
||||||
|
{{d-button action='removeFilter' actionParam=f icon='times'}}
|
||||||
|
{{/each}}
|
||||||
|
<div>{{d-button action='addFilter' label='admin.wizard.add' icon='plus'}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting full">
|
||||||
|
<div class="wizard-header small">
|
||||||
|
{{i18n 'admin.wizard.field.choices_custom'}}
|
||||||
|
</div>
|
||||||
{{#each dropdownChoices as |c|}}
|
{{#each dropdownChoices as |c|}}
|
||||||
<span class='wizard-dropdown-choice'>
|
<span class='custom-input'>
|
||||||
{{input type='text' value=c.label}}
|
{{input type='text' value=c.value placeholder=(i18n 'admin.wizard.field.choice.value')}}
|
||||||
|
{{input type='text' value=c.label placeholder=(i18n 'admin.wizard.field.choice.label')}}
|
||||||
</span>
|
</span>
|
||||||
{{d-button action='removeChoice' actionParam=c icon='times'}}
|
{{d-button action='removeChoice' actionParam=c icon='times'}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{d-button action='addChoice' label='admin.wizard.add' icon='plus'}}
|
<div>{{d-button action='addChoice' label='admin.wizard.add' icon='plus'}}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -1,9 +1,18 @@
|
||||||
<div class="setting">
|
<div class="setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<h3>{{i18n 'admin.wizard.translation'}}</h3>
|
<h3>{{i18n 'admin.wizard.id'}}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-value">
|
<div class="setting-value">
|
||||||
{{input name="translation_key" value=step.translation_key placeholderKey="admin.wizard.step.translation_placeholder"}}
|
{{input name="id" value=step.id placeholderKey="admin.wizard.id_placeholder" disabled=existingId}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting">
|
||||||
|
<div class="setting-label">
|
||||||
|
<h3>{{i18n 'admin.wizard.key'}}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="setting-value">
|
||||||
|
{{input name="key" value=step.key placeholderKey="admin.wizard.key_placeholder"}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -12,7 +21,7 @@
|
||||||
<h3>{{i18n 'admin.wizard.step.title'}}</h3>
|
<h3>{{i18n 'admin.wizard.step.title'}}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-value">
|
<div class="setting-value">
|
||||||
{{input name="title" value=step.title placeholderKey="admin.wizard.step.title_placeholder"}}
|
{{input name="title" value=step.title placeholderKey="admin.wizard.custom_text_placeholder"}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -30,7 +39,7 @@
|
||||||
<h3>{{i18n 'admin.wizard.step.description'}}</h3>
|
<h3>{{i18n 'admin.wizard.step.description'}}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-value">
|
<div class="setting-value">
|
||||||
{{textarea name="description" value=step.description placeholder=(i18n "admin.wizard.step.description_placeholder")}}
|
{{textarea name="description" value=step.description placeholder=(i18n "admin.wizard.custom_text_placeholder")}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
//= require ./wizard/custom-wizard
|
//= require ./wizard/custom-wizard
|
||||||
//= require_tree ./wizard/components
|
|
||||||
//= require_tree ./wizard/controllers
|
//= require_tree ./wizard/controllers
|
||||||
//= require_tree ./wizard/helpers
|
//= require_tree ./wizard/helpers
|
||||||
//= require_tree ./wizard/initializers
|
//= require_tree ./wizard/initializers
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
import { observes } from 'ember-addons/ember-computed-decorators';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
classNames: 'wizard-field-composer',
|
|
||||||
|
|
||||||
keyPress(e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
},
|
|
||||||
|
|
||||||
@observes('field.value')
|
|
||||||
validate() {
|
|
||||||
const minLength = Wizard.SiteSettings.min_post_length;
|
|
||||||
const post = this.get('field.value');
|
|
||||||
const field = this.get('field');
|
|
||||||
|
|
||||||
field.set('customValidation', true);
|
|
||||||
|
|
||||||
if (!post) {
|
|
||||||
return field.setValid(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minLength && post.length < minLength) {
|
|
||||||
return field.setValid(false, I18n.t('wizard.validation.too_short', { min: minLength }));
|
|
||||||
}
|
|
||||||
|
|
||||||
field.setValid(true);
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -7,7 +7,7 @@ export default StepController.extend({
|
||||||
const next = this.get('step.next');
|
const next = this.get('step.next');
|
||||||
if (response.refresh_required) {
|
if (response.refresh_required) {
|
||||||
const id = this.get('wizard.id');
|
const id = this.get('wizard.id');
|
||||||
document.location = getUrl(`/wizard/custom/${id}/steps/${next}`);
|
document.location = getUrl(`/w/${id}/steps/${next}`);
|
||||||
} else {
|
} else {
|
||||||
this.transitionToRoute('custom.step', next);
|
this.transitionToRoute('custom.step', next);
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,10 @@ export default StepController.extend({
|
||||||
|
|
||||||
goBack() {
|
goBack() {
|
||||||
this.transitionToRoute('custom.step', this.get('step.previous'));
|
this.transitionToRoute('custom.step', this.get('step.previous'));
|
||||||
|
},
|
||||||
|
|
||||||
|
showMessage(message) {
|
||||||
|
this.set('stepMessage', message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,80 +4,27 @@ export default {
|
||||||
initialize(app) {
|
initialize(app) {
|
||||||
if (app.constructor.name !== 'Class' || 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 findCustomWizard = requirejs('discourse/plugins/discourse-custom-wizard/wizard/models/custom').findCustomWizard;
|
|
||||||
const Router = requirejs('wizard/router').default;
|
const Router = requirejs('wizard/router').default;
|
||||||
|
const ApplicationRoute = requirejs('wizard/routes/application').default;
|
||||||
const ajax = requirejs('wizard/lib/ajax').ajax;
|
const ajax = requirejs('wizard/lib/ajax').ajax;
|
||||||
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 WizardStep = requirejs('wizard/components/wizard-step').default;
|
||||||
const getUrl = requirejs('discourse-common/lib/get-url').default;
|
const getUrl = requirejs('discourse-common/lib/get-url').default;
|
||||||
const FieldModel = requirejs('wizard/models/wizard-field').default;
|
const FieldModel = requirejs('wizard/models/wizard-field').default;
|
||||||
|
|
||||||
|
Router.reopen({
|
||||||
|
rootURL: getUrl('/w/')
|
||||||
|
});
|
||||||
|
|
||||||
Router.map(function() {
|
Router.map(function() {
|
||||||
this.route('custom', { path: '/custom/:id' }, function() {
|
this.route('custom', { path: '/:wizard_id' }, function() {
|
||||||
this.route('step', { path: '/steps/:step_id' });
|
this.route('step', { path: '/steps/:step_id' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
WizardApplicationRoute.reopen({
|
ApplicationRoute.reopen({
|
||||||
model() {
|
redirect() {
|
||||||
const customParams = this.paramsFor('custom');
|
this.transitionTo('custom');
|
||||||
return findCustomWizard(customParams.id);
|
|
||||||
},
|
|
||||||
|
|
||||||
afterModel(model) {
|
|
||||||
return Ember.RSVP.hash({
|
|
||||||
info: ajax({
|
|
||||||
url: `/site/basic-info`,
|
|
||||||
type: 'GET',
|
|
||||||
}).then((result) => {
|
|
||||||
return model.set('siteInfo', result);
|
|
||||||
}),
|
|
||||||
settings: ajax({
|
|
||||||
url: `/site/settings`,
|
|
||||||
type: 'GET',
|
|
||||||
}).then((result) => {
|
|
||||||
Object.assign(Wizard.SiteSettings, result);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
setupController(controller, model) {
|
|
||||||
Ember.run.scheduleOnce('afterRender', this, function(){
|
|
||||||
$('body.custom-wizard').css('background', model.get('background'));
|
|
||||||
});
|
|
||||||
|
|
||||||
controller.setProperties({
|
|
||||||
customWizard: true,
|
|
||||||
siteInfo: model.get('siteInfo')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
StepModel.reopen({
|
|
||||||
save() {
|
|
||||||
const fields = {};
|
|
||||||
this.get('fields').forEach(f => fields[f.id] = f.value);
|
|
||||||
return ajax({
|
|
||||||
url: `/wizard/custom/${this.get('wizardId')}/steps/${this.get('id')}`,
|
|
||||||
type: 'PUT',
|
|
||||||
data: { fields }
|
|
||||||
}).catch(response => {
|
|
||||||
response.responseJSON.errors.forEach(err => this.fieldError(err.field, err.description));
|
|
||||||
throw response;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
StepRoute.reopen({
|
|
||||||
afterModel(model) {
|
|
||||||
if (!model) {
|
|
||||||
return document.location = getUrl("/");
|
|
||||||
}
|
|
||||||
|
|
||||||
const wizard = this.modelFor('application');
|
|
||||||
return model.set("wizardId", wizard.id);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -93,12 +40,17 @@ export default {
|
||||||
};
|
};
|
||||||
}.property('step.banner'),
|
}.property('step.banner'),
|
||||||
|
|
||||||
|
handleMessage: function() {
|
||||||
|
const message = this.get('step.message');
|
||||||
|
this.sendAction('showMessage', message);
|
||||||
|
}.observes('step.message'),
|
||||||
|
|
||||||
advance() {
|
advance() {
|
||||||
this.set('saving', true);
|
this.set('saving', true);
|
||||||
this.get('step').save()
|
this.get('step').save()
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (this.get('finalStep')) {
|
if (this.get('finalStep')) {
|
||||||
document.location = getUrl("/");
|
this.sendAction('finished', response);
|
||||||
} else {
|
} else {
|
||||||
this.sendAction('goNext', response);
|
this.sendAction('goNext', response);
|
||||||
}
|
}
|
||||||
|
@ -111,10 +63,61 @@ export default {
|
||||||
quit() {
|
quit() {
|
||||||
this.set('finalStep', true);
|
this.set('finalStep', true);
|
||||||
this.send('nextStep');
|
this.send('nextStep');
|
||||||
|
},
|
||||||
|
|
||||||
|
showMessage(message) {
|
||||||
|
this.sendAction('showMessage', message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
StepModel.reopen({
|
||||||
|
save() {
|
||||||
|
const wizardId = this.get('wizardId');
|
||||||
|
const fields = {};
|
||||||
|
this.get('fields').forEach(f => fields[f.id] = f.value);
|
||||||
|
return ajax({
|
||||||
|
url: `/w/${wizardId}/steps/${this.get('id')}`,
|
||||||
|
type: 'PUT',
|
||||||
|
data: { fields }
|
||||||
|
}).catch(response => {
|
||||||
|
if (response && response.responseJSON && response.responseJSON.errors) {
|
||||||
|
let wizardErrors = [];
|
||||||
|
response.responseJSON.errors.forEach(err => {
|
||||||
|
if (err.field === wizardId) {
|
||||||
|
wizardErrors.push(err.description);
|
||||||
|
} else if (err.field) {
|
||||||
|
this.fieldError(err.field, err.description);
|
||||||
|
} else if (err) {
|
||||||
|
wizardErrors.push(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (wizardErrors.length) {
|
||||||
|
this.handleWizardError(wizardErrors.join('\n'));
|
||||||
|
}
|
||||||
|
throw response;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response && response.responseText) {
|
||||||
|
const responseText = response.responseText;
|
||||||
|
const start = responseText.indexOf('>') + 1;
|
||||||
|
const end = responseText.indexOf('plugins');
|
||||||
|
const message = responseText.substring(start, end);
|
||||||
|
this.handleWizardError(message);
|
||||||
|
throw message;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleWizardError(message) {
|
||||||
|
this.set('message', {
|
||||||
|
state: 'error',
|
||||||
|
text: message
|
||||||
|
});
|
||||||
|
Ember.run.later(() => this.set('message', null), 6000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
FieldModel.reopen({
|
FieldModel.reopen({
|
||||||
check() {
|
check() {
|
||||||
let valid = this.get('valid');
|
let valid = this.get('valid');
|
||||||
|
|
|
@ -9,13 +9,16 @@ const CustomWizard = Ember.Object.extend({
|
||||||
});
|
});
|
||||||
|
|
||||||
export function findCustomWizard(wizardId) {
|
export function findCustomWizard(wizardId) {
|
||||||
return ajax({ url: `/wizard/custom/${wizardId}` }).then(result => {
|
return ajax({ url: `/w/${wizardId}` }).then(result => {
|
||||||
const wizard = result.wizard;
|
const wizard = result.wizard;
|
||||||
|
|
||||||
|
if (!wizard.completed) {
|
||||||
wizard.steps = wizard.steps.map(step => {
|
wizard.steps = wizard.steps.map(step => {
|
||||||
const stepObj = Step.create(step);
|
const stepObj = Step.create(step);
|
||||||
stepObj.fields = stepObj.fields.map(f => WizardField.create(f));
|
stepObj.fields = stepObj.fields.map(f => WizardField.create(f));
|
||||||
return stepObj;
|
return stepObj;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return CustomWizard.create(wizard);
|
return CustomWizard.create(wizard);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
import IndexRoute from 'wizard/routes/index';
|
export default Ember.Route.extend({
|
||||||
|
|
||||||
export default IndexRoute.extend({
|
|
||||||
beforeModel() {
|
beforeModel() {
|
||||||
const appModel = this.modelFor('application');
|
const appModel = this.modelFor('custom');
|
||||||
|
if (appModel.completed) {
|
||||||
|
this.set('completed', true);
|
||||||
|
} else if (appModel.start) {
|
||||||
this.replaceWith('custom.step', appModel.start);
|
this.replaceWith('custom.step', appModel.start);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setupController(controller) {
|
||||||
|
const completed = this.get('completed');
|
||||||
|
controller.set('completed', completed);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,3 +1,23 @@
|
||||||
import StepRoute from 'wizard/routes/step';
|
export default Ember.Route.extend({
|
||||||
|
model(params) {
|
||||||
|
const appModel = this.modelFor('custom');
|
||||||
|
const allSteps = appModel.steps;
|
||||||
|
if (allSteps) {
|
||||||
|
const step = allSteps.findBy('id', params.step_id);
|
||||||
|
return step ? step : allSteps[0];
|
||||||
|
};
|
||||||
|
|
||||||
export default StepRoute.extend();
|
return appModel;
|
||||||
|
},
|
||||||
|
|
||||||
|
afterModel(model) {
|
||||||
|
if (model.completed) return this.transitionTo('index');
|
||||||
|
return model.set("wizardId", this.modelFor('custom').id);
|
||||||
|
},
|
||||||
|
|
||||||
|
setupController(controller, step) {
|
||||||
|
controller.setProperties({
|
||||||
|
step, wizard: this.modelFor('custom')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
37
assets/javascripts/wizard/routes/custom.js.es6
Normale Datei
37
assets/javascripts/wizard/routes/custom.js.es6
Normale Datei
|
@ -0,0 +1,37 @@
|
||||||
|
import { findCustomWizard } from '../models/custom';
|
||||||
|
import { ajax } from 'wizard/lib/ajax';
|
||||||
|
import { getUrl } from 'discourse-common/lib/get-url';
|
||||||
|
|
||||||
|
export default Ember.Route.extend({
|
||||||
|
model(params) {
|
||||||
|
return findCustomWizard(params.wizard_id);
|
||||||
|
},
|
||||||
|
|
||||||
|
afterModel() {
|
||||||
|
return ajax({
|
||||||
|
url: `/site/settings`,
|
||||||
|
type: 'GET',
|
||||||
|
}).then((result) => {
|
||||||
|
Object.assign(Wizard.SiteSettings, result);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setupController(controller, model) {
|
||||||
|
Ember.run.scheduleOnce('afterRender', this, function(){
|
||||||
|
$('body.custom-wizard').css('background', model.get('background'));
|
||||||
|
});
|
||||||
|
|
||||||
|
controller.setProperties({
|
||||||
|
customWizard: true,
|
||||||
|
logoUrl: Wizard.SiteSettings.logo_small_url
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
finished(result) {
|
||||||
|
let url = "/";
|
||||||
|
if (result.topic_id) url += `t/${result.topic_id}`;
|
||||||
|
document.location.replace(getUrl(url));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,16 +1 @@
|
||||||
{{#if showCanvas}}
|
{{outlet}}
|
||||||
{{wizard-canvas}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class='wizard-column'>
|
|
||||||
<div class='wizard-column-contents'>
|
|
||||||
{{outlet}}
|
|
||||||
</div>
|
|
||||||
<div class='wizard-footer'>
|
|
||||||
{{#if customWizard}}
|
|
||||||
<img src="{{siteInfo.logo_small_url}}" style="background-image: initial; width: 33px; height: 33px;"/>
|
|
||||||
{{else}}
|
|
||||||
<div class='discourse-logo'></div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
{{textarea elementId=field.id value=field.value placeholder=field.placeholder tabindex="9"}}
|
|
16
assets/javascripts/wizard/templates/custom.hbs
Normale Datei
16
assets/javascripts/wizard/templates/custom.hbs
Normale Datei
|
@ -0,0 +1,16 @@
|
||||||
|
{{#if showCanvas}}
|
||||||
|
{{wizard-canvas}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<div class='wizard-column'>
|
||||||
|
<div class='wizard-column-contents'>
|
||||||
|
{{outlet}}
|
||||||
|
</div>
|
||||||
|
<div class='wizard-footer'>
|
||||||
|
{{#if customWizard}}
|
||||||
|
<img src="{{logoUrl}}" style="background-image: initial; width: 33px; height: 33px;"/>
|
||||||
|
{{else}}
|
||||||
|
<div class='discourse-logo'></div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
3
assets/javascripts/wizard/templates/custom.index.hbs
Normale Datei
3
assets/javascripts/wizard/templates/custom.index.hbs
Normale Datei
|
@ -0,0 +1,3 @@
|
||||||
|
{{#if completed}}
|
||||||
|
{{i18n 'wizard.completed'}}
|
||||||
|
{{/if}}
|
|
@ -1 +1,9 @@
|
||||||
{{wizard-step step=step wizard=wizard goNext="goNext" goBack="goBack"}}
|
<div class="step-message {{stepMessage.state}}">
|
||||||
|
{{stepMessage.text}}
|
||||||
|
</div>
|
||||||
|
{{wizard-step step=step
|
||||||
|
wizard=wizard
|
||||||
|
goNext="goNext"
|
||||||
|
goBack="goBack"
|
||||||
|
finished="finished"
|
||||||
|
showMessage="showMessage"}}
|
||||||
|
|
89
assets/stylesheets/wizard/wizard_custom.scss
Normale Datei
89
assets/stylesheets/wizard/wizard_custom.scss
Normale Datei
|
@ -0,0 +1,89 @@
|
||||||
|
.custom-wizard {
|
||||||
|
background-color: initial;
|
||||||
|
|
||||||
|
.wizard-step-description {
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard-column .wizard-step-banner {
|
||||||
|
width: initial;
|
||||||
|
max-width: 660px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-group {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
margin-right: 20px;
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 200px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard-step-form .wizard-btn {
|
||||||
|
display: block;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard-column .wizard-field .input-area {
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-list-box {
|
||||||
|
max-width: 550px;
|
||||||
|
position: relative;
|
||||||
|
margin: 10px 0;
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
position: absolute;
|
||||||
|
right: 50%;
|
||||||
|
top: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-text {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
border: 1px solid #e9e9e9;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
list-style: none;
|
||||||
|
height: 95px;
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
padding: 6px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #fff;
|
||||||
|
|
||||||
|
&:hover, &.selected {
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-results {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.default {
|
||||||
|
margin: 0 auto;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
color: #919191;
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,15 +13,17 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.wizard-header {
|
.wizard-header {
|
||||||
font-size: 1.3em;
|
font-size: 1.4em;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
|
|
||||||
&.medium {
|
&.medium {
|
||||||
font-size: 1.1em;
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.small {
|
&.small {
|
||||||
font-size: 0.97em;
|
font-size: 1em;
|
||||||
|
text-decoration: underline;
|
||||||
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +38,7 @@
|
||||||
.setting {
|
.setting {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
min-width: 49%;
|
width: 49%;
|
||||||
|
|
||||||
.setting-label {
|
.setting-label {
|
||||||
width: 90px;
|
width: 90px;
|
||||||
|
@ -49,6 +51,22 @@
|
||||||
&.full {
|
&.full {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons .error {
|
||||||
|
color: $danger;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons .remove {
|
||||||
|
float: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,19 +78,49 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wizard-column-contents {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.wizard-custom-step {
|
.wizard-custom-step {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
background-color: dark-light-diff($primary, $secondary, 96%, -65%);
|
background-color: dark-light-diff($primary, $secondary, 96%, -65%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.wizard-dropdown-choices {
|
.step-message {
|
||||||
margin-bottom: 25px;
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 0;
|
||||||
|
line-height: 0;
|
||||||
|
text-align: center;
|
||||||
|
transition: all .2s;
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
height: 60px;
|
||||||
|
line-height: 60px;
|
||||||
|
background-color: $success;
|
||||||
|
color: $secondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
height: 60px;
|
||||||
|
line-height: 60px;
|
||||||
|
background-color: $danger;
|
||||||
|
color: $secondary;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.wizard-dropdown-choice {
|
.wizard-dropdown-choices {
|
||||||
|
padding: 15px 15px 0 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
background-color: $secondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting .custom-input {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,38 +12,55 @@ en:
|
||||||
background_placeholder: "Background css property"
|
background_placeholder: "Background css property"
|
||||||
save_submissions: "Save"
|
save_submissions: "Save"
|
||||||
save_submissions_label: "Save wizard submissions"
|
save_submissions_label: "Save wizard submissions"
|
||||||
|
multiple_submissions: "Multiple"
|
||||||
|
multiple_submissions_label: "Allow multiple submissions by the same user"
|
||||||
save: "Save Changes"
|
save: "Save Changes"
|
||||||
remove: "Delete Wizard"
|
remove: "Delete Wizard"
|
||||||
header: "Wizard"
|
header: "Wizard"
|
||||||
add: "Add"
|
add: "Add"
|
||||||
url: "Url"
|
url: "Url"
|
||||||
translation: "Translation"
|
key: "Key"
|
||||||
|
id: "Id"
|
||||||
|
id_placeholder: "Underscored. Cannot be changed."
|
||||||
|
key_placeholder: "Translation key"
|
||||||
|
custom_text_placeholder: "Overrides translation"
|
||||||
|
type: "Type"
|
||||||
|
error:
|
||||||
|
name_required: "Wizards must have a name."
|
||||||
|
steps_required: "Wizards must have at least one step."
|
||||||
|
id_required: "All Step, Fields and Actions need an Id"
|
||||||
|
field:
|
||||||
|
need_choices: "All dropdowns need a translated choices, custom choies or preset choices."
|
||||||
|
choices_label_empty: "Custom choice labels cannot be empty."
|
||||||
step:
|
step:
|
||||||
header: "Steps"
|
header: "Steps"
|
||||||
title: "Title"
|
title: "Title"
|
||||||
title_placeholder: "Overrides title translation"
|
|
||||||
banner: "Banner"
|
banner: "Banner"
|
||||||
banner_placeholder: "Image url"
|
banner_placeholder: "Image url"
|
||||||
description: "Description"
|
description: "Description"
|
||||||
description_placeholder: "Overrides description translation"
|
|
||||||
translation_placeholder: "Translation key for step"
|
|
||||||
|
|
||||||
field:
|
field:
|
||||||
header: "Fields"
|
header: "Fields"
|
||||||
label: "Label"
|
label: "Label"
|
||||||
description: "Description"
|
description: "Description"
|
||||||
type: "Type"
|
choices_label: "Dropdown Choices (use one type)"
|
||||||
choices_label: "Dropdown Choices"
|
choices_translation: "Translation"
|
||||||
add_choice: "Add"
|
choices_custom: "Custom"
|
||||||
|
choices_preset:
|
||||||
|
label: "Preset"
|
||||||
|
none: "Select a data type"
|
||||||
|
categories: "Categories"
|
||||||
|
filter: "Filter"
|
||||||
|
key: "Key"
|
||||||
|
value: "Value"
|
||||||
|
choice:
|
||||||
|
value: "Value"
|
||||||
|
label: "Label"
|
||||||
required: "Required"
|
required: "Required"
|
||||||
required_label: "Field is Required"
|
required_label: "Field is Required"
|
||||||
translation_placeholder: "Translation key for field"
|
min_length: "Min Length"
|
||||||
|
min_length_placeholder: "Minimum length in characters"
|
||||||
action:
|
action:
|
||||||
header: "Actions"
|
header: "Actions"
|
||||||
label: "Label"
|
|
||||||
type: "Type"
|
|
||||||
send_message:
|
send_message:
|
||||||
label: "Send Message"
|
label: "Send Message"
|
||||||
title: "Title"
|
title: "Title"
|
||||||
|
@ -62,5 +79,4 @@ en:
|
||||||
|
|
||||||
wizard_js:
|
wizard_js:
|
||||||
wizard:
|
wizard:
|
||||||
validation:
|
completed: "You have completed this wizard."
|
||||||
too_short: "Post must be at least {{min}} characters"
|
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
en:
|
en:
|
||||||
custom_wizard:
|
wizard:
|
||||||
title: "Wizard"
|
custom_title: "Wizard"
|
||||||
|
field:
|
||||||
new_wizard:
|
too_short: "%{label} must be at least %{min} characters"
|
||||||
step_1:
|
|
||||||
title: "Translated title"
|
|
||||||
description: "Translated description"
|
|
||||||
field_1:
|
|
||||||
label: "Translated field title"
|
|
||||||
description: "Translated field description"
|
|
||||||
|
|
126
lib/builder.rb
126
lib/builder.rb
|
@ -1,12 +1,17 @@
|
||||||
class CustomWizard::Builder
|
class CustomWizard::Builder
|
||||||
|
|
||||||
|
attr_accessor :wizard, :updater, :submission
|
||||||
|
|
||||||
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
|
id: wizard_id,
|
||||||
@wizard.save_submissions = data['save_submissions']
|
save_submissions: data['save_submissions'],
|
||||||
@wizard.background = data["background"]
|
multiple_submissions: data['multiple_submissions'],
|
||||||
|
background: data["background"],
|
||||||
|
custom: true
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.sorted_handlers
|
def self.sorted_handlers
|
||||||
|
@ -23,13 +28,16 @@ class CustomWizard::Builder
|
||||||
end
|
end
|
||||||
|
|
||||||
def build
|
def build
|
||||||
|
unless (@wizard.completed? && !@custom_wizard.respond_to?(:multiple_submissions)) ||
|
||||||
|
!@custom_wizard.steps
|
||||||
@custom_wizard.steps.each do |s|
|
@custom_wizard.steps.each do |s|
|
||||||
@wizard.append_step(s['id']) do |step|
|
@wizard.append_step(s['id']) do |step|
|
||||||
step.title = s['title'] if s['title']
|
step.title = s['title'] if s['title']
|
||||||
step.description = s['description'] if s['description']
|
step.description = s['description'] if s['description']
|
||||||
step.banner = s['banner'] if s['banner']
|
step.banner = s['banner'] if s['banner']
|
||||||
step.translation_key = s['translation_key'] if s['translation_key']
|
step.key = s['key'] if s['key']
|
||||||
|
|
||||||
|
if s['fields'] && s['fields'].length
|
||||||
s['fields'].each do |f|
|
s['fields'].each do |f|
|
||||||
params = {
|
params = {
|
||||||
id: f['id'],
|
id: f['id'],
|
||||||
|
@ -39,44 +47,76 @@ class CustomWizard::Builder
|
||||||
|
|
||||||
params[:label] = f['label'] if f['label']
|
params[:label] = f['label'] if f['label']
|
||||||
params[:description] = f['description'] if f['description']
|
params[:description] = f['description'] if f['description']
|
||||||
params[:translation_key] = f['translation_key'] if f['translation_key']
|
params[:key] = f['key'] if f['key']
|
||||||
|
|
||||||
|
submissions = Array.wrap(PluginStore.get("custom_wizard_submissions", @wizard.id))
|
||||||
|
if submissions.last && submissions.last['completed'] === false
|
||||||
|
@submission = submissions.last
|
||||||
|
params[:value] = @submission[f['id']] if @submission[f['id']]
|
||||||
|
end
|
||||||
|
|
||||||
field = step.add_field(params)
|
field = step.add_field(params)
|
||||||
|
|
||||||
if f['type'] == 'dropdown'
|
if f['type'] === 'dropdown'
|
||||||
|
if f['choices'] && f['choices'].length > 0
|
||||||
f['choices'].each do |c|
|
f['choices'].each do |c|
|
||||||
field.add_choice(c['id'], label: c['label'])
|
field.add_choice(c['value'], label: c['label'])
|
||||||
|
end
|
||||||
|
elsif f['choices_key'] && f['choices_key'].length > 0
|
||||||
|
choices = I18n.t(f['choices_key'])
|
||||||
|
if choices.is_a?(Hash)
|
||||||
|
choices.each do |k, v|
|
||||||
|
field.add_choice(k, label: v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elsif f['choices_preset'] && f['choices_preset'].length > 0
|
||||||
|
objects = []
|
||||||
|
|
||||||
|
if f['choices_preset'] === 'categories'
|
||||||
|
objects = Site.new(Guardian.new(@wizard.user)).categories
|
||||||
|
end
|
||||||
|
|
||||||
|
if f['choices_filters'] && f['choices_filters'].length > 0
|
||||||
|
f['choices_filters'].each do |f|
|
||||||
|
objects.reject! { |o| o[f['key']] != f['value'] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if objects.length > 0
|
||||||
|
objects.each do |o|
|
||||||
|
field.add_choice(o.id, label: o.name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
step.on_update do |updater|
|
step.on_update do |updater|
|
||||||
|
|
||||||
@updater = updater
|
@updater = updater
|
||||||
input = updater.fields
|
input = updater.fields
|
||||||
user = @wizard.user
|
user = @wizard.user
|
||||||
|
|
||||||
if @wizard.save_submissions && input
|
if s['fields'] && s['fields'].length
|
||||||
store_key = @wizard.id
|
s['fields'].each do |f|
|
||||||
submissions = Array.wrap(PluginStore.get("custom_wizard_submissions", store_key))
|
value = input[f['id']]
|
||||||
submission = {}
|
min_length = f['min_length']
|
||||||
|
if min_length && value.is_a?(String) && value.length < min_length.to_i
|
||||||
if submissions.last && submissions.last['completed'] === false
|
label = f['label'] || I18n.t("#{f['key']}.label")
|
||||||
submission = submissions.last
|
updater.errors.add(f['id'].to_s, I18n.t('wizard.field.too_short', label: label, min: min_length.to_i))
|
||||||
submissions.pop(1)
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
submission['user_id'] = @wizard.user.id
|
next if updater.errors.any?
|
||||||
submission['completed'] = updater.step.next.nil?
|
|
||||||
|
|
||||||
input.each do |key, value|
|
CustomWizard::Builder.step_handlers.each do |handler|
|
||||||
submission[key] = value
|
if handler[:wizard_id] == @wizard.id
|
||||||
|
handler[:block].call(self)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
submissions.push(submission)
|
next if updater.errors.any?
|
||||||
|
|
||||||
PluginStore.set('custom_wizard_submissions', store_key, submissions)
|
|
||||||
end
|
|
||||||
|
|
||||||
if s['actions'] && s['actions'].length
|
if s['actions'] && s['actions'].length
|
||||||
s['actions'].each do |a|
|
s['actions'].each do |a|
|
||||||
|
@ -89,11 +129,11 @@ class CustomWizard::Builder
|
||||||
|
|
||||||
post = creator.create
|
post = creator.create
|
||||||
if creator.errors.present?
|
if creator.errors.present?
|
||||||
raise StandardError, creator.errors.full_messages.join(" ")
|
updater.errors.add(:create_topic, creator.errors.full_messages.join(" "))
|
||||||
end
|
else
|
||||||
|
|
||||||
updater.result = { topic_id: post.topic.id }
|
updater.result = { topic_id: post.topic.id }
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if a['type'] === 'send_message'
|
if a['type'] === 'send_message'
|
||||||
creator = PostCreator.new(user,
|
creator = PostCreator.new(user,
|
||||||
|
@ -105,17 +145,35 @@ class CustomWizard::Builder
|
||||||
post = creator.create
|
post = creator.create
|
||||||
|
|
||||||
if creator.errors.present?
|
if creator.errors.present?
|
||||||
raise StandardError, creator.errors.full_messages.join(" ")
|
updater.errors.add(:send_message, creator.errors.full_messages.join(" "))
|
||||||
end
|
else
|
||||||
|
|
||||||
updater.result = { topic_id: post.topic.id }
|
updater.result = { topic_id: post.topic.id }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
CustomWizard::Builder.step_handlers.each do |handler|
|
if @wizard.save_submissions && updater.errors.empty?
|
||||||
if handler[:wizard_id] == @wizard.id
|
store_key = @wizard.id
|
||||||
handler[:block].call(self)
|
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?
|
||||||
|
|
||||||
|
if input
|
||||||
|
input.each do |key, value|
|
||||||
|
submission[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
submissions.push(submission)
|
||||||
|
PluginStore.set('custom_wizard_submissions', store_key, submissions)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class CustomWizard::Field
|
class CustomWizard::Field
|
||||||
def self.types
|
def self.types
|
||||||
@types ||= ['dropdown', 'image', 'radio', 'text', 'textarea', 'composer']
|
@types ||= ['dropdown', 'image', 'radio', 'text', 'textarea']
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.require_assets
|
def self.require_assets
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class CustomWizard::Wizard
|
class CustomWizard::Wizard
|
||||||
|
|
||||||
attr_reader :id, :name, :steps, :background, :save_submissions, :custom
|
attr_reader :id, :name, :steps, :background, :save_submissions, :multiple_submissions, :custom
|
||||||
|
|
||||||
def initialize(data)
|
def initialize(data)
|
||||||
data = data.is_a?(String) ? ::JSON.parse(data) : data
|
data = data.is_a?(String) ? ::JSON.parse(data) : data
|
||||||
|
@ -8,6 +8,7 @@ class CustomWizard::Wizard
|
||||||
@name = data['name']
|
@name = data['name']
|
||||||
@background = data['background']
|
@background = data['background']
|
||||||
@save_submissions = data['save_submissions']
|
@save_submissions = data['save_submissions']
|
||||||
|
@multiple_submissions = data['multiple_submissions']
|
||||||
@steps = data['steps']
|
@steps = data['steps']
|
||||||
@custom = true
|
@custom = true
|
||||||
end
|
end
|
||||||
|
|
114
plugin.rb
114
plugin.rb
|
@ -3,10 +3,11 @@
|
||||||
# version: 0.1
|
# version: 0.1
|
||||||
# authors: Angus McLeod
|
# authors: Angus McLeod
|
||||||
|
|
||||||
register_asset 'stylesheets/custom_wizard.scss'
|
register_asset 'stylesheets/wizard_custom_admin.scss'
|
||||||
|
|
||||||
config = Rails.application.config
|
config = Rails.application.config
|
||||||
config.assets.paths << Rails.root.join("plugins", "discourse-custom-wizard", "assets", "javascripts")
|
config.assets.paths << Rails.root.join("plugins", "discourse-custom-wizard", "assets", "javascripts")
|
||||||
|
config.assets.paths << Rails.root.join("plugins", "discourse-custom-wizard", "assets", "stylesheets", "wizard")
|
||||||
|
|
||||||
after_initialize do
|
after_initialize do
|
||||||
require_dependency "application_controller"
|
require_dependency "application_controller"
|
||||||
|
@ -33,9 +34,7 @@ after_initialize do
|
||||||
|
|
||||||
require_dependency 'admin_constraint'
|
require_dependency 'admin_constraint'
|
||||||
Discourse::Application.routes.append do
|
Discourse::Application.routes.append do
|
||||||
namespace :wizard do
|
mount ::CustomWizard::Engine, at: 'w'
|
||||||
mount ::CustomWizard::Engine, at: 'custom'
|
|
||||||
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' => 'admin#index'
|
||||||
|
@ -52,21 +51,61 @@ after_initialize do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class ::Wizard
|
require_dependency 'wizard'
|
||||||
attr_accessor :id, :background, :save_submissions
|
require_dependency 'wizard/step'
|
||||||
end
|
require_dependency 'wizard/step_updater'
|
||||||
|
|
||||||
class ::Wizard::Step
|
|
||||||
attr_accessor :title, :description, :translation_key
|
|
||||||
end
|
|
||||||
|
|
||||||
class ::Wizard::StepUpdater
|
|
||||||
attr_accessor :result, :step
|
|
||||||
end
|
|
||||||
|
|
||||||
require_dependency 'wizard/field'
|
require_dependency 'wizard/field'
|
||||||
Wizard::Field.class_eval do
|
|
||||||
attr_reader :label, :description, :translation_key
|
::Wizard.class_eval do
|
||||||
|
attr_accessor :id, :background, :save_submissions, :multiple_submissions
|
||||||
|
|
||||||
|
def initialize(user, attrs = {})
|
||||||
|
@steps = []
|
||||||
|
@user = user
|
||||||
|
@first_step = nil
|
||||||
|
@max_topics_to_require_completion = 15
|
||||||
|
@id = attrs[:id] if attrs[:id]
|
||||||
|
@save_submissions = attrs[:save_submissions] if attrs[:save_submissions]
|
||||||
|
@multiple_submissions = attrs[:multiple_submissions] if attrs[:multiple_submissions]
|
||||||
|
@background = attrs[:background] if attrs[:background]
|
||||||
|
@custom = attrs[:custom] if attrs[:custom]
|
||||||
|
end
|
||||||
|
|
||||||
|
def completed?
|
||||||
|
completed_steps?(@steps.map(&:id))
|
||||||
|
end
|
||||||
|
|
||||||
|
def completed_steps?(steps)
|
||||||
|
steps = [steps].flatten.uniq
|
||||||
|
|
||||||
|
completed = UserHistory.where(
|
||||||
|
acting_user_id: @user.id,
|
||||||
|
action: UserHistory.actions[:wizard_step]
|
||||||
|
).where(context: steps)
|
||||||
|
.distinct.order(:context).pluck(:context)
|
||||||
|
|
||||||
|
steps.sort == completed
|
||||||
|
end
|
||||||
|
|
||||||
|
def start
|
||||||
|
completed = UserHistory.where(
|
||||||
|
acting_user_id: @user.id,
|
||||||
|
action: UserHistory.actions[:wizard_step]
|
||||||
|
).where(context: @steps.map(&:id))
|
||||||
|
.uniq.pluck(:context)
|
||||||
|
|
||||||
|
# First uncompleted step
|
||||||
|
steps = @custom ? @steps : steps_with_fields
|
||||||
|
steps.each do |s|
|
||||||
|
return s unless completed.include?(s.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
@first_step
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
::Wizard::Field.class_eval do
|
||||||
|
attr_reader :label, :description, :key, :min_length
|
||||||
|
|
||||||
def initialize(attrs)
|
def initialize(attrs)
|
||||||
attrs = attrs || {}
|
attrs = attrs || {}
|
||||||
|
@ -76,14 +115,23 @@ after_initialize do
|
||||||
@required = !!attrs[:required]
|
@required = !!attrs[:required]
|
||||||
@label = attrs[:label]
|
@label = attrs[:label]
|
||||||
@description = attrs[:description]
|
@description = attrs[:description]
|
||||||
@translation_key = attrs[:translation_key]
|
@key = attrs[:key]
|
||||||
|
@min_length = attrs[:min_length]
|
||||||
@value = attrs[:value]
|
@value = attrs[:value]
|
||||||
@choices = []
|
@choices = []
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class ::Wizard::Step
|
||||||
|
attr_accessor :title, :description, :key
|
||||||
|
end
|
||||||
|
|
||||||
|
class ::Wizard::StepUpdater
|
||||||
|
attr_accessor :result, :step
|
||||||
|
end
|
||||||
|
|
||||||
::WizardSerializer.class_eval do
|
::WizardSerializer.class_eval do
|
||||||
attributes :id, :background
|
attributes :id, :background, :completed
|
||||||
|
|
||||||
def id
|
def id
|
||||||
object.id
|
object.id
|
||||||
|
@ -93,32 +141,48 @@ after_initialize do
|
||||||
object.background
|
object.background
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def completed
|
||||||
|
object.completed?
|
||||||
|
end
|
||||||
|
|
||||||
|
def include_completed?
|
||||||
|
object.completed? && !object.multiple_submissions && !scope.current_user.admin?
|
||||||
|
end
|
||||||
|
|
||||||
def include_start?
|
def include_start?
|
||||||
object.start
|
object.start && include_steps?
|
||||||
|
end
|
||||||
|
|
||||||
|
def include_steps?
|
||||||
|
!include_completed?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
::WizardStepSerializer.class_eval do
|
::WizardStepSerializer.class_eval do
|
||||||
def title
|
def title
|
||||||
return object.title if object.title
|
return object.title if object.title
|
||||||
I18n.t("#{object.translation_key || i18n_key}.title", default: '')
|
I18n.t("#{object.key || i18n_key}.title", default: '')
|
||||||
end
|
end
|
||||||
|
|
||||||
def description
|
def description
|
||||||
return object.description if object.description
|
return object.description if object.description
|
||||||
I18n.t("#{object.translation_key || i18n_key}.description", default: '')
|
I18n.t("#{object.key || i18n_key}.description", default: '')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
::WizardFieldSerializer.class_eval do
|
::WizardFieldSerializer.class_eval do
|
||||||
def label
|
def label
|
||||||
return object.label if object.label
|
return object.label if object.label
|
||||||
I18n.t("#{object.translation_key || i18n_key}.label", default: '')
|
I18n.t("#{object.key || i18n_key}.label", default: '')
|
||||||
end
|
end
|
||||||
|
|
||||||
def description
|
def description
|
||||||
return object.description if object.description
|
return object.description if object.description
|
||||||
I18n.t("#{object.translation_key || i18n_key}.description", default: '')
|
I18n.t("#{object.key || i18n_key}.description", default: '')
|
||||||
|
end
|
||||||
|
|
||||||
|
def placeholder
|
||||||
|
I18n.t("#{object.key || i18n_key}.placeholder", default: '')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
.custom-wizard {
|
|
||||||
background-color: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-wizard .wizard-step-description {
|
|
||||||
line-height: 1.7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-wizard .wizard-column .wizard-step-banner {
|
|
||||||
width: initial;
|
|
||||||
max-width: 660px;
|
|
||||||
}
|
|
Laden …
In neuem Issue referenzieren