Wizard: Server Side Validation + Finished Step
This commit is contained in:
parent
be1d74d207
commit
9f12b571ef
|
@ -64,10 +64,11 @@ export default Ember.Component.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
const $elem = this.$();
|
const $elem = this.$();
|
||||||
const minimumResultsForSearch = this.capabilities.isIOS ? -1 : 5;
|
const caps = this.capabilities;
|
||||||
|
const minimumResultsForSearch = (caps && caps.isIOS) ? -1 : 5;
|
||||||
$elem.select2({
|
$elem.select2({
|
||||||
formatResult: this.comboTemplate, minimumResultsForSearch,
|
formatResult: this.comboTemplate, minimumResultsForSearch,
|
||||||
width: 'resolve',
|
width: this.get('width') || 'resolve',
|
||||||
allowClear: true
|
allowClear: true
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { htmlHelper } from 'discourse-common/lib/helpers';
|
||||||
|
|
||||||
|
export default htmlHelper((key, params) => I18n.t(key, params.hash));
|
|
@ -1,4 +1,4 @@
|
||||||
import ComboboxView from 'discourse/components/combo-box';
|
import ComboboxView from 'discourse-common/components/combo-box';
|
||||||
import { categoryBadgeHTML } from 'discourse/helpers/category-link';
|
import { categoryBadgeHTML } from 'discourse/helpers/category-link';
|
||||||
import computed from 'ember-addons/ember-computed-decorators';
|
import computed from 'ember-addons/ember-computed-decorators';
|
||||||
import { observes, on } from 'ember-addons/ember-computed-decorators';
|
import { observes, on } from 'ember-addons/ember-computed-decorators';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { iconHTML } from 'discourse-common/helpers/fa-icon';
|
import { iconHTML } from 'discourse-common/helpers/fa-icon';
|
||||||
import Combobox from 'discourse/components/combo-box';
|
import Combobox from 'discourse-common/components/combo-box';
|
||||||
import { on, observes } from 'ember-addons/ember-computed-decorators';
|
import { on, observes } from 'ember-addons/ember-computed-decorators';
|
||||||
|
|
||||||
export default Combobox.extend({
|
export default Combobox.extend({
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//= require env
|
||||||
//= require jquery_include
|
//= require jquery_include
|
||||||
//= require ember_include
|
//= require ember_include
|
||||||
//= require loader
|
//= require loader
|
||||||
|
|
|
@ -60,7 +60,6 @@
|
||||||
//= require ./discourse/views/container
|
//= require ./discourse/views/container
|
||||||
//= require ./discourse/views/modal-body
|
//= require ./discourse/views/modal-body
|
||||||
//= require ./discourse/views/flag
|
//= require ./discourse/views/flag
|
||||||
//= require ./discourse/components/combo-box
|
|
||||||
//= require ./discourse/components/edit-category-panel
|
//= require ./discourse/components/edit-category-panel
|
||||||
//= require ./discourse/views/button
|
//= require ./discourse/views/button
|
||||||
//= require ./discourse/components/dropdown-button
|
//= require ./discourse/components/dropdown-button
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
//= require logster
|
//= require logster
|
||||||
//= require ./env
|
|
||||||
//= require ./discourse-objects
|
//= require ./discourse-objects
|
||||||
//= require probes.js
|
//= require probes.js
|
||||||
|
|
||||||
|
@ -38,4 +37,3 @@
|
||||||
//= require virtual-dom
|
//= require virtual-dom
|
||||||
//= require virtual-dom-amd
|
//= require virtual-dom-amd
|
||||||
//= require highlight.js
|
//= require highlight.js
|
||||||
//= require_tree ./discourse/ember
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
//= require env
|
|
||||||
//= require template_include.js
|
//= require template_include.js
|
||||||
|
//= require select2.js
|
||||||
|
|
|
@ -4,5 +4,8 @@ export default Ember.Component.extend({
|
||||||
classNameBindings: [':wizard-field', ':text-field', 'field.invalid'],
|
classNameBindings: [':wizard-field', ':text-field', 'field.invalid'],
|
||||||
|
|
||||||
@computed('field.id')
|
@computed('field.id')
|
||||||
inputClassName: id => `field-${Ember.String.dasherize(id)}`
|
inputClassName: id => `field-${Ember.String.dasherize(id)}`,
|
||||||
|
|
||||||
|
@computed('field.type')
|
||||||
|
inputComponentName: type => `wizard-field-${type}`
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,6 +12,9 @@ export default Ember.Component.extend({
|
||||||
@computed('step.displayIndex', 'wizard.totalSteps')
|
@computed('step.displayIndex', 'wizard.totalSteps')
|
||||||
showNextButton: (current, total) => current < total,
|
showNextButton: (current, total) => current < total,
|
||||||
|
|
||||||
|
@computed('step.displayIndex', 'wizard.totalSteps')
|
||||||
|
showDoneButton: (current, total) => current === total,
|
||||||
|
|
||||||
@computed('step.index')
|
@computed('step.index')
|
||||||
showBackButton: index => index > 0,
|
showBackButton: index => index > 0,
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ export const States = {
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
_validState: null,
|
_validState: null,
|
||||||
|
errorDescription: null,
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this._super();
|
this._super();
|
||||||
|
@ -23,8 +24,14 @@ export default {
|
||||||
@computed('_validState')
|
@computed('_validState')
|
||||||
unchecked: state => state === States.UNCHECKED,
|
unchecked: state => state === States.UNCHECKED,
|
||||||
|
|
||||||
setValid(valid) {
|
setValid(valid, description) {
|
||||||
this.set('_validState', valid ? States.VALID : States.INVALID);
|
this.set('_validState', valid ? States.VALID : States.INVALID);
|
||||||
|
|
||||||
|
if (!valid && description && description.length) {
|
||||||
|
this.set('errorDescription', description);
|
||||||
|
} else {
|
||||||
|
this.set('errorDescription', null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
export default Ember.Route.extend({
|
export default Ember.Route.extend({
|
||||||
model(params) {
|
model(params) {
|
||||||
const allSteps = this.modelFor('application').steps;
|
const allSteps = this.modelFor('application').steps;
|
||||||
return allSteps.findProperty('id', params.step_id);
|
const step = allSteps.findProperty('id', params.step_id);
|
||||||
|
return step ? step : allSteps[0];
|
||||||
},
|
},
|
||||||
|
|
||||||
setupController(controller, step) {
|
setupController(controller, step) {
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{{combo-box value=field.value content=field.options nameProperty="label" width="400px"}}
|
|
@ -0,0 +1 @@
|
||||||
|
{{input value=field.value class=inputClassName placeholder=field.placeholder}}
|
|
@ -2,6 +2,14 @@
|
||||||
<span class='label-value'>{{field.label}}</span>
|
<span class='label-value'>{{field.label}}</span>
|
||||||
|
|
||||||
<div class='input-area'>
|
<div class='input-area'>
|
||||||
{{input value=field.value class=inputClassName placeholder=field.placeholder}}
|
{{component inputComponentName field=field inputClassName=inputClassName}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{#if field.errorDescription}}
|
||||||
|
<div class='field-error-description'>{{field.errorDescription}}</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if field.description}}
|
||||||
|
<div class='field-description'>{{field.description}}</div>
|
||||||
|
{{/if}}
|
||||||
</label>
|
</label>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if step.description}}
|
{{#if step.description}}
|
||||||
<p class='wizard-step-description'>{{step.description}}</p>
|
<p class='wizard-step-description'>{{{step.description}}}</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#wizard-step-form step=step}}
|
{{#wizard-step-form step=step}}
|
||||||
|
@ -14,24 +14,33 @@
|
||||||
|
|
||||||
<div class='wizard-step-footer'>
|
<div class='wizard-step-footer'>
|
||||||
<div class='wizard-progress'>
|
<div class='wizard-progress'>
|
||||||
<div class='text'>{{i18n "wizard.step" current=step.displayIndex total=wizard.totalSteps}}</div>
|
<div class='text'>{{bound-i18n "wizard.step" current=step.displayIndex total=wizard.totalSteps}}</div>
|
||||||
<div class='bar-container'>
|
<div class='bar-container'>
|
||||||
<div class='bar-contents' style={{barStyle}}></div>
|
<div class='bar-contents' style={{barStyle}}></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if showBackButton}}
|
<div class='wizard-buttons'>
|
||||||
<button class='wizard-btn back' {{action "backStep"}} disabled={{saving}}>
|
{{#if showBackButton}}
|
||||||
{{fa-icon "chevron-left"}}
|
<button class='wizard-btn back' {{action "backStep"}} disabled={{saving}}>
|
||||||
{{i18n "wizard.back"}}
|
{{fa-icon "chevron-left"}}
|
||||||
</button>
|
{{i18n "wizard.back"}}
|
||||||
{{/if}}
|
</button>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{#if showNextButton}}
|
{{#if showNextButton}}
|
||||||
<button class='wizard-btn next' {{action "nextStep"}} disabled={{saving}}>
|
<button class='wizard-btn next' {{action "nextStep"}} disabled={{saving}}>
|
||||||
{{i18n "wizard.next"}}
|
{{i18n "wizard.next"}}
|
||||||
{{fa-icon "chevron-right"}}
|
{{fa-icon "chevron-right"}}
|
||||||
</button>
|
</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if showDoneButton}}
|
||||||
|
<button class='wizard-btn done' {{action "finished"}} disabled={{saving}}>
|
||||||
|
{{fa-icon "check"}}
|
||||||
|
{{i18n "wizard.done"}}
|
||||||
|
</button>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,7 +18,9 @@ test("Forum Name Step", assert => {
|
||||||
assert.ok(exists('.wizard-step-title'));
|
assert.ok(exists('.wizard-step-title'));
|
||||||
assert.ok(exists('.wizard-step-description'));
|
assert.ok(exists('.wizard-step-description'));
|
||||||
assert.ok(!exists('.invalid .field-full-name'), "don't show it as invalid until the user does something");
|
assert.ok(!exists('.invalid .field-full-name'), "don't show it as invalid until the user does something");
|
||||||
|
assert.ok(exists('.wizard-field .field-description'));
|
||||||
assert.ok(!exists('.wizard-btn.back'));
|
assert.ok(!exists('.wizard-btn.back'));
|
||||||
|
assert.ok(!exists('.wizard-field .field-error-description'));
|
||||||
});
|
});
|
||||||
|
|
||||||
// invalid data
|
// invalid data
|
||||||
|
@ -32,16 +34,19 @@ test("Forum Name Step", assert => {
|
||||||
click('.wizard-btn.next');
|
click('.wizard-btn.next');
|
||||||
andThen(() => {
|
andThen(() => {
|
||||||
assert.ok(exists('.invalid .field-full-name'));
|
assert.ok(exists('.invalid .field-full-name'));
|
||||||
|
assert.ok(exists('.wizard-field .field-error-description'));
|
||||||
});
|
});
|
||||||
|
|
||||||
// server validation ok
|
// server validation ok
|
||||||
fillIn('input.field-full-name', "Evil Trout");
|
fillIn('input.field-full-name', "Evil Trout");
|
||||||
click('.wizard-btn.next');
|
click('.wizard-btn.next');
|
||||||
andThen(() => {
|
andThen(() => {
|
||||||
|
assert.ok(!exists('.wizard-field .field-error-description'));
|
||||||
assert.ok(!exists('.wizard-step-title'));
|
assert.ok(!exists('.wizard-step-title'));
|
||||||
assert.ok(!exists('.wizard-step-description'));
|
assert.ok(!exists('.wizard-step-description'));
|
||||||
assert.ok(exists('input.field-email'), "went to the next step");
|
assert.ok(exists('input.field-email'), "went to the next step");
|
||||||
assert.ok(!exists('.wizard-btn.next'));
|
assert.ok(!exists('.wizard-btn.next'));
|
||||||
|
assert.ok(exists('.wizard-btn.done'), 'last step shows a done button');
|
||||||
assert.ok(exists('.wizard-btn.back'), 'shows the back button');
|
assert.ok(exists('.wizard-btn.back'), 'shows the back button');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,10 @@ export default function() {
|
||||||
title: 'hello there',
|
title: 'hello there',
|
||||||
index: 0,
|
index: 0,
|
||||||
description: 'hello!',
|
description: 'hello!',
|
||||||
fields: [{ id: 'full_name', type: 'text', required: true }],
|
fields: [{ id: 'full_name',
|
||||||
|
type: 'text',
|
||||||
|
required: true,
|
||||||
|
description: "Your name" }],
|
||||||
next: 'second-step'
|
next: 'second-step'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
.select2-results .select2-highlighted {
|
||||||
|
background: dark-light-diff($highlight, $secondary, 50%, -80%);
|
||||||
|
color: $primary;
|
||||||
|
}
|
||||||
|
|
||||||
.category-combobox, .select2-drop {
|
.category-combobox, .select2-drop {
|
||||||
|
|
||||||
.badge-category {
|
.badge-category {
|
||||||
|
|
|
@ -332,11 +332,6 @@ Version: @@ver@@ Timestamp: @@timestamp@@
|
||||||
.select2-results-dept-6 .select2-result-label { padding-left: 110px }
|
.select2-results-dept-6 .select2-result-label { padding-left: 110px }
|
||||||
.select2-results-dept-7 .select2-result-label { padding-left: 120px }
|
.select2-results-dept-7 .select2-result-label { padding-left: 120px }
|
||||||
|
|
||||||
.select2-results .select2-highlighted {
|
|
||||||
background: dark-light-diff($highlight, $secondary, 50%, -80%);
|
|
||||||
color: $primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select2-results li em {
|
.select2-results li em {
|
||||||
background: #feffde;
|
background: #feffde;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
@import "vendor/normalize";
|
@import "vendor/normalize";
|
||||||
@import "vendor/font_awesome/font-awesome";
|
@import "vendor/font_awesome/font-awesome";
|
||||||
|
@import "vendor/select2";
|
||||||
|
|
||||||
body {
|
body.wizard {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
background-image: url('/images/wizard/bubbles.png');
|
background-image: url('/images/wizard/bubbles.png');
|
||||||
background-repeat: repeat;
|
background-repeat: repeat;
|
||||||
|
@ -12,6 +13,10 @@ body {
|
||||||
line-height: 1.4em;
|
line-height: 1.4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.select {
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
.wizard-column {
|
.wizard-column {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
box-shadow: 0 5px 10px rgba(0,0,0,0.2);
|
box-shadow: 0 5px 10px rgba(0,0,0,0.2);
|
||||||
|
@ -77,10 +82,10 @@ body {
|
||||||
background-color: #6699ff;
|
background-color: #6699ff;
|
||||||
color: white;
|
color: white;
|
||||||
border: 0px;
|
border: 0px;
|
||||||
float: right;
|
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
transition: background-color .3s;
|
transition: background-color .3s;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #80B3FF;
|
background-color: #80B3FF;
|
||||||
|
@ -131,6 +136,25 @@ body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.wizard-btn:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.wizard-btn.done {
|
||||||
|
background-color: #33B333;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #4DCD4D;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: #66E666;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background-color: #006700;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.wizard-field {
|
.wizard-field {
|
||||||
|
@ -142,6 +166,16 @@ body {
|
||||||
margin-top: 0.5em;
|
margin-top: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.field-error-description {
|
||||||
|
color: red;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-description {
|
||||||
|
color: #999;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
&.text-field {
|
&.text-field {
|
||||||
input {
|
input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -9,7 +9,16 @@ class StepsController < ApplicationController
|
||||||
def update
|
def update
|
||||||
updater = Wizard::StepUpdater.new(current_user, params[:id])
|
updater = Wizard::StepUpdater.new(current_user, params[:id])
|
||||||
updater.update(params[:fields])
|
updater.update(params[:fields])
|
||||||
render nothing: true
|
|
||||||
|
if updater.success?
|
||||||
|
render json: success_json
|
||||||
|
else
|
||||||
|
errors = []
|
||||||
|
updater.errors.messages.each do |field, msg|
|
||||||
|
errors << {field: field, description: msg.join }
|
||||||
|
end
|
||||||
|
render json: { errors: errors }, status: 422
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class WizardFieldSerializer < ApplicationSerializer
|
class WizardFieldSerializer < ApplicationSerializer
|
||||||
|
|
||||||
attributes :id, :type, :required, :value, :label, :placeholder
|
attributes :id, :type, :required, :value, :label, :placeholder, :description, :options
|
||||||
|
|
||||||
def id
|
def id
|
||||||
object.id
|
object.id
|
||||||
|
@ -41,4 +41,23 @@ class WizardFieldSerializer < ApplicationSerializer
|
||||||
def include_placeholder?
|
def include_placeholder?
|
||||||
placeholder.present?
|
placeholder.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def description
|
||||||
|
I18n.t("#{i18n_key}.description", default: '')
|
||||||
|
end
|
||||||
|
|
||||||
|
def include_description?
|
||||||
|
description.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def options
|
||||||
|
object.options.map do |o|
|
||||||
|
{id: o, label: I18n.t("#{i18n_key}.options.#{o}")}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def include_options?
|
||||||
|
object.options.present?
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<%= stylesheet_link_tag 'wizard' %>
|
<%= stylesheet_link_tag 'wizard' %>
|
||||||
<%= script 'wizard-vendor' %>
|
|
||||||
<%= script 'ember_jquery' %>
|
<%= script 'ember_jquery' %>
|
||||||
|
<%= script 'wizard-vendor' %>
|
||||||
<%= script 'wizard-application' %>
|
<%= script 'wizard-application' %>
|
||||||
<%= script "locales/#{I18n.locale}" %>
|
<%= script "locales/#{I18n.locale}" %>
|
||||||
<%= render partial: "common/special_font_face" %>
|
<%= render partial: "common/special_font_face" %>
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
<title><%= t 'wizard.title' %></title>
|
<title><%= t 'wizard.title' %></title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body class='wizard'>
|
||||||
<div id='wizard-main'></div>
|
<div id='wizard-main'></div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
<title>QUnit Test Runner</title>
|
<title>QUnit Test Runner</title>
|
||||||
<%= stylesheet_link_tag "qunit" %>
|
<%= stylesheet_link_tag "qunit" %>
|
||||||
<%= stylesheet_link_tag "test_helper" %>
|
<%= stylesheet_link_tag "test_helper" %>
|
||||||
|
<%= stylesheet_link_tag "wizard" %>
|
||||||
<%= javascript_include_tag "qunit" %>
|
<%= javascript_include_tag "qunit" %>
|
||||||
<%= javascript_include_tag "wizard/test/test_helper" %>
|
<%= javascript_include_tag "wizard/test/test_helper" %>
|
||||||
<%= csrf_meta_tags %>
|
<%= csrf_meta_tags %>
|
||||||
|
|
|
@ -3227,6 +3227,7 @@ en:
|
||||||
|
|
||||||
wizard_js:
|
wizard_js:
|
||||||
wizard:
|
wizard:
|
||||||
|
done: "Done"
|
||||||
back: "Back"
|
back: "Back"
|
||||||
next: "Next"
|
next: "Next"
|
||||||
step: "Step %{current} of %{total}"
|
step: "Step %{current} of %{total}"
|
||||||
|
|
|
@ -3233,4 +3233,27 @@ en:
|
||||||
contact_email:
|
contact_email:
|
||||||
label: "Contact E-mail"
|
label: "Contact E-mail"
|
||||||
placeholder: "name@example.com"
|
placeholder: "name@example.com"
|
||||||
|
description: "The key contact responsible for this site. This is used for critical notifications such as unhandled flags and on the \"About\" page for urgent matters."
|
||||||
|
contact_url:
|
||||||
|
label: "Contact URL"
|
||||||
|
placeholder: "http://www.example.com/contact-us"
|
||||||
|
description: "Will be displayed on your \"About\" page."
|
||||||
|
site_contact_username:
|
||||||
|
label: "Site Contact Username"
|
||||||
|
description: "All automated messages will be sent from this user."
|
||||||
|
colors:
|
||||||
|
title: "Choose a Color Scheme"
|
||||||
|
fields:
|
||||||
|
color_scheme:
|
||||||
|
label: "Color Scheme"
|
||||||
|
options:
|
||||||
|
default: "Default Scheme"
|
||||||
|
dark: "Dark Scheme"
|
||||||
|
|
||||||
|
finished:
|
||||||
|
title: "Your Discourse Forum is Ready!"
|
||||||
|
description: |
|
||||||
|
<p>If you ever feel like changing these settings, visit your admin section.</p>
|
||||||
|
|
||||||
|
<p>Have fun and enjoy your new community!</p>
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ class Wizard
|
||||||
step.previous = last_step
|
step.previous = last_step
|
||||||
step.index = last_step.index + 1
|
step.index = last_step.index + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.build
|
def self.build
|
||||||
|
@ -38,9 +37,20 @@ class Wizard
|
||||||
wizard.append_step(title)
|
wizard.append_step(title)
|
||||||
|
|
||||||
contact = wizard.create_step('contact')
|
contact = wizard.create_step('contact')
|
||||||
contact.add_field(id: 'contact_email', type: 'text', required: true)
|
contact.add_field(id: 'contact_email', type: 'text', required: true, value: SiteSetting.contact_email)
|
||||||
|
contact.add_field(id: 'contact_url', type: 'text', value: SiteSetting.contact_url)
|
||||||
|
contact.add_field(id: 'site_contact_username', type: 'text', value: SiteSetting.site_contact_username)
|
||||||
wizard.append_step(contact)
|
wizard.append_step(contact)
|
||||||
|
|
||||||
|
theme = wizard.create_step('colors')
|
||||||
|
scheme = theme.add_field(id: 'color_scheme', type: 'dropdown', required: true)
|
||||||
|
scheme.add_option('default')
|
||||||
|
scheme.add_option('dark')
|
||||||
|
wizard.append_step(theme)
|
||||||
|
|
||||||
|
finished = wizard.create_step('finished')
|
||||||
|
wizard.append_step(finished);
|
||||||
|
|
||||||
wizard
|
wizard
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
class Wizard
|
class Wizard
|
||||||
class Field
|
class Field
|
||||||
attr_reader :id, :type, :required, :value
|
|
||||||
|
attr_reader :id, :type, :required, :value, :options
|
||||||
attr_accessor :step
|
attr_accessor :step
|
||||||
|
|
||||||
def initialize(attrs)
|
def initialize(attrs)
|
||||||
|
@ -10,6 +11,12 @@ class Wizard
|
||||||
@type = attrs[:type]
|
@type = attrs[:type]
|
||||||
@required = !!attrs[:required]
|
@required = !!attrs[:required]
|
||||||
@value = attrs[:value]
|
@value = attrs[:value]
|
||||||
|
@options = []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def add_option(id)
|
||||||
|
@options << id
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,22 +1,15 @@
|
||||||
class Wizard
|
class Wizard
|
||||||
class StepUpdater
|
class StepUpdater
|
||||||
|
include ActiveModel::Model
|
||||||
attr_accessor :errors
|
|
||||||
|
|
||||||
def initialize(current_user, id)
|
def initialize(current_user, id)
|
||||||
@current_user = current_user
|
@current_user = current_user
|
||||||
@id = id
|
@id = id
|
||||||
@errors = []
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def update(fields)
|
def update(fields)
|
||||||
updater_method = "update_#{@id.underscore}".to_sym
|
updater_method = "update_#{@id.underscore}".to_sym
|
||||||
|
send(updater_method, fields.symbolize_keys) if respond_to?(updater_method)
|
||||||
if respond_to?(updater_method)
|
|
||||||
send(updater_method, fields.symbolize_keys)
|
|
||||||
else
|
|
||||||
raise Discourse::InvalidAccess.new
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_forum_title(fields)
|
def update_forum_title(fields)
|
||||||
|
@ -24,6 +17,12 @@ class Wizard
|
||||||
update_setting(:site_description, fields, :site_description)
|
update_setting(:site_description, fields, :site_description)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update_contact(fields)
|
||||||
|
update_setting(:contact_email, fields, :contact_email)
|
||||||
|
update_setting(:contact_url, fields, :contact_url)
|
||||||
|
update_setting(:site_contact_username, fields, :site_contact_username)
|
||||||
|
end
|
||||||
|
|
||||||
def success?
|
def success?
|
||||||
@errors.blank?
|
@errors.blank?
|
||||||
end
|
end
|
||||||
|
@ -33,9 +32,10 @@ class Wizard
|
||||||
def update_setting(id, fields, field_id)
|
def update_setting(id, fields, field_id)
|
||||||
value = fields[field_id]
|
value = fields[field_id]
|
||||||
value.strip! if value.is_a?(String)
|
value.strip! if value.is_a?(String)
|
||||||
SiteSetting.set_and_log(id, value, @current_user)
|
|
||||||
|
SiteSetting.set_and_log(id, value, @current_user) if SiteSetting.send(id) != value
|
||||||
rescue Discourse::InvalidParameters => e
|
rescue Discourse::InvalidParameters => e
|
||||||
@errors << {field: field_id, description: e.message }
|
errors.add(field_id, e.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,11 +4,35 @@ require_dependency 'wizard/step_updater'
|
||||||
describe Wizard::StepUpdater do
|
describe Wizard::StepUpdater do
|
||||||
let(:user) { Fabricate(:admin) }
|
let(:user) { Fabricate(:admin) }
|
||||||
|
|
||||||
it "can update the forum title" do
|
it "updates the forum title step" do
|
||||||
updater = Wizard::StepUpdater.new(user, 'forum_title')
|
updater = Wizard::StepUpdater.new(user, 'forum_title')
|
||||||
updater.update(title: 'new forum title')
|
updater.update(title: 'new forum title', site_description: 'neat place')
|
||||||
|
|
||||||
expect(updater.success?).to eq(true)
|
expect(updater.success?).to eq(true)
|
||||||
expect(SiteSetting.title).to eq("new forum title")
|
expect(SiteSetting.title).to eq("new forum title")
|
||||||
|
expect(SiteSetting.site_description).to eq("neat place")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "contact step" do
|
||||||
|
let(:updater) { Wizard::StepUpdater.new(user, 'contact') }
|
||||||
|
|
||||||
|
it "updates the fields correctly" do
|
||||||
|
updater.update(contact_email: 'eviltrout@example.com',
|
||||||
|
contact_url: 'http://example.com/custom-contact-url',
|
||||||
|
site_contact_username: user.username)
|
||||||
|
|
||||||
|
expect(updater.success?).to eq(true)
|
||||||
|
expect(SiteSetting.contact_email).to eq("eviltrout@example.com")
|
||||||
|
expect(SiteSetting.contact_url).to eq("http://example.com/custom-contact-url")
|
||||||
|
expect(SiteSetting.site_contact_username).to eq(user.username)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't update when there are errors" do
|
||||||
|
updater.update(contact_email: 'not-an-email',
|
||||||
|
site_contact_username: 'not-a-username')
|
||||||
|
expect(updater.success?).to eq(false)
|
||||||
|
expect(updater.errors).to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,7 +16,6 @@ describe Wizard do
|
||||||
let(:step2) { wizard.create_step('second-step') }
|
let(:step2) { wizard.create_step('second-step') }
|
||||||
|
|
||||||
it "adds the step correctly" do
|
it "adds the step correctly" do
|
||||||
|
|
||||||
expect(step1.index).to be_blank
|
expect(step1.index).to be_blank
|
||||||
|
|
||||||
wizard.append_step(step1)
|
wizard.append_step(step1)
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
require 'wizard'
|
||||||
|
|
||||||
|
describe Wizard::Step do
|
||||||
|
|
||||||
|
let(:wizard) { Wizard.new }
|
||||||
|
let(:step) { wizard.create_step('test-step') }
|
||||||
|
|
||||||
|
it "supports fields and options" do
|
||||||
|
expect(step.fields).to be_empty
|
||||||
|
text = step.add_field(id: 'test', type: 'text')
|
||||||
|
expect(step.fields).to eq([text])
|
||||||
|
|
||||||
|
dropdown = step.add_field(id: 'snacks', type: 'dropdown')
|
||||||
|
dropdown.add_option(id: 'candy')
|
||||||
|
dropdown.add_option(id: 'nachos')
|
||||||
|
dropdown.add_option(id: 'pizza')
|
||||||
|
|
||||||
|
expect(step.fields).to eq([text, dropdown])
|
||||||
|
expect(dropdown.options.size).to eq(3)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
|
@ -3,6 +3,10 @@ require 'rails_helper'
|
||||||
describe ExtraLocalesController do
|
describe ExtraLocalesController do
|
||||||
|
|
||||||
context 'show' do
|
context 'show' do
|
||||||
|
before do
|
||||||
|
I18n.locale = :en
|
||||||
|
I18n.reload!
|
||||||
|
end
|
||||||
|
|
||||||
it "needs a valid bundle" do
|
it "needs a valid bundle" do
|
||||||
get :show, bundle: 'made-up-bundle'
|
get :show, bundle: 'made-up-bundle'
|
||||||
|
|
|
@ -19,15 +19,15 @@ describe StepsController do
|
||||||
log_in(:admin)
|
log_in(:admin)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "raises an error with an invalid id" do
|
it "updates properly if you are staff" do
|
||||||
xhr :put, :update, id: 'made-up-id', fields: { forum_title: "updated title" }
|
xhr :put, :update, id: 'contact', fields: { contact_email: "eviltrout@example.com" }
|
||||||
expect(response).to_not be_success
|
expect(response).to be_success
|
||||||
|
expect(SiteSetting.contact_email).to eq("eviltrout@example.com")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "updates properly if you are staff" do
|
it "returns errors if the field has them" do
|
||||||
xhr :put, :update, id: 'forum-title', fields: { title: "updated title" }
|
xhr :put, :update, id: 'contact', fields: { contact_email: "not-an-email" }
|
||||||
expect(response).to be_success
|
expect(response).to_not be_success
|
||||||
expect(SiteSetting.title).to eq("updated title")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue