Spiegel von
https://github.com/paviliondev/discourse-custom-wizard.git
synchronisiert 2024-11-21 17:00:29 +01:00
Init unfinshed
Dieser Commit ist enthalten in:
Commit
ebd026887c
25 geänderte Dateien mit 495 neuen und 0 gelöschten Zeilen
11
assets/javascripts/discourse/components/wizard-custom-action.js.es6
Normale Datei
11
assets/javascripts/discourse/components/wizard-custom-action.js.es6
Normale Datei
|
@ -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)
|
||||
},
|
||||
});
|
21
assets/javascripts/discourse/components/wizard-custom-field.js.es6
Normale Datei
21
assets/javascripts/discourse/components/wizard-custom-field.js.es6
Normale Datei
|
@ -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() {
|
||||
|
||||
}
|
||||
}
|
||||
});
|
26
assets/javascripts/discourse/components/wizard-custom-step.js.es6
Normale Datei
26
assets/javascripts/discourse/components/wizard-custom-step.js.es6
Normale Datei
|
@ -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'));
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
{{nav-item route='adminWizards' label='admin.wizard.label'}}
|
26
assets/javascripts/discourse/controllers/admin-wizard.js.es6
Normale Datei
26
assets/javascripts/discourse/controllers/admin-wizard.js.es6
Normale Datei
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
10
assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6
Normale Datei
10
assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6
Normale Datei
|
@ -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 });
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
6
assets/javascripts/discourse/initializers/wizard-edits.js.es6
Normale Datei
6
assets/javascripts/discourse/initializers/wizard-edits.js.es6
Normale Datei
|
@ -0,0 +1,6 @@
|
|||
export default {
|
||||
name: 'wizard-edits',
|
||||
initialize() {
|
||||
|
||||
}
|
||||
};
|
41
assets/javascripts/discourse/models/custom-wizard.js.es6
Normale Datei
41
assets/javascripts/discourse/models/custom-wizard.js.es6
Normale Datei
|
@ -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;
|
23
assets/javascripts/discourse/routes/admin-wizard.js.es6
Normale Datei
23
assets/javascripts/discourse/routes/admin-wizard.js.es6
Normale Datei
|
@ -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);
|
||||
}
|
||||
});
|
19
assets/javascripts/discourse/routes/admin-wizards-custom.js.es6
Normale Datei
19
assets/javascripts/discourse/routes/admin-wizards-custom.js.es6
Normale Datei
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
5
assets/javascripts/discourse/routes/admin-wizards.js.es6
Normale Datei
5
assets/javascripts/discourse/routes/admin-wizards.js.es6
Normale Datei
|
@ -0,0 +1,5 @@
|
|||
export default Discourse.Route.extend({
|
||||
redirect() {
|
||||
this.transitionTo('adminWizardsCustom');
|
||||
}
|
||||
});
|
24
assets/javascripts/discourse/templates/admin-wizard.hbs
Normale Datei
24
assets/javascripts/discourse/templates/admin-wizard.hbs
Normale Datei
|
@ -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>
|
|
@ -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>
|
15
assets/javascripts/discourse/templates/admin-wizards-custom.hbs
Normale Datei
15
assets/javascripts/discourse/templates/admin-wizards-custom.hbs
Normale Datei
|
@ -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>
|
7
assets/javascripts/discourse/templates/admin-wizards.hbs
Normale Datei
7
assets/javascripts/discourse/templates/admin-wizards.hbs
Normale Datei
|
@ -0,0 +1,7 @@
|
|||
{{#admin-nav}}
|
||||
{{nav-item route='adminWizardsCustom' label='admin.wizard.label'}}
|
||||
{{/admin-nav}}
|
||||
|
||||
<div class="admin-container">
|
||||
{{outlet}}
|
||||
</div>
|
|
@ -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}}
|
|
@ -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>
|
|
@ -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'}}
|
3
assets/stylesheets/custom-wizard.scss
Normale Datei
3
assets/stylesheets/custom-wizard.scss
Normale Datei
|
@ -0,0 +1,3 @@
|
|||
.wizards-nav-button {
|
||||
@extend .nav-pills;
|
||||
}
|
31
config/locales/client.en.yml
Normale Datei
31
config/locales/client.en.yml
Normale Datei
|
@ -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
53
controllers/admin.rb
Normale Datei
|
@ -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
11
controllers/steps.rb
Normale Datei
|
@ -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
35
lib/builder.rb
Normale Datei
|
@ -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
10
lib/wizard.rb
Normale Datei
|
@ -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
38
plugin.rb
Normale Datei
|
@ -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
|
Laden …
In neuem Issue referenzieren