0
0
Fork 1
Spiegel von https://github.com/paviliondev/discourse-custom-wizard.git synchronisiert 2024-11-22 09:20:29 +01:00

Custom forms display steps, just don't save data yet

Dieser Commit ist enthalten in:
Angus McLeod 2017-09-29 19:27:03 +08:00
Ursprung 3fa2735c63
Commit f8848d2b93
31 geänderte Dateien mit 338 neuen und 110 gelöschten Zeilen

Datei anzeigen

@ -1,6 +1,6 @@
class CustomWizard::AdminController < ::ApplicationController class CustomWizard::AdminController < ::ApplicationController
before_filter :ensure_logged_in before_action :ensure_logged_in
before_filter :ensure_admin before_action :ensure_admin
def index def index
render nothing: true render nothing: true
@ -11,9 +11,20 @@ class CustomWizard::AdminController < ::ApplicationController
wizard = ::JSON.parse(params[:wizard]) wizard = ::JSON.parse(params[:wizard])
wizard["id"] = SecureRandom.hex(8) if !wizard["id"] 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
PluginStore.set('custom_wizards', wizard["id"], wizard) unless saved
PluginStore.set('custom_wizard', wizard["id"], wizard)
end
render json: success_json render json: success_json
end end
@ -21,7 +32,7 @@ class CustomWizard::AdminController < ::ApplicationController
def remove def remove
params.require(:id) params.require(:id)
PluginStore.remove('custom_wizards', params[:id]) PluginStore.remove('custom_wizard', params[:id])
render json: success_json render json: success_json
end end
@ -29,13 +40,13 @@ class CustomWizard::AdminController < ::ApplicationController
def find def find
params.require(:id) params.require(:id)
wizard = PluginStore.get('custom_wizards', params[:id]) wizard = PluginStore.get('custom_wizard', params[:id])
render json: success_json.merge(wizard: wizard) render json: success_json.merge(wizard: wizard)
end end
def all def all
rows = PluginStoreRow.where(plugin_name: 'custom_wizards').order(:id) rows = PluginStoreRow.where(plugin_name: 'custom_wizard').order(:id)
wizards = rows ? [*rows].map do |r| wizards = rows ? [*rows].map do |r|
CustomWizard::Wizard.new(r.value) CustomWizard::Wizard.new(r.value)

Datei anzeigen

@ -1,9 +1,9 @@
class StepsController < ApplicationController class CustomWizard::StepsController < ApplicationController
before_filter :ensure_logged_in before_action :ensure_logged_in
def update def update
wizard = CustomWizard::Builder.new(current_user, params[:wizard_id]).build wizard = CustomWizard::Builder.new(current_user, params[:wizard_id]).build
updater = wizard.create_updater(params[:id], params[:fields]) updater = wizard.create_updater(params[:step_id], params[:fields])
updater.update updater.update
if updater.success? if updater.success?
@ -18,5 +18,4 @@ class StepsController < ApplicationController
render json: { errors: errors }, status: 422 render json: { errors: errors }, status: 422
end end
end end
end end

Datei anzeigen

@ -6,7 +6,7 @@ class CustomWizard::WizardController < ::ApplicationController
def index def index
respond_to do |format| respond_to do |format|
format.json do format.json do
wizard = CustomWizard::Builder.new(current_user, params[:name]).build wizard = CustomWizard::Builder.new(current_user, params[:wizard_id]).build
render_serialized(wizard, WizardSerializer) render_serialized(wizard, WizardSerializer)
end end
format.html {} format.html {}

Datei anzeigen

@ -1,5 +1,6 @@
<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 %>
<%= preload_script "ember_jquery" %> <%= preload_script "ember_jquery" %>
<%= preload_script "wizard-vendor" %> <%= preload_script "wizard-vendor" %>
@ -16,12 +17,12 @@
<title><%= t 'custom_wizard.title' %></title> <title><%= t 'custom_wizard.title' %></title>
</head> </head>
<body class='wizard'> <body class='custom-wizard'>
<div id='wizard-main'></div> <div id='custom-wizard-main'></div>
<script> <script>
(function() { (function() {
var wizard = require('wizard/wizard').default.create(); var wizard = require('discourse/plugins/discourse-custom-wizard/wizard/custom-wizard').default.create();
wizard.start(); wizard.start();
})(); })();
</script> </script>

Datei anzeigen

