1
0
Fork 0
Dieser Commit ist enthalten in:
Angus McLeod 2017-09-23 10:34:07 +08:00
Commit ebd026887c
25 geänderte Dateien mit 495 neuen und 0 gelöschten Zeilen

Datei anzeigen

@ -0,0 +1,11 @@
import { default as computed } from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({
targets: ['topic', 'profile', 'email', 'badge', 'save'],
isTopic: Ember.computed.equal('targets', 'topic'),
init() {
this._super(...arguments);
console.log(this)
},
});

Datei anzeigen

@ -0,0 +1,21 @@
import { observes } from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({
classNames: 'wizard-custom-field',
fieldTypes: ['dropdown', 'image', 'radio', 'text', 'textarea'],
isDropdown: Ember.computed.equal('field.type', 'dropdown'),
choices: Ember.A(),
@observes('field.label')
setFieldId() {
const label = this.get('field.label');
console.log('setting id')
this.set('field.id', Ember.String.underscore(label));
},
actions: {
addChoice() {
}
}
});

Datei anzeigen

@ -0,0 +1,26 @@
import { default as computed } from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({
classNames: 'wizard-custom-step',
@computed('step.fields.@each.id')
allowAddAction(stepFields) {
console.log(stepFields)
return stepFields.get('firstObject.id');
},
actions: {
addField() {
console.log('adding field')
this.get('step.fields').pushObject(Ember.Object.create());
},
addAction() {
this.get('step.actions').pushObject(Ember.Object.create());
},
removeStep() {
this.sendAction('removeStep', this.get('step.name'));
}
}
});

Datei anzeigen

@ -0,0 +1 @@
{{nav-item route='adminWizards' label='admin.wizard.label'}}

Datei anzeigen

@ -0,0 +1,26 @@
export default Ember.Controller.extend({
actions: {
save() {
this.get('model').save().then(() => {
this.transitionToRoute('adminWizardsCustom');
});
},
remove() {
this.get('model').destroy().then(() => {
this.transitionToRoute('adminWizardsCustom');
});
},
addStep() {
this.get('model.steps').pushObject({
fields: Ember.A(),
actions: Ember.A()
});
},
removeStep(name) {
this.get('model.steps').findBy('name', name);
}
}
});

Datei anzeigen

@ -0,0 +1,10 @@
export default {
resource: 'admin',
map() {
this.route('adminWizards', { path: '/wizards', resetNamespace: true }, function() {
this.route('adminWizardsCustom', { path: '/custom', resetNamespace: true }, function() {
this.route('adminWizard', { path: '/:name', resetNamespace: true });
});
});
}
};

Datei anzeigen

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

Datei anzeigen

@ -0,0 +1,41 @@
import { ajax } from 'discourse/lib/ajax';
const CustomWizard = Discourse.Model.extend({
steps: Ember.A(),
save() {
const steps = JSON.stringify(this.get('steps').toArray());
return ajax(`/admin/wizards/custom/${this.get('name')}`, {
type: 'PUT',
data: { steps }
});
},
destroy() {
return ajax(`/admin/wizards/custom/${this.get('name')}`, {
type: 'DELETE'
});
}
});
CustomWizard.reopenClass({
findAll() {
return ajax("/admin/wizards/custom/all").then(result => {
return result.wizards.map(w => CustomWizard.create(w));
});
},
create() {
const wizard = this._super.apply(this, arguments);
const steps = wizard.get('steps');
steps.forEach((s) => {
s.fields = Ember.A(s.fields);
s.actions = Ember.A(s.actions);
});
return wizard;
}
});
export default CustomWizard;

Datei anzeigen

@ -0,0 +1,23 @@
import CustomWizard from '../models/custom-wizard';
export default Discourse.Route.extend({
model(params) {
if (params.name === 'new') {
this.set('new', true);
return CustomWizard.create();
}
this.set('new', false);
const wizard = this.modelFor('admin-wizards-custom').findBy('name', params.name );
if (!wizard) { return this.transitionTo('adminWizardsCustom.index'); }
return wizard;
},
setupController(controller, model) {
controller.set("new", this.get('new'));
controller.set("model", model);
}
});

Datei anzeigen

@ -0,0 +1,19 @@
import CustomWizard from '../models/custom-wizard';
export default Discourse.Route.extend({
model() {
return CustomWizard.findAll();
},
setupController(controller, model){
controller.set("model", model.toArray());
},
actions: {
willTransition(transition) {
if (transition.intent.name === 'adminWizardsCustom') {
this.refresh();
}
}
}
});

Datei anzeigen

