0
0
Fork 1
Spiegel von https://github.com/paviliondev/discourse-custom-wizard.git synchronisiert 2024-11-10 04:12:53 +01:00
Dieser Commit ist enthalten in:
Angus McLeod 2020-04-08 17:59:54 +10:00
Ursprung 98f9215d65
Commit 024ab63006
13 geänderte Dateien mit 281 neuen und 132 gelöschten Zeilen

Datei anzeigen

@ -1,4 +1,4 @@
import { default as discourseComputed, observes } from 'discourse-common/utils/decorators'; import { default as discourseComputed, observes, on } from 'discourse-common/utils/decorators';
import { equal, empty, or } from "@ember/object/computed"; import { equal, empty, or } from "@ember/object/computed";
import { actionTypes, generateName, selectKitContent } from '../lib/wizard'; import { actionTypes, generateName, selectKitContent } from '../lib/wizard';
import Component from "@ember/component"; import Component from "@ember/component";
@ -21,16 +21,14 @@ export default Component.extend({
publicTopicFields: or('createTopic', 'openComposer'), publicTopicFields: or('createTopic', 'openComposer'),
showSkipRedirect: or('createTopic', 'sendMessage'), showSkipRedirect: or('createTopic', 'sendMessage'),
@observes('action.type')
setupDefaults() {
if (this.action.type) {
this.set('action.label', generateName(this.action.type));
};
},
@discourseComputed('wizard.steps') @discourseComputed('wizard.steps')
runAfterContent(steps) { runAfterContent(steps) {
let content = steps.map(s => ({ id: s.id, name: s.label })); let content = steps.map(function(step) {
return {
id: step.id,
name: step.title || step.id
};
});
content.unshift({ content.unshift({
id: 'wizard_completion', id: 'wizard_completion',

Datei anzeigen

@ -1,4 +1,5 @@
import { default as discourseComputed, on, observes } from 'discourse-common/utils/decorators'; import { default as discourseComputed, on, observes } from 'discourse-common/utils/decorators';
import { generateName, defaultProperties } from '../lib/wizard';
import { notEmpty } from "@ember/object/computed"; import { notEmpty } from "@ember/object/computed";
import { scheduleOnce, bind } from "@ember/runloop"; import { scheduleOnce, bind } from "@ember/runloop";
import EmberObject from "@ember/object"; import EmberObject from "@ember/object";
@ -35,23 +36,29 @@ export default Component.extend({
@discourseComputed('type') @discourseComputed('type')
header: (type) => `admin.wizard.${type}.header`, header: (type) => `admin.wizard.${type}.header`,
@discourseComputed('current', 'items.@each.id', 'items.@each.label') @discourseComputed('current', 'items.@each.id', 'items.@each.type')
links(current, items) { links(current, items) {
if (!items) return; if (!items) return;
return items.map((item) => { return items.map((item) => {
if (item) { if (item) {
const id = item.id; let link = {
const type = this.type; id: item.id
const label = item.label || item.title || id; }
let link = { id, label };
let label = item.label || item.title || item.id;
if (this.generateLabels && item.type) {
label = generateName(item.type);
}
link.label = label;
let classes = 'btn'; let classes = 'btn';
if (current && item.id === current.id) { if (current && item.id === current.id) {
classes += ' btn-primary'; classes += ' btn-primary';
}; };
link['classes'] = classes; link.classes = classes;
return link; return link;
} }
@ -63,15 +70,25 @@ export default Component.extend({
const items = this.items; const items = this.items;
const type = this.type; const type = this.type;
const newId = `${type}_${items.length + 1}`; const newId = `${type}_${items.length + 1}`;
let params = { id: newId, isNew: true };
let params = {
id: newId,
isNew: true
};
if (type === 'step') { if (type === 'step') {
params['fields'] = A(); params.fields = A();
params['actions'] = A();
}; };
if (defaultProperties[type]) {
Object.keys(defaultProperties[type]).forEach(key => {
params[key] = defaultProperties[type][key];
});
}
const newItem = EmberObject.create(params); const newItem = EmberObject.create(params);
items.pushObject(newItem); items.pushObject(newItem);
this.set('current', newItem); this.set('current', newItem);
}, },

Datei anzeigen

@ -18,12 +18,7 @@ export default Controller.extend({
if (currentStep) { if (currentStep) {
const fields = currentStep.fields; const fields = currentStep.fields;
const actions = currentStep.actions; this.set('currentField', fields && fields.length ? fields[0] : null)
this.setProperties({
currentField: fields.length ? fields[0] : null,
currentAction: actions.length ? actions[0] : null
});
} }
scheduleOnce('afterRender', () => ($("body").addClass('admin-wizard'))); scheduleOnce('afterRender', () => ($("body").addClass('admin-wizard')));
@ -98,6 +93,7 @@ export default Controller.extend({
this.send("refreshWizard"); this.send("refreshWizard");
} }
}).catch((result) => { }).catch((result) => {
console.log('catch result: ', result)
this.set('saving', false); this.set('saving', false);
this.set('error', I18n.t(`admin.wizard.error.${result.error}`, result.errorParams || {})); this.set('error', I18n.t(`admin.wizard.error.${result.error}`, result.errorParams || {}));
later(() => this.set('error', null), 10000); later(() => this.set('error', null), 10000);

Datei anzeigen

@ -85,7 +85,7 @@ function wizardHasAdvanced(property, value) {
} }
function stepHasAdvanced(property, value) { function stepHasAdvanced(property, value) {
return advancedProperties.step[property] && present(value); return advancedProperties.steps[property] && present(value);
} }
function objectHasAdvanced(params, type) { function objectHasAdvanced(params, type) {
@ -96,17 +96,39 @@ function objectHasAdvanced(params, type) {
}); });
} }
/// to be removed
function actionPatch(json) {
let actions = json.actions || [];
json.steps.forEach(step => {
if (step.actions && step.actions.length) {
step.actions.forEach(action => {
action.run_after = 'wizard_completion';
actions.push(action);
});
}
});
json.actions = actions;
return json;
}
///
function buildProperties(json) { function buildProperties(json) {
let props = { let props = {
steps: A(); steps: A(),
action: A(); actions: A()
}; };
if (present(json)) { if (present(json)) {
props.id = json.id; props.id = json.id;
props.existingId = true; props.existingId = true;
properties.wizard.forEach((p) => { // to fix
properties.wizard
.filter(p => ['steps', 'actions'].indexOf(p) === -1)
.forEach((p) => {
props[p] = buildProperty(json, p, 'wizard'); props[p] = buildProperty(json, p, 'wizard');
if (wizardHasAdvanced(p, json[p])) { if (wizardHasAdvanced(p, json[p])) {
@ -120,7 +142,7 @@ function buildProperties(json) {
isNew: false isNew: false
}; };
properties.step.forEach((p) => { properties.steps.forEach((p) => {
stepParams[p] = buildProperty(stepJson, p, 'wizard'); stepParams[p] = buildProperty(stepJson, p, 'wizard');
if (stepHasAdvanced(p, stepJson[p])) { if (stepHasAdvanced(p, stepJson[p])) {
@ -132,9 +154,9 @@ function buildProperties(json) {
if (present(stepJson.fields)) { if (present(stepJson.fields)) {
stepJson.fields.forEach((f) => { stepJson.fields.forEach((f) => {
let params = buildObject(f, 'field'); let params = buildObject(f, 'fields');
if (objectHasAdvanced(params, 'field')) { if (objectHasAdvanced(params, 'fields')) {
params.showAdvanced = true; params.showAdvanced = true;
} }
@ -142,17 +164,21 @@ function buildProperties(json) {
}); });
} }
steps.pushObject( props.steps.pushObject(
EmberObject.create(stepParams) EmberObject.create(stepParams)
); );
}); });
}; };
// to be removed
json = actionPatch(json);
// to be removed
if (present(json.actions)) { if (present(json.actions)) {
json.actions.forEach((a) => { json.actions.forEach((a) => {
let params = buildObject(a, 'action'); let params = buildObject(a, 'actions');
if (objectHasAdvanced(params, 'action')) { if (objectHasAdvanced(params, 'actions')) {
params.showAdvanced = true; params.showAdvanced = true;
} }
@ -177,7 +203,7 @@ function buildProperties(json) {
} }
export { export {
buildStepJson, buildProperties,
buildJson, present,
buildProperties mapped
} }

Datei anzeigen

@ -116,30 +116,93 @@ const actionProperties = [
const properties = { const properties = {
wizard: wizardProperties, wizard: wizardProperties,
step: stepProperties, steps: stepProperties,
field: fieldProperties, fields: fieldProperties,
action: actionProperties actions: actionProperties
} }
const objectArrays = [ const actionTypeProperties = {
'steps', create_topic: [
'fields', 'id',
'actions' 'type',
]; 'run_after',
'title',
'post',
'post_builder',
'post_template',
'category',
'tags',
'skip_redirect',
'custom_fields'
],
send_message: [
'id',
'type',
'run_after',
'title',
'post',
'post_builder',
'post_template',
'skip_redirect',
'custom_fields',
'required',
'recipient'
],
open_composer: [
'id',
'type',
'run_after',
'title',
'post',
'post_builder',
'post_template',
'category',
'tags',
'custom_fields'
],
update_profile: [
'id',
'type',
'run_after',
'profile_updates',
'custom_fields'
],
add_to_group: [
'id',
'type',
'run_after',
'group'
],
route_to: [
'id',
'type',
'run_after',
'url',
'code'
],
send_to_api: [
'id',
'type',
'run_after',
'api',
'api_endpoint',
'api_body'
]
}
const mappedProperties = { const mappedProperties = {
wizard: [ wizard: [
'permitted' 'permitted'
], ],
step: [ steps: [
'required_data', 'required_data',
'permitted_params' 'permitted_params'
], ],
field: [ fields: [
'prefill', 'prefill',
'content' 'content'
], ],
action: [ actions: [
'title', 'title',
'category', 'category',
'tags', 'tags',
@ -151,6 +214,12 @@ const mappedProperties = {
] ]
} }
const defaultProperties = {
action: {
run_after: 'wizard_completion'
}
}
const advancedFieldTypes = [ const advancedFieldTypes = [
'category', 'category',
'tag', 'tag',
@ -176,11 +245,11 @@ const actionTypes = [
}); });
const advancedProperties = { const advancedProperties = {
step: [ steps: [
'required_data', 'required_data',
'permitted_params' 'permitted_params'
], ],
field: advancedFieldTypes.reduce( fields: advancedFieldTypes.reduce(
function(map, type) { function(map, type) {
map[type] = advancedFieldProperties; map[type] = advancedFieldProperties;
if (type === 'category') { if (type === 'category') {
@ -189,7 +258,7 @@ const advancedProperties = {
return map; return map;
}, {} }, {}
), ),
action: actionTypes.reduce( actions: actionTypes.reduce(
function(map, type) { function(map, type) {
if (type === 'route_to') { if (type === 'route_to') {
map[type] = ['code']; map[type] = ['code'];
@ -212,10 +281,11 @@ export {
camelCase, camelCase,
snakeCase, snakeCase,
properties, properties,
objectArrays,
wizardProperties, wizardProperties,
mappedProperties, mappedProperties,
profileFields, profileFields,
advancedProperties, advancedProperties,
actionTypes actionTypes,
actionTypeProperties,
defaultProperties
}; };

Datei anzeigen

@ -1,7 +1,7 @@
import { ajax } from 'discourse/lib/ajax'; import { ajax } from 'discourse/lib/ajax';
import EmberObject from "@ember/object"; import EmberObject from "@ember/object";
import { buildJson, buildProperties, present } from '../lib/wizard-json'; import { buildProperties, present, mapped } from '../lib/wizard-json';
import { properties, arrays, camelCase, snakeCase } from '../lib/wizard'; import { properties, actionTypeProperties, camelCase, snakeCase } from '../lib/wizard';
import { Promise } from "rsvp"; import { Promise } from "rsvp";
const jsonStrings = ['api_body']; const jsonStrings = ['api_body'];
@ -14,13 +14,13 @@ const CustomWizard = EmberObject.extend({
let json = this.buildJson(this, 'wizard'); let json = this.buildJson(this, 'wizard');
if (json.error) { if (json.error) {
reject({ eror: json.error }); reject({ error: json.error });
} }
ajax("/admin/wizards/custom/save", { ajax("/admin/wizards/custom/save", {
type: 'PUT', type: 'PUT',
data: { data: {
wizard: JSON.stringify(wizardJson) wizard: JSON.stringify(json)
} }
}).then((result) => { }).then((result) => {
if (result.error) { if (result.error) {
@ -33,34 +33,43 @@ const CustomWizard = EmberObject.extend({
}, },
buildJson(object, type, result = {}) { buildJson(object, type, result = {}) {
for (let property of properties[type]) { let allowedProperties;
if (type === 'actions') {
if (!object.type) {
result.error = {
type: 'required',
params: {
type,
property: 'type'
}
}
return result;
}
allowedProperties = actionTypeProperties[object.type];
} else {
allowedProperties = properties[type];
}
for (let property of allowedProperties) {
let value = object.get(property); let value = object.get(property);
if (objectArrays[type]) {
result[property] = [];
for (let obj of value) {
let obj = this.buildJson(value, property, result);
if (obj.error) {
result.error = r.error;
break;
} else {
result[property].push(obj);
}
}
}
if (required[property] && !value) { if (required[property] && !value) {
result.error = 'required' result.error = {
result.errorParams = { type, property }; type: 'required',
params: { type, property }
}
} }
if (dependent[property] && !properties[type][dependent[property]]) { let dependentOn = dependent[property];
result.error = 'dependent'; if (dependentOn && value && !object[dependentOn]) {
result.errorParams = { result.error = {
dependentProperty: properties[type][dependent[property]], type: 'dependent',
property params: {
property,
dependentOn
}
} }
} }
@ -68,24 +77,43 @@ const CustomWizard = EmberObject.extend({
try { try {
value = JSON.parse(value); value = JSON.parse(value);
} catch (e) { } catch (e) {
result.error = 'invalid'; result.error = {
result.errorParams = { property }; type: 'invalid',
params: { type, property }
} }
} }
if (mapped(property, type)) {
value = this.buildMappedJson(value);
} }
if (result.error) { if (result.error) {
break; break;
} else if (value) { }
if (properties[property]) {
result[property] = [];
for (let item of value) {
let itemParams = this.buildJson(item, property);
if (itemParams.error) {
result.error = r.error;
break;
} else {
result[property].push(itemParams);
}
}
} else {
if (mapped(property, type)) {
value = this.buildMappedJson(value);
}
if (value !== undefined && value !== null) {
result[property] = value; result[property] = value;
} }
}); }
};
return result; return result;
} },
buildMappedJson(inputs) { buildMappedJson(inputs) {
if (!inputs || !inputs.length) return false; if (!inputs || !inputs.length) return false;

Datei anzeigen

@ -91,12 +91,12 @@ export default DiscourseRoute.extend({
setupController(controller, model) { setupController(controller, model) {
const newWizard = this.get('newWizard'); const newWizard = this.get('newWizard');
const steps = model.get('steps') || [];
controller.setProperties({ controller.setProperties({
newWizard, newWizard,
model, model,
currentStep: steps[0] currentStep: model.steps[0],
currentAction: model.actions[0]
}); });
}, },

Datei anzeigen

@ -150,7 +150,10 @@
{{/if}} {{/if}}
</div> </div>
{{wizard-links type="step" current=currentStep items=model.steps}} {{wizard-links
type="step"
current=currentStep
items=model.steps}}
{{#if currentStep}} {{#if currentStep}}
{{wizard-custom-step {{wizard-custom-step
@ -160,7 +163,11 @@
wizardFields=wizardFields}} wizardFields=wizardFields}}
{{/if}} {{/if}}
{{wizard-links type="action" current=currentAction items=model.actions}} {{wizard-links
type="action"
current=currentAction
items=model.actions
generateLabels=true}}
{{#if currentAction}} {{#if currentAction}}
{{wizard-custom-action {{wizard-custom-action

Datei anzeigen

@ -16,7 +16,7 @@
<div class="setting"> <div class="setting">
<div class="setting-label"> <div class="setting-label">
<label>{{i18n "admin.wizard.run_after"}}</label> <label>{{i18n "admin.wizard.action.run_after.label"}}</label>
</div> </div>
<div class="setting-value"> <div class="setting-value">

Datei anzeigen

@ -97,7 +97,7 @@ en:
error: error:
required: "{{type}} requires {{property}}" required: "{{type}} requires {{property}}"
invalid: "{{property}} is invalid" invalid: "{{property}} is invalid"
dependent: "{{dependentProperty}} is dependent on {{property}}" dependent: "{{property}} is dependent on {{dependentOn}}"
step: step:
header: "Steps" header: "Steps"
@ -152,7 +152,7 @@ en:
run_after: run_after:
label: "Run After" label: "Run After"
wizard_completion: "Wizard completes" wizard_completion: "Wizard Completion"
custom_fields: custom_fields:
label: "Custom" label: "Custom"

Datei anzeigen

@ -151,18 +151,22 @@ class CustomWizard::AdminController < ::ApplicationController
error = check_depdendent(step, error) error = check_depdendent(step, error)
break if error.present? break if error.present?
if step['fields'].present?
step['fields'].each do |field| step['fields'].each do |field|
error = check_required(field, :field, error) error = check_required(field, :field, error)
error = check_depdendent(field, error) error = check_depdendent(field, error)
break if error.present? break if error.present?
end end
end end
end
if wizard['actions'].present?
wizard['actions'].each do |action| wizard['actions'].each do |action|
error = check_required(action, :action, error) error = check_required(action, :action, error)
error = check_depdendent(action, error) error = check_depdendent(action, error)
break if error.present? break if error.present?
end end
end
if error if error
{ error: error } { error: error }
@ -211,8 +215,8 @@ class CustomWizard::AdminController < ::ApplicationController
return after_time_validation[:error] if after_time_validation[:error] return after_time_validation[:error] if after_time_validation[:error]
wizard['steps'].each do |step| wizard['steps'].each do |step|
if s['raw_description'] if step['raw_description']
step['description'] = PrettyText.cook(s['raw_description']) step['description'] = PrettyText.cook(step['raw_description'])
end end
end end

Datei anzeigen

@ -151,8 +151,8 @@ class CustomWizard::Builder
if @actions.present? if @actions.present?
@actions.each do |action| @actions.each do |action|
if (action.run_after === updater.step.id) || if (action['run_after'] === updater.step.id) ||
(final_step && (!action.run_after || (action.run_after === 'wizard_completion'))) (final_step && (!action['run_after'] || (action['run_after'] === 'wizard_completion')))
CustomWizard::Action.new( CustomWizard::Action.new(
action: action, action: action,

Datei anzeigen

@ -2,7 +2,6 @@ class CustomWizard::Template
attr_reader :id, attr_reader :id,
:name, :name,
:steps,
:background, :background,
:save_submissions, :save_submissions,
:multiple_submissions, :multiple_submissions,
@ -13,7 +12,9 @@ class CustomWizard::Template
:after_time_scheduled, :after_time_scheduled,
:required, :required,
:theme_id, :theme_id,
:permitted :permitted,
:steps,
:actions
def initialize(data) def initialize(data)
data = data.is_a?(String) ? ::JSON.parse(data) : data data = data.is_a?(String) ? ::JSON.parse(data) : data
@ -22,7 +23,6 @@ class CustomWizard::Template
@id = data['id'] @id = data['id']
@name = data['name'] @name = data['name']
@steps = data['steps']
@background = data['background'] @background = data['background']
@save_submissions = data['save_submissions'] || false @save_submissions = data['save_submissions'] || false
@multiple_submissions = data['multiple_submissions'] || false @multiple_submissions = data['multiple_submissions'] || false
@ -39,5 +39,8 @@ class CustomWizard::Template
theme = Theme.find_by(name: data['theme']) theme = Theme.find_by(name: data['theme'])
@theme_id = theme.id if theme @theme_id = theme.id if theme
end end
@steps = data['steps']
@actions = data['actions']
end end
end end