@ -4,17 +4,15 @@ export default Ember.Component.extend({
classNames: 'wizard-custom-step', classNames: 'wizard-custom-step',
@computed('step.fields.@each.id') @computed('step.fields.@each.id')
allowAddAction(stepFields) { allowAddAction: stepFields => stepFields.get('firstObject.id'),
return stepFields.get('firstObject.id');
},
actions: { actions: {
addField() { addField() {
this.get('step.fields').pushObject(Ember.Object.create()); this.get('step.fields').pushObject(Ember.Object.create({ id: '', label: '' }));
}, },
addAction() { addAction() {
this.get('step.actions').pushObject(Ember.Object.create()); this.get('step.actions').pushObject(Ember.Object.create({ id: '', label: '' }));
}, },
removeField(field) { removeField(field) {

Datei anzeigen

@ -13,10 +13,10 @@ export default Ember.Controller.extend({
}, },
addStep() { addStep() {
this.get('model.steps').pushObject({ this.get('model.steps').pushObject(Ember.Object.create({
fields: Ember.A(), fields: Ember.A(),
actions: Ember.A() actions: Ember.A()
}); }));
}, },
removeStep(step) { removeStep(step) {

Datei anzeigen

@ -3,7 +3,7 @@ export default {
map() { map() {
this.route('adminWizards', { path: '/wizards', resetNamespace: true }, function() { this.route('adminWizards', { path: '/wizards', resetNamespace: true }, function() {
this.route('adminWizardsCustom', { path: '/custom', resetNamespace: true }, function() { this.route('adminWizardsCustom', { path: '/custom', resetNamespace: true }, function() {
this.route('adminWizard', { path: '/:name', resetNamespace: true }); this.route('adminWizard', { path: '/:wizard_id', resetNamespace: true });
}); });
}); });
} }

Datei anzeigen

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

Datei anzeigen

@ -1,22 +1,55 @@
import { ajax } from 'discourse/lib/ajax';
import { default as computed } from 'ember-addons/ember-computed-decorators'; import { default as computed } from 'ember-addons/ember-computed-decorators';
import { ajax } from 'discourse/lib/ajax';
const CustomWizard = Discourse.Model.extend({ const CustomWizard = Discourse.Model.extend({
steps: Ember.A(), init() {
const id = this.get('id');
if (id) this.set('existingId', id);
},
@computed('name') @computed('name')
dasherizedName(name) { id(name) {
return Ember.String.dasherize(name); return name ? Ember.String.dasherize(name) : null;
}, },
save() { save() {
const wizard = { const stepsObj = this.get('steps');
id: this.get('id'), let steps = [];
steps: this.get('steps').toArray(),
name: this.get('name') stepsObj.forEach((s) => {
let step = {
id: Ember.String.dasherize(s.title),
title: s.title,
banner: s.banner,
description: s.description,
fields: [],
actions: []
};
const fields = s.get('fields');
fields.forEach((f) => {
f.set('id', Ember.String.dasherize(f.get('label')));
step['fields'].push(f);
});
s.actions.forEach((a) => {
a['id'] = Ember.String.dasherize(a.label);
step['actions'].push(a);
});
steps.push(step);
});
const id = this.get('id');
const name = this.get('name');
let wizard = { id, name, steps };
const existingId = this.get('existingId');
if (existingId && existingId !== id) {
wizard['existing_id'] = existingId;
}; };
return ajax(`/admin/wizards/custom/save`, { return ajax("/admin/wizards/custom/save", {
type: 'PUT', type: 'PUT',
data: { data: {
wizard: JSON.stringify(wizard) wizard: JSON.stringify(wizard)
@ -25,7 +58,7 @@ const CustomWizard = Discourse.Model.extend({
}, },
remove() { remove() {
return ajax(`/admin/wizards/custom/remove`, { return ajax("/admin/wizards/custom/remove", {
type: 'DELETE', type: 'DELETE',
data: { data: {
id: this.get('id') id: this.get('id')
@ -36,20 +69,53 @@ const CustomWizard = Discourse.Model.extend({
CustomWizard.reopenClass({ CustomWizard.reopenClass({
findAll() { findAll() {
return ajax("/admin/wizards/custom/all").then(result => { return ajax("/admin/wizards/custom/all", {
type: 'GET'
}).then(result => {
return result.wizards.map(w => CustomWizard.create(w)); return result.wizards.map(w => CustomWizard.create(w));
}); });
}, },
create() { create(w) {
const wizard = this._super.apply(this, arguments); const wizard = this._super.apply(this);
const steps = wizard.get('steps');
steps.forEach((s) => { let steps = Ember.A();
s.fields = Ember.A(s.fields); let props = { steps };
s.fields.forEach((f) => f.choices = Ember.A(f.choices));
s.actions = Ember.A(s.actions); if (w) {
}); props['id'] = w.id; props['name'] = w.name;
if (w.steps) {
w.steps.forEach((s) => {
let fields = Ember.A();
s.fields.forEach((f) => {
let choices = Ember.A();
f.choices.forEach((c) => {
choices.pushObject(Ember.Object.create(c));
});
fields.pushObject(Ember.Object.create(f));
});
let actions = Ember.A();
s.actions.forEach((a) => {
actions.pushObject(Ember.Object.create(a));
});
steps.pushObject(Ember.Object.create({
id: s.id,
title: s.title,
description: s.description,
fields,
actions
}));
});
}
};
wizard.setProperties(props);
return wizard; return wizard;
} }

Datei anzeigen

@ -2,15 +2,13 @@ import CustomWizard from '../models/custom-wizard';
export default Discourse.Route.extend({ export default Discourse.Route.extend({
model(params) { model(params) {
if (params.name === 'new') { if (params.wizard_id === 'new') {
this.set('new', true); this.set('new', true);
return CustomWizard.create({ name: '', steps: []}); return CustomWizard.create();
} }
this.set('new', false); this.set('new', false);
const wizard = this.modelFor('admin-wizards-custom').findBy('dasherizedName', params.name); const wizard = this.modelFor('admin-wizards-custom').findBy('id', params.wizard_id);
if (!wizard) return this.transitionTo('adminWizardsCustom.index'); if (!wizard) return this.transitionTo('adminWizardsCustom.index');
return wizard; return wizard;

Datei anzeigen

@ -1,5 +1,4 @@
<div class="form-horizontal"> <div class="form-horizontal">
<div> <div>
<label for="name">{{i18n 'admin.wizard.name'}}</label> <label for="name">{{i18n 'admin.wizard.name'}}</label>
{{text-field name="name" value=model.name placeholderKey="admin.wizard.name_placeholder"}} {{text-field name="name" value=model.name placeholderKey="admin.wizard.name_placeholder"}}
@ -21,5 +20,4 @@
{{/unless}} {{/unless}}
<span class="saving {{unless savingStatus 'hidden'}}">{{savingStatus}}</span> <span class="saving {{unless savingStatus 'hidden'}}">{{savingStatus}}</span>
</div> </div>
</div> </div>

Datei anzeigen

@ -3,7 +3,7 @@
<ul> <ul>
{{#each model as |w|}} {{#each model as |w|}}
<li> <li>
{{#link-to "adminWizard" w.dasherizedName}}{{w.name}}{{/link-to}} {{#link-to "adminWizard" w.id}}{{w.name}}{{/link-to}}
</li> </li>
{{/each}} {{/each}}
</ul> </ul>

Datei anzeigen

@ -0,0 +1 @@
{{input type='text' value=choice.label}}

Datei anzeigen

@ -14,7 +14,7 @@
{{#if isDropdown}} {{#if isDropdown}}
<span>{{i18n 'admin.wizard.field.choices_label'}}</span> <span>{{i18n 'admin.wizard.field.choices_label'}}</span>
{{#each field.choices as |c|}} {{#each field.choices as |c|}}
{{input type='text' value=c.label}} {{wizard-custom-choice choice=c}}
{{/each}} {{/each}}
{{d-button action='addChoice' label='admin.wizard.field.add_choice'}} {{d-button action='addChoice' label='admin.wizard.field.add_choice'}}
{{/if}} {{/if}}

Datei anzeigen

@ -1,3 +1,6 @@
//= require ./wizard/custom-wizard
//= require_tree ./wizard/controllers
//= require_tree ./wizard/initializers //= require_tree ./wizard/initializers
//= require_tree ./wizard/models //= require_tree ./wizard/models
//= require_tree ./wizard/routes //= require_tree ./wizard/routes
//= require_tree ./wizard/templates

Datei anzeigen

@ -0,0 +1,12 @@
{{#if showCanvas}}
{{wizard-canvas}}
{{/if}}
<div class='wizard-column'>
<div class='wizard-column-contents'>
{{outlet}}
</div>
<div class='wizard-footer'>
<div class='discourse-logo'></div>
</div>
</div>

Datei anzeigen

@ -0,0 +1,20 @@
import StepController from 'wizard/controllers/step';
import getUrl from 'discourse-common/lib/get-url';
export default StepController.extend({
actions: {
goNext(response) {
const next = this.get('step.next');
if (response.refresh_required) {
const id = this.get('wizard.id');
document.location = getUrl(`/wizard/custom/${id}/steps/${next}`);
} else {
this.transitionToRoute('custom.step', next);
}
},
goBack() {
this.transitionToRoute('custom.step', this.get('step.previous'));
}
}
});

Datei anzeigen

@ -0,0 +1,5 @@
import WizardApplication from 'wizard/wizard';
export default WizardApplication.extend({
rootElement: '#custom-wizard-main'
});

Datei anzeigen

@ -1,13 +1,66 @@
import Router from 'wizard/router';
export default { export default {
name: 'custom-routes', name: 'custom-routes',
initialize() { initialize(container, app) {
if (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 ajax = requirejs('wizard/lib/ajax').ajax;
const StepRoute = requirejs('wizard/routes/step').default;
const StepModel = requirejs('wizard/models/step').default;
Router.map(function() { Router.map(function() {
this.route('custom', { path: '/custom/:name' }, function() { this.route('custom', { path: '/custom/:id' }, function() {
this.route('step', { path: '/steps/:step_id' }); this.route('step', { path: '/steps/:step_id' });
}); });
}); });
WizardApplicationRoute.reopen({
model() {
const customParams = this.paramsFor('custom');
return findCustomWizard(customParams.id);
},
afterModel(model) {
return ajax({
url: `/site/basic-info`,
type: 'GET',
}).then((result) => {
return model.set('siteInfo', result);
});
},
setupController(controller, model) {
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) {
const wizard = this.modelFor('application');
return model.set("wizardId", wizard.id);
}
});
} }
}; };

Datei anzeigen

@ -1,22 +1,26 @@
import Step from 'wizard/models/step'; import { default as computed } from 'ember-addons/ember-computed-decorators';
import WizardField from 'wizard/models/wizard-field'; import WizardField from 'wizard/models/wizard-field';
import { ajax } from 'wizard/lib/ajax'; import { ajax } from 'wizard/lib/ajax';
import computed from 'ember-addons/ember-computed-decorators'; import Step from 'wizard/models/step';
const CustomWizard = Ember.Object.extend({ const CustomWizard = Ember.Object.extend({
@computed('steps.length') @computed('steps.length')
totalSteps: length => length totalSteps: length => length
}); });
export function findCustomWizard(name) { export function findCustomWizard(wizardId) {
return ajax({ url: `/wizard/custom/${name}.json` }).then(response => { return ajax({ url: `/wizard/custom/${wizardId}` }).then(result => {
const wizard = response.wizard; const wizard = result.wizard;
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;
}); });
console.log(wizard)
return CustomWizard.create(wizard); return CustomWizard.create(wizard);
}); });
} };
export default CustomWizard;

Datei anzeigen

@ -1,7 +0,0 @@
import { findCustomWizard } from '../models/custom-wizard';
export default Ember.Route.extend({
model(params) {
return findCustomWizard(params.name);
}
});

Datei anzeigen

@ -0,0 +1,8 @@
import IndexRoute from 'wizard/routes/index';
export default IndexRoute.extend({
beforeModel() {
const appModel = this.modelFor('application');
this.replaceWith('custom.step', appModel.start);
}
});

Datei anzeigen

@ -0,0 +1,3 @@
import StepRoute from 'wizard/routes/step';
export default StepRoute.extend();

Datei anzeigen

@ -1,11 +0,0 @@
import { findCustomWizard } from '../models/custom-wizard';
export default Ember.Route.extend({
model(params) {
return findCustomWizard(params.name);
},
afterModel(model) {
this.replaceWith('step', model.start);
}
});

Datei anzeigen

@ -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="{{siteInfo.logo_small_url}}" style="background-image: initial; width: 33px; height: 33px;"/>
{{else}}
<div class='discourse-logo'></div>
{{/if}}
</div>
</div>

Datei anzeigen

@ -0,0 +1 @@
{{wizard-step step=step wizard=wizard goNext="goNext" goBack="goBack"}}

Datei anzeigen

@ -1,31 +1,27 @@
class CustomWizard::Builder class CustomWizard::Builder
def initialize(user, wizard_name) def initialize(user, wizard_id)
rows = PluginStoreRow.where(plugin_name: 'custom_wizards') data = PluginStore.get('custom_wizard', wizard_id)
return if !rows @custom_wizard = CustomWizard::Wizard.new(data)
[*rows].each do |r|
wizard = CustomWizard::Wizard.new(r.value)
@template = wizard if wizard.name.dasherize.downcase == wizard_name
end
@wizard = Wizard.new(user) @wizard = Wizard.new(user)
@wizard.id = wizard_id
end end
def build def build
@template.steps.each do |s| @custom_wizard.steps.each do |s|
@wizard.append_step(s['title']) do |step| @wizard.append_step(s['id']) do |step|
step.title = s['title'] if s['title']
step.banner = s['banner'] if s['banner'] step.banner = s['banner'] if s['banner']
s['fields'].each do |f| s['fields'].each do |f|
field = step.add_field(id: f['id'], field = step.add_field(id: f['id'],
type: f['type'], type: f['type'],
required: f['required'], label: f['label'],
value: f['value']) description: f['description'],
required: f['required'])
if f['type'] == 'dropdown' if f['type'] == 'dropdown'
f['choices'].each do |c| f['choices'].each do |c|
field.add_choice(c) field.add_choice(c['id'], label: c['label'])
end end
end end
end end

Datei anzeigen

@ -1,11 +1,12 @@
class CustomWizard::Wizard class CustomWizard::Wizard
attr_reader :name, :steps attr_reader :id, :name, :steps, :custom
def initialize(data) def initialize(data)
parsed = ::JSON.parse(data) data = data.is_a?(String) ? ::JSON.parse(data) : data
@id = parsed['id'] @id = data['id']
@name = parsed['name'] @name = data['name']
@steps = parsed['steps'] @steps = data['steps']
@custom = true
end end
end end

Datei anzeigen

@ -3,7 +3,7 @@
# version: 0.1 # version: 0.1
# authors: Angus McLeod # authors: Angus McLeod
register_asset 'stylesheets/custom-wizard.scss' register_asset 'stylesheets/custom_wizard.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")
@ -24,10 +24,10 @@ after_initialize do
load File.expand_path('../app/controllers/admin.rb', __FILE__) load File.expand_path('../app/controllers/admin.rb', __FILE__)
CustomWizard::Engine.routes.draw do CustomWizard::Engine.routes.draw do
get ':name' => 'wizard#index' get ':wizard_id' => 'wizard#index'
get ':name/steps' => 'steps#index' get ':wizard_id/steps' => 'steps#index'
get ':name/steps/:id' => 'wizard#index' get ':wizard_id/steps/:step_id' => 'wizard#index'
put ':name/steps/:id' => 'steps#update' put ':wizard_id/steps/:step_id' => 'steps#update'
end end
require_dependency 'admin_constraint' require_dependency 'admin_constraint'
@ -40,9 +40,64 @@ after_initialize do
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#all'
get 'admin/wizards/custom/:id' => 'admin#find' get 'admin/wizards/custom/:wizard_id' => 'admin#find'
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'
end end
end end
class ::Wizard
attr_accessor :id
end
class ::Wizard::Step
attr_accessor :title
end
::Wizard::Field.class_eval do
attr_reader :label, :description
def initialize(attrs)
attrs = attrs || {}
@id = attrs[:id]
@type = attrs[:type]
@required = !!attrs[:required]
@label = attrs[:label]
@description = attrs[:description]
@value = attrs[:value]
@choices = []
end
end
add_to_serializer(:wizard, :id) { object.id }
::WizardStepSerializer.class_eval do
def title
if object.title
object.title
else
I18n.t("#{i18n_key}.title", default: '')
end
end
end
::WizardFieldSerializer.class_eval do
def label
puts "LABEL: #{object.label}"
if object.label
object.label
else
I18n.t("#{i18n_key}.label", default: '')
end
end
def description
if object.description
object.description
else
I18n.t("#{i18n_key}.description", default: '')
end
end
end
end end

3
public/desktop.css Normale Datei
Datei anzeigen

@ -0,0 +1,3 @@
.custom-wizard {
background-color: #f4f4f4;
}