@ -0,0 +1,5 @@
export default Discourse.Route.extend({
redirect() {
this.transitionTo('adminWizardsCustom');
}
});

Datei anzeigen

@ -0,0 +1,24 @@
<div class="form-horizontal">
<div>
<label for="name">{{i18n 'admin.wizard.name'}}</label>
{{text-field name="name" value=model.name placeholderKey="admin.wizard.name_placeholder"}}
</div>
{{#if model.steps}}
{{#each model.steps as |s|}}
{{wizard-custom-step step=s}}
{{/each}}
{{/if}}
{{d-button action='addStep' label='admin.wizard.add_step'}}
<div class='buttons'>
<button {{action "save"}} disabled={{disableSave}} class='btn btn-primary'>{{i18n 'admin.wizard.save'}}</button>
{{#unless new}}
<button {{action "remove"}} class='btn btn-danger'>{{d-icon "trash-o"}}{{i18n 'admin.wizard.remove'}}</button>
{{/unless}}
<span class="saving {{unless savingStatus 'hidden'}}">{{savingStatus}}</span>
</div>
</div>

Datei anzeigen

@ -0,0 +1,7 @@
<div class="groups-type-index">
<div>
{{#link-to 'adminWizard' 'new' class="btn"}}
{{d-icon "plus"}} {{i18n 'admin.wizard.new'}}
{{/link-to}}
</div>
</div>

Datei anzeigen

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

Datei anzeigen

@ -0,0 +1,7 @@
{{#admin-nav}}
{{nav-item route='adminWizardsCustom' label='admin.wizard.label'}}
{{/admin-nav}}
<div class="admin-container">
{{outlet}}
</div>

Datei anzeigen

@ -0,0 +1,17 @@
{{combo-box value=action.data content=stepFields valueAttribute='id' nameProperty='label'}}
{{combo-box value=action.target content=targets}}
{{#if isTopic}}
{{category-select-box value=action.target.category_id tabindex="3"}}
{{/if}}
{{#if isEmail}}
<span>{{i18n 'admin.wizard.action.email'}}</span>
{{input type='text' value=action.target.email}}
{{/if}}
{{#if isProfile}}
<span>{{i18n 'admin.wizard.action.email'}}</span>
{{combo-box value=action.target.profile_field content=profileFields}}
{{/if}}
{{#if isBadge}}
<span>{{i18n 'admin.wizard.action.badge'}}</span>
{{combo-box value=action.target.badge content=badges}}
{{/if}}

Datei anzeigen

@ -0,0 +1,25 @@
<div for={{field.id}}>
<div>
<span>{{i18n 'admin.wizard.field.label'}}</span>
{{text-field name="label" value=field.label}}
</div>
<div>
<span>{{i18n 'admin.wizard.field.description'}}</span>
{{text-field name="description" value=field.description}}
</div>
<div>
<span>{{i18n 'admin.wizard.field.type'}}</span>
{{combo-box value=field.type content=fieldTypes}}
</div>
{{#if isDropdown}}
<span>{{i18n 'admin.wizard.field.choices_label'}}</span>
{{#each field.choices as |c|}}
{{input type='text' value=c}}
{{/each}}
{{d-button action='addChoice' label='admin.wizard.field.add_choice'}}
{{/if}}
<div>
<span>{{i18n 'admin.wizard.field.required'}}</span>
{{input type='checkbox' checked=field.required}}
</div>
</div>

Datei anzeigen

@ -0,0 +1,30 @@
<div>
<label for="title">{{i18n 'admin.wizard.step.title'}}</label>
{{text-field name="title" value=step.title placeholderKey="admin.wizard.step.title_placeholder"}}
</div>
<div>
<label for="banner">{{i18n 'admin.wizard.step.banner'}}</label>
{{input name="banner" value=step.banner placeholderKey="admin.wizard.step.banner_placeholder"}}
</div>
<div>
<label for="description">{{i18n 'admin.wizard.step.description'}}</label>
{{input name="description" value=step.description placeholderKey="admin.wizard.step.description_placeholder"}}
</div>
{{#each step.fields as |f|}}
{{wizard-custom-field field=f}}
{{/each}}
{{d-button action='addField' label='admin.wizard.step.add_field'}}
{{#each step.actions as |a|}}
{{wizard-custom-action action=a stepFields=step.fields}}
{{/each}}
{{#if allowAddAction}}
{{d-button action='addAction' label='admin.wizard.step.add_action'}}
{{/if}}
{{d-button action='removeStep' label='admin.wizard.remove_step'}}

Datei anzeigen

@ -0,0 +1,3 @@
.wizards-nav-button {
@extend .nav-pills;
}

31
config/locales/client.en.yml Normale Datei
Datei anzeigen

@ -0,0 +1,31 @@
en:
admin_js:
admin:
wizard:
label: "Wizards"
new: "New"
custom_label: "Custom"
name: "Name"
name_placeholder: "name of the wizard"
add_step: "Add Step"
remove_step: "Remove Step"
save: "Save Wizard"
remove: "Delete Wizard"
step:
title: "Step Title"
title_placeholder: "This will appear at the top of the step"
banner: "Step Banner"
banner_placeholder: "This image will appear under the title"
description: "Step Description"
description_placeholder: "This will appear underneath the title and / or title"
add_field: "Add Field"
add_action: "Add Action"
field:
label: "Field Name"
description: "Field Description"
type: "Field Type"
choices_label: "Dropdown Choices"
add_choice: "Add Choice"
required: "Field Required"
action:
email: "Email"

53
controllers/admin.rb Normale Datei
Datei anzeigen

@ -0,0 +1,53 @@
class CustomWizard::AdminController < ::ApplicationController
before_filter :ensure_logged_in
before_filter :ensure_admin
def index
render nothing: true
end
def save
params.require(:name)
params.permit(:steps)
wizard = { name: params[:name] }
wizard['steps'] = params[:steps] if params[:steps]
key = params[:name].downcase
PluginStore.set('custom_wizards', key, wizard)
render json: success_json
end
def remove
params.require(:name)
key = params[:name].downcase
PluginStore.remove('custom_wizards', key)
render json: success_json
end
def find
params.require(:name)
key = params[:name].downcase
wizard = PluginStore.get('custom_wizards', key)
render json: success_json.merge(wizard: wizard)
end
def all
rows = PluginStoreRow.where(plugin_name: 'custom_wizards')
wizards = rows ? [*rows].map do |r|
CustomWizard::Wizard.new(r.value)
end : []
render json: success_json.merge(wizards: wizards)
end
end

11
controllers/steps.rb Normale Datei
Datei anzeigen

@ -0,0 +1,11 @@
class CustomWizard::StepsController < ::ApplicationController
def all
respond_to do |format|
format.json do
wizard = CustomWizard::Builder.new(current_user, params[:wizard_id]).build
render_serialized(wizard, WizardSerializer)
end
format.html {}
end
end
end

35
lib/builder.rb Normale Datei
Datei anzeigen

@ -0,0 +1,35 @@
class CustomWizard::Builder
def initialize(user, wizard_id)
@wizard = Wizard.new(user)
@template = PluginStore.get('custom_wizard', wizard_id)
end
def build
@template.each do |s|
@wizard.append_step(s.title) do |step|
step.banner = s.banner if s.banner
s.fields.each do |f|
field = step.add_field(id: f.id,
type: f.type,
required: f.required,
value: f.value)
if f.type == 'dropdown'
f.choices.each do |c|
field.add_choice(c)
end
end
end
step.on_update do |updater|
puts "UPDATER: #{updater}"
## do stuff
end
end
end
@wizard
end
end

10
lib/wizard.rb Normale Datei
Datei anzeigen

@ -0,0 +1,10 @@
class CustomWizard::Wizard
attr_reader :name, :steps
def initialize(data)
parsed = ::JSON.parse(data)
@name = parsed['name']
@steps = JSON.parse(parsed['steps'])
end
end

38
plugin.rb Normale Datei
Datei anzeigen

@ -0,0 +1,38 @@
# name: discourse-custom-wizard
# about: Allows the admins to create custom user input forms
# version: 0.1
# authors: Angus McLeod
register_asset 'stylesheets/custom-wizard.scss'
after_initialize do
require_dependency "application_controller"
module ::CustomWizard
class Engine < ::Rails::Engine
engine_name "custom_wizard"
isolate_namespace CustomWizard
end
end
CustomWizard::Engine.routes.draw do
get 'custom' => 'admin#index'
get 'custom/new' => 'admin#index'
get 'custom/all' => "admin#all"
get 'custom/:name' => "admin#find"
put 'custom/:name' => "admin#save"
delete 'custom/:name' => "admin#remove"
end
require_dependency 'admin_constraint'
Discourse::Application.routes.append do
namespace :admin, constraints: AdminConstraint.new do
mount ::CustomWizard::Engine, at: 'wizards'
end
end
load File.expand_path('../lib/builder.rb', __FILE__)
load File.expand_path('../lib/wizard.rb', __FILE__)
load File.expand_path('../controllers/steps.rb', __FILE__)
load File.expand_path('../controllers/admin.rb', __FILE__)
end