Step and field conditionality (#87)
* Re structure builder logic to allow for step conditionality Concerns - Performance. Look at whether the additional build in the steps controller can be reduced - Does not work if applied to the last step. - Certain conditions will not work with the first step(?) - How should this be scoped to known functionality? * Add indexes and conditions to steps and fields * Complete and add spec * Complete backend * Complete step conditionality and field indexing * Fix failing spec * Update coverage * Apply rubocop * Apply prettier * Apply prettier to wizard js * Fix schema issues created in merge * Remove setting label for force_final * Improve client wizard cache naming * Improve steps controller and spec conditionality * Improve final step attribute naming * Fix failing spec * Linting * Add one more final step test * Linting * Fix eslint issues * Apply prettier * Linting, syntax, merge and copy cleanups * Update wizard-admin.scss * Fix template linting * Rubocop fixes
Dieser Commit ist enthalten in:
Ursprung
ec21c8e274
Commit
ceef3f4bc9
70 geänderte Dateien mit 1387 neuen und 573 gelöschten Zeilen
|
@ -107,6 +107,40 @@ export default Component.extend(UndoChanges, {
|
||||||
return this.setupTypeOutput(fieldType, options);
|
return this.setupTypeOutput(fieldType, options);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@discourseComputed("step.index")
|
||||||
|
fieldConditionOptions(stepIndex) {
|
||||||
|
const options = {
|
||||||
|
inputTypes: "validation",
|
||||||
|
context: "field",
|
||||||
|
textSelection: "value",
|
||||||
|
userFieldSelection: true,
|
||||||
|
groupSelection: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (stepIndex > 0) {
|
||||||
|
options.wizardFieldSelection = true;
|
||||||
|
options.wizardActionSelection = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("step.index")
|
||||||
|
fieldIndexOptions(stepIndex) {
|
||||||
|
const options = {
|
||||||
|
context: "field",
|
||||||
|
userFieldSelection: true,
|
||||||
|
groupSelection: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (stepIndex > 0) {
|
||||||
|
options.wizardFieldSelection = true;
|
||||||
|
options.wizardActionSelection = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
imageUploadDone(upload) {
|
imageUploadDone(upload) {
|
||||||
this.set("field.image", upload.url);
|
this.set("field.image", upload.url);
|
||||||
|
|
|
@ -1,8 +1,27 @@
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
classNames: "wizard-custom-step",
|
classNames: "wizard-custom-step",
|
||||||
|
|
||||||
|
@discourseComputed("step.index")
|
||||||
|
stepConditionOptions(stepIndex) {
|
||||||
|
const options = {
|
||||||
|
inputTypes: "validation",
|
||||||
|
context: "step",
|
||||||
|
textSelection: "value",
|
||||||
|
userFieldSelection: true,
|
||||||
|
groupSelection: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (stepIndex > 0) {
|
||||||
|
options["wizardFieldSelection"] = true;
|
||||||
|
options["wizardActionSelection"] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
bannerUploadDone(upload) {
|
bannerUploadDone(upload) {
|
||||||
this.set("step.banner", upload.url);
|
this.set("step.banner", upload.url);
|
||||||
|
|
|
@ -38,6 +38,7 @@ export default Component.extend({
|
||||||
const items = this.items;
|
const items = this.items;
|
||||||
const item = items.findBy("id", itemId);
|
const item = items.findBy("id", itemId);
|
||||||
items.removeObject(item);
|
items.removeObject(item);
|
||||||
|
item.set("index", newIndex);
|
||||||
items.insertAt(newIndex, item);
|
items.insertAt(newIndex, item);
|
||||||
scheduleOnce("afterRender", this, () => this.applySortable());
|
scheduleOnce("afterRender", this, () => this.applySortable());
|
||||||
},
|
},
|
||||||
|
@ -90,22 +91,14 @@ export default Component.extend({
|
||||||
|
|
||||||
params.isNew = true;
|
params.isNew = true;
|
||||||
|
|
||||||
let next = 1;
|
let index = 0;
|
||||||
|
|
||||||
if (items.length) {
|
if (items.length) {
|
||||||
next =
|
index = items.length;
|
||||||
Math.max.apply(
|
|
||||||
Math,
|
|
||||||
items.map((i) => {
|
|
||||||
let parts = i.id.split("_");
|
|
||||||
let lastPart = parts[parts.length - 1];
|
|
||||||
return isNaN(lastPart) ? 0 : lastPart;
|
|
||||||
})
|
|
||||||
) + 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let id = `${itemType}_${next}`;
|
params.index = index;
|
||||||
|
|
||||||
|
let id = `${itemType}_${index + 1}`;
|
||||||
if (itemType === "field") {
|
if (itemType === "field") {
|
||||||
id = `${this.parentId}_${id}`;
|
id = `${this.parentId}_${id}`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,54 +23,69 @@ function castCase(property, value) {
|
||||||
return property.indexOf("_type") > -1 ? camelCase(value) : value;
|
return property.indexOf("_type") > -1 ? camelCase(value) : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildProperty(json, property, type) {
|
function buildMappedProperty(value) {
|
||||||
let value = json[property];
|
let inputs = [];
|
||||||
|
|
||||||
if (mapped(property, type) && present(value) && value.constructor === Array) {
|
value.forEach((inputJson) => {
|
||||||
let inputs = [];
|
let input = {};
|
||||||
|
|
||||||
value.forEach((inputJson) => {
|
Object.keys(inputJson).forEach((inputKey) => {
|
||||||
let input = {};
|
if (inputKey === "pairs") {
|
||||||
|
let pairs = [];
|
||||||
|
let pairCount = inputJson.pairs.length;
|
||||||
|
|
||||||
Object.keys(inputJson).forEach((inputKey) => {
|
inputJson.pairs.forEach((pairJson) => {
|
||||||
if (inputKey === "pairs") {
|
let pair = {};
|
||||||
let pairs = [];
|
|
||||||
let pairCount = inputJson.pairs.length;
|
|
||||||
|
|
||||||
inputJson.pairs.forEach((pairJson) => {
|
Object.keys(pairJson).forEach((pairKey) => {
|
||||||
let pair = {};
|
pair[pairKey] = castCase(pairKey, pairJson[pairKey]);
|
||||||
|
|
||||||
Object.keys(pairJson).forEach((pairKey) => {
|
|
||||||
pair[pairKey] = castCase(pairKey, pairJson[pairKey]);
|
|
||||||
});
|
|
||||||
|
|
||||||
pair.pairCount = pairCount;
|
|
||||||
|
|
||||||
pairs.push(EmberObject.create(pair));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
input.pairs = pairs;
|
pair.pairCount = pairCount;
|
||||||
} else {
|
|
||||||
input[inputKey] = castCase(inputKey, inputJson[inputKey]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
inputs.push(EmberObject.create(input));
|
pairs.push(EmberObject.create(pair));
|
||||||
|
});
|
||||||
|
|
||||||
|
input.pairs = pairs;
|
||||||
|
} else {
|
||||||
|
input[inputKey] = castCase(inputKey, inputJson[inputKey]);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return A(inputs);
|
inputs.push(EmberObject.create(input));
|
||||||
} else {
|
});
|
||||||
return value;
|
|
||||||
}
|
return A(inputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildObject(json, type) {
|
function buildProperty(json, property, type, objectIndex) {
|
||||||
|
let value = json[property];
|
||||||
|
if (
|
||||||
|
property === "index" &&
|
||||||
|
(value === null || value === undefined) &&
|
||||||
|
(objectIndex !== null || objectIndex !== undefined)
|
||||||
|
) {
|
||||||
|
return objectIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!mapped(property, type) ||
|
||||||
|
!present(value) ||
|
||||||
|
!value.constructor === Array
|
||||||
|
) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildMappedProperty(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildObject(json, type, objectIndex) {
|
||||||
let props = {
|
let props = {
|
||||||
isNew: false,
|
isNew: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.keys(json).forEach((prop) => {
|
Object.keys(json).forEach((prop) => {
|
||||||
props[prop] = buildProperty(json, prop, type);
|
props[prop] = buildProperty(json, prop, type, objectIndex);
|
||||||
});
|
});
|
||||||
|
|
||||||
return EmberObject.create(props);
|
return EmberObject.create(props);
|
||||||
|
@ -80,8 +95,8 @@ function buildObjectArray(json, type) {
|
||||||
let array = A();
|
let array = A();
|
||||||
|
|
||||||
if (present(json)) {
|
if (present(json)) {
|
||||||
json.forEach((objJson) => {
|
json.forEach((objJson, objectIndex) => {
|
||||||
let object = buildObject(objJson, type);
|
let object = buildObject(objJson, type, objectIndex);
|
||||||
|
|
||||||
if (hasAdvancedProperties(object, type)) {
|
if (hasAdvancedProperties(object, type)) {
|
||||||
object.set("showAdvanced", true);
|
object.set("showAdvanced", true);
|
||||||
|
@ -94,9 +109,9 @@ function buildObjectArray(json, type) {
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildBasicProperties(json, type, props) {
|
function buildBasicProperties(json, type, props, objectIndex = null) {
|
||||||
listProperties(type).forEach((p) => {
|
listProperties(type).forEach((p) => {
|
||||||
props[p] = buildProperty(json, p, type);
|
props[p] = buildProperty(json, p, type, objectIndex);
|
||||||
|
|
||||||
if (hasAdvancedProperties(json, type)) {
|
if (hasAdvancedProperties(json, type)) {
|
||||||
props.showAdvanced = true;
|
props.showAdvanced = true;
|
||||||
|
@ -142,12 +157,17 @@ function buildProperties(json) {
|
||||||
props = buildBasicProperties(json, "wizard", props);
|
props = buildBasicProperties(json, "wizard", props);
|
||||||
|
|
||||||
if (present(json.steps)) {
|
if (present(json.steps)) {
|
||||||
json.steps.forEach((stepJson) => {
|
json.steps.forEach((stepJson, objectIndex) => {
|
||||||
let stepProps = {
|
let stepProps = {
|
||||||
isNew: false,
|
isNew: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
stepProps = buildBasicProperties(stepJson, "step", stepProps);
|
stepProps = buildBasicProperties(
|
||||||
|
stepJson,
|
||||||
|
"step",
|
||||||
|
stepProps,
|
||||||
|
objectIndex
|
||||||
|
);
|
||||||
stepProps.fields = buildObjectArray(stepJson.fields, "field");
|
stepProps.fields = buildObjectArray(stepJson.fields, "field");
|
||||||
|
|
||||||
props.steps.pushObject(EmberObject.create(stepProps));
|
props.steps.pushObject(EmberObject.create(stepProps));
|
||||||
|
|
|
@ -37,6 +37,7 @@ const wizard = {
|
||||||
const step = {
|
const step = {
|
||||||
basic: {
|
basic: {
|
||||||
id: null,
|
id: null,
|
||||||
|
index: null,
|
||||||
title: null,
|
title: null,
|
||||||
key: null,
|
key: null,
|
||||||
banner: null,
|
banner: null,
|
||||||
|
@ -44,9 +45,11 @@ const step = {
|
||||||
required_data: null,
|
required_data: null,
|
||||||
required_data_message: null,
|
required_data_message: null,
|
||||||
permitted_params: null,
|
permitted_params: null,
|
||||||
|
condition: null,
|
||||||
|
force_final: false,
|
||||||
},
|
},
|
||||||
mapped: ["required_data", "permitted_params"],
|
mapped: ["required_data", "permitted_params", "condition", "index"],
|
||||||
advanced: ["required_data", "permitted_params"],
|
advanced: ["required_data", "permitted_params", "condition", "index"],
|
||||||
required: ["id"],
|
required: ["id"],
|
||||||
dependent: {},
|
dependent: {},
|
||||||
objectArrays: {
|
objectArrays: {
|
||||||
|
@ -60,16 +63,18 @@ const step = {
|
||||||
const field = {
|
const field = {
|
||||||
basic: {
|
basic: {
|
||||||
id: null,
|
id: null,
|
||||||
|
index: null,
|
||||||
label: null,
|
label: null,
|
||||||
image: null,
|
image: null,
|
||||||
description: null,
|
description: null,
|
||||||
required: null,
|
required: null,
|
||||||
key: null,
|
key: null,
|
||||||
type: null,
|
type: null,
|
||||||
|
condition: null,
|
||||||
},
|
},
|
||||||
types: {},
|
types: {},
|
||||||
mapped: ["prefill", "content"],
|
mapped: ["prefill", "content", "condition", "index"],
|
||||||
advanced: ["property", "key"],
|
advanced: ["property", "key", "condition", "index"],
|
||||||
required: ["id", "type"],
|
required: ["id", "type"],
|
||||||
dependent: {},
|
dependent: {},
|
||||||
objectArrays: {},
|
objectArrays: {},
|
||||||
|
|
|
@ -131,11 +131,15 @@ const CustomWizard = EmberObject.extend({
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
buildMappedJson(inputs) {
|
buildMappedJson(value) {
|
||||||
if (!inputs || !inputs.length) {
|
if (typeof value === "string" || Number.isInteger(value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (!value || !value.length) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let inputs = value;
|
||||||
let result = [];
|
let result = [];
|
||||||
|
|
||||||
inputs.forEach((inpt) => {
|
inputs.forEach((inpt) => {
|
||||||
|
|
|
@ -188,6 +188,30 @@
|
||||||
{{#if field.showAdvanced}}
|
{{#if field.showAdvanced}}
|
||||||
<div class="advanced-settings">
|
<div class="advanced-settings">
|
||||||
|
|
||||||
|
<div class="setting full field-mapper-setting">
|
||||||
|
<div class="setting-label">
|
||||||
|
<label>{{i18n "admin.wizard.condition"}}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-value">
|
||||||
|
{{wizard-mapper
|
||||||
|
inputs=field.condition
|
||||||
|
options=fieldConditionOptions}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting full field-mapper-setting">
|
||||||
|
<div class="setting-label">
|
||||||
|
<label>{{i18n "admin.wizard.index"}}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-value">
|
||||||
|
{{wizard-mapper
|
||||||
|
inputs=field.index
|
||||||
|
options=fieldIndexOptions}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{{#if isCategory}}
|
{{#if isCategory}}
|
||||||
<div class="setting">
|
<div class="setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
|
|
|
@ -38,6 +38,27 @@
|
||||||
{{#if step.showAdvanced}}
|
{{#if step.showAdvanced}}
|
||||||
<div class="advanced-settings">
|
<div class="advanced-settings">
|
||||||
|
|
||||||
|
<div class="setting full field-mapper-setting">
|
||||||
|
<div class="setting-label">
|
||||||
|
<label>{{i18n "admin.wizard.condition"}}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-value">
|
||||||
|
{{wizard-mapper
|
||||||
|
inputs=step.condition
|
||||||
|
options=stepConditionOptions}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting full">
|
||||||
|
<div class="setting-label"></div>
|
||||||
|
<div class="setting-value force-final">
|
||||||
|
<h4>{{i18n "admin.wizard.step.force_final.label"}}</h4>
|
||||||
|
{{input type="checkbox" checked=step.force_final}}
|
||||||
|
<span>{{i18n "admin.wizard.step.force_final.description"}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="setting full field-mapper-setting">
|
<div class="setting full field-mapper-setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<label>{{i18n "admin.wizard.step.required_data.label"}}</label>
|
<label>{{i18n "admin.wizard.step.required_data.label"}}</label>
|
||||||
|
@ -92,7 +113,6 @@
|
||||||
placeholderKey="admin.wizard.translation_placeholder"}}
|
placeholderKey="admin.wizard.translation_placeholder"}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
@ -105,6 +125,7 @@
|
||||||
{{#each step.fields as |field|}}
|
{{#each step.fields as |field|}}
|
||||||
{{wizard-custom-field
|
{{wizard-custom-field
|
||||||
field=field
|
field=field
|
||||||
|
step=step
|
||||||
currentFieldId=currentField.id
|
currentFieldId=currentField.id
|
||||||
fieldTypes=fieldTypes
|
fieldTypes=fieldTypes
|
||||||
removeField="removeField"
|
removeField="removeField"
|
||||||
|
|
|
@ -4,14 +4,15 @@ import getUrl from "discourse-common/lib/get-url";
|
||||||
export default StepController.extend({
|
export default StepController.extend({
|
||||||
actions: {
|
actions: {
|
||||||
goNext(response) {
|
goNext(response) {
|
||||||
const next = this.get("step.next");
|
let nextStepId = response["next_step_id"];
|
||||||
|
|
||||||
if (response.redirect_on_next) {
|
if (response.redirect_on_next) {
|
||||||
window.location.href = response.redirect_on_next;
|
window.location.href = response.redirect_on_next;
|
||||||
} else if (response.refresh_required) {
|
} else if (response.refresh_required) {
|
||||||
const id = this.get("wizard.id");
|
const wizardId = this.get("wizard.id");
|
||||||
window.location.href = getUrl(`/w/${id}/steps/${next}`);
|
window.location.href = getUrl(`/w/${wizardId}/steps/${nextStepId}`);
|
||||||
} else {
|
} else {
|
||||||
this.transitionToRoute("custom.step", next);
|
this.transitionToRoute("custom.step", nextStepId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,9 @@ export default {
|
||||||
const CustomWizard = requirejs(
|
const CustomWizard = requirejs(
|
||||||
"discourse/plugins/discourse-custom-wizard/wizard/models/custom"
|
"discourse/plugins/discourse-custom-wizard/wizard/models/custom"
|
||||||
).default;
|
).default;
|
||||||
|
const updateCachedWizard = requirejs(
|
||||||
|
"discourse/plugins/discourse-custom-wizard/wizard/models/custom"
|
||||||
|
).updateCachedWizard;
|
||||||
const StepModel = requirejs("wizard/models/step").default;
|
const StepModel = requirejs("wizard/models/step").default;
|
||||||
const StepComponent = requirejs("wizard/components/wizard-step").default;
|
const StepComponent = requirejs("wizard/components/wizard-step").default;
|
||||||
const ajax = requirejs("wizard/lib/ajax").ajax;
|
const ajax = requirejs("wizard/lib/ajax").ajax;
|
||||||
|
@ -18,6 +21,7 @@ export default {
|
||||||
"discourse/plugins/discourse-custom-wizard/wizard/lib/text-lite"
|
"discourse/plugins/discourse-custom-wizard/wizard/lib/text-lite"
|
||||||
).cook;
|
).cook;
|
||||||
const { schedule } = requirejs("@ember/runloop");
|
const { schedule } = requirejs("@ember/runloop");
|
||||||
|
const { alias, not } = requirejs("@ember/object/computed");
|
||||||
|
|
||||||
StepModel.reopen({
|
StepModel.reopen({
|
||||||
save() {
|
save() {
|
||||||
|
@ -155,12 +159,17 @@ export default {
|
||||||
this.sendAction("showMessage", message);
|
this.sendAction("showMessage", message);
|
||||||
}.observes("step.message"),
|
}.observes("step.message"),
|
||||||
|
|
||||||
|
showNextButton: not("step.final"),
|
||||||
|
showDoneButton: alias("step.final"),
|
||||||
|
|
||||||
advance() {
|
advance() {
|
||||||
this.set("saving", true);
|
this.set("saving", true);
|
||||||
this.get("step")
|
this.get("step")
|
||||||
.save()
|
.save()
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (this.get("finalStep")) {
|
updateCachedWizard(CustomWizard.build(response["wizard"]));
|
||||||
|
|
||||||
|
if (response["final"]) {
|
||||||
CustomWizard.finished(response);
|
CustomWizard.finished(response);
|
||||||
} else {
|
} else {
|
||||||
this.sendAction("goNext", response);
|
this.sendAction("goNext", response);
|
||||||
|
@ -178,7 +187,6 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
done() {
|
done() {
|
||||||
this.set("finalStep", true);
|
|
||||||
this.send("nextStep");
|
this.send("nextStep");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -31,63 +31,45 @@ CustomWizard.reopenClass({
|
||||||
}
|
}
|
||||||
window.location.href = getUrl(url);
|
window.location.href = getUrl(url);
|
||||||
},
|
},
|
||||||
});
|
|
||||||
|
|
||||||
export function findCustomWizard(wizardId, params = {}) {
|
build(wizardJson) {
|
||||||
let url = `/w/${wizardId}`;
|
if (!wizardJson) {
|
||||||
|
|
||||||
let paramKeys = Object.keys(params).filter((k) => {
|
|
||||||
if (k === "wizard_id") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return !!params[k];
|
|
||||||
});
|
|
||||||
|
|
||||||
if (paramKeys.length) {
|
|
||||||
url += "?";
|
|
||||||
paramKeys.forEach((k, i) => {
|
|
||||||
if (i > 0) {
|
|
||||||
url += "&";
|
|
||||||
}
|
|
||||||
url += `${k}=${params[k]}`;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return ajax({ url, cache: false, dataType: "json" }).then((result) => {
|
|
||||||
const wizard = result;
|
|
||||||
if (!wizard) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!wizard.completed) {
|
if (!wizardJson.completed && wizardJson.steps) {
|
||||||
wizard.steps = wizard.steps.map((step) => {
|
wizardJson.steps = wizardJson.steps
|
||||||
const stepObj = Step.create(step);
|
.map((step) => {
|
||||||
|
const stepObj = Step.create(step);
|
||||||
|
|
||||||
stepObj.fields.sort((a, b) => {
|
stepObj.fields.sort((a, b) => {
|
||||||
return parseFloat(a.number) - parseFloat(b.number);
|
return parseFloat(a.number) - parseFloat(b.number);
|
||||||
|
});
|
||||||
|
|
||||||
|
let tabindex = 1;
|
||||||
|
stepObj.fields.forEach((f) => {
|
||||||
|
f.tabindex = tabindex;
|
||||||
|
|
||||||
|
if (["date_time"].includes(f.type)) {
|
||||||
|
tabindex = tabindex + 2;
|
||||||
|
} else {
|
||||||
|
tabindex++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
stepObj.fields = stepObj.fields.map((f) => WizardField.create(f));
|
||||||
|
|
||||||
|
return stepObj;
|
||||||
|
})
|
||||||
|
.sort((a, b) => {
|
||||||
|
return parseFloat(a.index) - parseFloat(b.index);
|
||||||
});
|
});
|
||||||
|
|
||||||
let tabindex = 1;
|
|
||||||
stepObj.fields.forEach((f) => {
|
|
||||||
f.tabindex = tabindex;
|
|
||||||
|
|
||||||
if (["date_time"].includes(f.type)) {
|
|
||||||
tabindex = tabindex + 2;
|
|
||||||
} else {
|
|
||||||
tabindex++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
stepObj.fields = stepObj.fields.map((f) => WizardField.create(f));
|
|
||||||
|
|
||||||
return stepObj;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wizard.categories) {
|
if (wizardJson.categories) {
|
||||||
let subcatMap = {};
|
let subcatMap = {};
|
||||||
let categoriesById = {};
|
let categoriesById = {};
|
||||||
let categories = wizard.categories.map((c) => {
|
let categories = wizardJson.categories.map((c) => {
|
||||||
if (c.parent_category_id) {
|
if (c.parent_category_id) {
|
||||||
subcatMap[c.parent_category_id] =
|
subcatMap[c.parent_category_id] =
|
||||||
subcatMap[c.parent_category_id] || [];
|
subcatMap[c.parent_category_id] || [];
|
||||||
|
@ -116,12 +98,47 @@ export function findCustomWizard(wizardId, params = {}) {
|
||||||
Discourse.Site.currentProp("categoriesById", categoriesById);
|
Discourse.Site.currentProp("categoriesById", categoriesById);
|
||||||
Discourse.Site.currentProp(
|
Discourse.Site.currentProp(
|
||||||
"uncategorized_category_id",
|
"uncategorized_category_id",
|
||||||
wizard.uncategorized_category_id
|
wizardJson.uncategorized_category_id
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CustomWizard.create(wizard);
|
return CustomWizard.create(wizardJson);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export function findCustomWizard(wizardId, params = {}) {
|
||||||
|
let url = `/w/${wizardId}`;
|
||||||
|
|
||||||
|
let paramKeys = Object.keys(params).filter((k) => {
|
||||||
|
if (k === "wizard_id") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !!params[k];
|
||||||
|
});
|
||||||
|
|
||||||
|
if (paramKeys.length) {
|
||||||
|
url += "?";
|
||||||
|
paramKeys.forEach((k, i) => {
|
||||||
|
if (i > 0) {
|
||||||
|
url += "&";
|
||||||
|
}
|
||||||
|
url += `${k}=${params[k]}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ajax({ url, cache: false, dataType: "json" }).then((result) => {
|
||||||
|
return CustomWizard.build(result);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _wizard_store;
|
||||||
|
|
||||||
|
export function updateCachedWizard(wizard) {
|
||||||
|
_wizard_store = wizard;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCachedWizard() {
|
||||||
|
return _wizard_store;
|
||||||
|
}
|
||||||
|
|
||||||
export default CustomWizard;
|
export default CustomWizard;
|
||||||
|
|
|
@ -1,22 +1,19 @@
|
||||||
|
import { getCachedWizard } from "../models/custom";
|
||||||
|
|
||||||
export default Ember.Route.extend({
|
export default Ember.Route.extend({
|
||||||
beforeModel() {
|
beforeModel() {
|
||||||
const appModel = this.modelFor("custom");
|
const wizard = getCachedWizard();
|
||||||
if (
|
if (wizard && wizard.permitted && !wizard.completed && wizard.start) {
|
||||||
appModel &&
|
this.replaceWith("custom.step", wizard.start);
|
||||||
appModel.permitted &&
|
|
||||||
!appModel.completed &&
|
|
||||||
appModel.start
|
|
||||||
) {
|
|
||||||
this.replaceWith("custom.step", appModel.start);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
model() {
|
model() {
|
||||||
return this.modelFor("custom");
|
return getCachedWizard();
|
||||||
},
|
},
|
||||||
|
|
||||||
setupController(controller, model) {
|
setupController(controller, model) {
|
||||||
if (model) {
|
if (model && model.id) {
|
||||||
const completed = model.get("completed");
|
const completed = model.get("completed");
|
||||||
const permitted = model.get("permitted");
|
const permitted = model.get("permitted");
|
||||||
const wizardId = model.get("id");
|
const wizardId = model.get("id");
|
||||||
|
|
|
@ -1,28 +1,33 @@
|
||||||
import WizardI18n from "../lib/wizard-i18n";
|
import WizardI18n from "../lib/wizard-i18n";
|
||||||
|
import { getCachedWizard } from "../models/custom";
|
||||||
|
|
||||||
export default Ember.Route.extend({
|
export default Ember.Route.extend({
|
||||||
model(params) {
|
beforeModel() {
|
||||||
const appModel = this.modelFor("custom");
|
this.set("wizard", getCachedWizard());
|
||||||
const allSteps = appModel.steps;
|
},
|
||||||
if (allSteps) {
|
|
||||||
const step = allSteps.findBy("id", params.step_id);
|
|
||||||
return step ? step : allSteps[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return appModel;
|
model(params) {
|
||||||
|
const wizard = this.wizard;
|
||||||
|
|
||||||
|
if (wizard && wizard.steps) {
|
||||||
|
const step = wizard.steps.findBy("id", params.step_id);
|
||||||
|
return step ? step : wizard.steps[0];
|
||||||
|
} else {
|
||||||
|
return wizard;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
afterModel(model) {
|
afterModel(model) {
|
||||||
if (model.completed) {
|
if (model.completed) {
|
||||||
return this.transitionTo("index");
|
return this.transitionTo("index");
|
||||||
}
|
}
|
||||||
return model.set("wizardId", this.modelFor("custom").id);
|
return model.set("wizardId", this.wizard.id);
|
||||||
},
|
},
|
||||||
|
|
||||||
setupController(controller, model) {
|
setupController(controller, model) {
|
||||||
let props = {
|
let props = {
|
||||||
step: model,
|
step: model,
|
||||||
wizard: this.modelFor("custom"),
|
wizard: this.wizard,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!model.permitted) {
|
if (!model.permitted) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* eslint no-undef: 0*/
|
/* eslint no-undef: 0*/
|
||||||
|
|
||||||
import { findCustomWizard } from "../models/custom";
|
import { findCustomWizard, updateCachedWizard } from "../models/custom";
|
||||||
import { ajax } from "wizard/lib/ajax";
|
import { ajax } from "wizard/lib/ajax";
|
||||||
|
|
||||||
export default Ember.Route.extend({
|
export default Ember.Route.extend({
|
||||||
|
@ -12,7 +12,9 @@ export default Ember.Route.extend({
|
||||||
return findCustomWizard(params.wizard_id, this.get("queryParams"));
|
return findCustomWizard(params.wizard_id, this.get("queryParams"));
|
||||||
},
|
},
|
||||||
|
|
||||||
afterModel() {
|
afterModel(model) {
|
||||||
|
updateCachedWizard(model);
|
||||||
|
|
||||||
return ajax({
|
return ajax({
|
||||||
url: `/site/settings`,
|
url: `/site/settings`,
|
||||||
type: "GET",
|
type: "GET",
|
||||||
|
@ -25,11 +27,11 @@ export default Ember.Route.extend({
|
||||||
const background = model ? model.get("background") : "AliceBlue";
|
const background = model ? model.get("background") : "AliceBlue";
|
||||||
Ember.run.scheduleOnce("afterRender", this, function () {
|
Ember.run.scheduleOnce("afterRender", this, function () {
|
||||||
$("body.custom-wizard").css("background", background);
|
$("body.custom-wizard").css("background", background);
|
||||||
if (model) {
|
|
||||||
$("#custom-wizard-main").addClass(model.get("id").dasherize());
|
if (model && model.id) {
|
||||||
|
$("#custom-wizard-main").addClass(model.id.dasherize());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
controller.setProperties({
|
controller.setProperties({
|
||||||
customWizard: true,
|
customWizard: true,
|
||||||
logoUrl: Wizard.SiteSettings.logo_small,
|
logoUrl: Wizard.SiteSettings.logo_small,
|
||||||
|
|
|
@ -303,6 +303,16 @@
|
||||||
max-width: 250px !important;
|
max-width: 250px !important;
|
||||||
min-width: 250px !important;
|
min-width: 250px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.force-final {
|
||||||
|
padding: 1em;
|
||||||
|
background-color: var(--primary-very-low);
|
||||||
|
|
||||||
|
label,
|
||||||
|
span {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.full,
|
&.full,
|
||||||
|
|
|
@ -50,6 +50,11 @@ textarea {
|
||||||
border-color: var(--danger);
|
border-color: var(--danger);
|
||||||
box-shadow: shadow("focus-danger");
|
box-shadow: shadow("focus-danger");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&[type="checkbox"] {
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.spinner {
|
.spinner {
|
||||||
|
|
|
@ -56,6 +56,8 @@ en:
|
||||||
undo: "Undo"
|
undo: "Undo"
|
||||||
clear: "Clear"
|
clear: "Clear"
|
||||||
select_type: "Select a type"
|
select_type: "Select a type"
|
||||||
|
condition: "Condition"
|
||||||
|
index: "Index"
|
||||||
|
|
||||||
message:
|
message:
|
||||||
wizard:
|
wizard:
|
||||||
|
@ -153,6 +155,9 @@ en:
|
||||||
not_permitted_message: "Message shown when required data not present"
|
not_permitted_message: "Message shown when required data not present"
|
||||||
permitted_params:
|
permitted_params:
|
||||||
label: "Params"
|
label: "Params"
|
||||||
|
force_final:
|
||||||
|
label: "Conditional Final Step"
|
||||||
|
description: "Display this step as the final step if conditions on later steps have not passed when the user reaches this step."
|
||||||
|
|
||||||
field:
|
field:
|
||||||
header: "Fields"
|
header: "Fields"
|
||||||
|
|
|
@ -41,7 +41,7 @@ class CustomWizard::AdminManagerController < CustomWizard::AdminController
|
||||||
end
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
template_json = JSON.parse file
|
template_json = JSON.parse(file)
|
||||||
rescue JSON::ParserError
|
rescue JSON::ParserError
|
||||||
return render_error(I18n.t('wizard.import.error.invalid_json'))
|
return render_error(I18n.t('wizard.import.error.invalid_json'))
|
||||||
end
|
end
|
||||||
|
|
|
@ -37,7 +37,7 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
|
||||||
wizard_id = template.save(create: params[:create])
|
wizard_id = template.save(create: params[:create])
|
||||||
|
|
||||||
if template.errors.any?
|
if template.errors.any?
|
||||||
render json: failed_json.merge(errors: result.errors.full_messages)
|
render json: failed_json.merge(errors: template.errors.full_messages)
|
||||||
else
|
else
|
||||||
render json: success_json.merge(wizard_id: wizard_id)
|
render json: success_json.merge(wizard_id: wizard_id)
|
||||||
end
|
end
|
||||||
|
@ -83,15 +83,19 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
|
||||||
permitted: mapped_params,
|
permitted: mapped_params,
|
||||||
steps: [
|
steps: [
|
||||||
:id,
|
:id,
|
||||||
|
:index,
|
||||||
:title,
|
:title,
|
||||||
:key,
|
:key,
|
||||||
:banner,
|
:banner,
|
||||||
:raw_description,
|
:raw_description,
|
||||||
:required_data_message,
|
:required_data_message,
|
||||||
|
:force_final,
|
||||||
required_data: mapped_params,
|
required_data: mapped_params,
|
||||||
permitted_params: mapped_params,
|
permitted_params: mapped_params,
|
||||||
|
condition: mapped_params,
|
||||||
fields: [
|
fields: [
|
||||||
:id,
|
:id,
|
||||||
|
:index,
|
||||||
:label,
|
:label,
|
||||||
:image,
|
:image,
|
||||||
:description,
|
:description,
|
||||||
|
@ -107,6 +111,8 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
|
||||||
:property,
|
:property,
|
||||||
prefill: mapped_params,
|
prefill: mapped_params,
|
||||||
content: mapped_params,
|
content: mapped_params,
|
||||||
|
condition: mapped_params,
|
||||||
|
index: mapped_params,
|
||||||
validations: {},
|
validations: {},
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|
|
@ -4,31 +4,67 @@ class CustomWizard::StepsController < ::ApplicationController
|
||||||
before_action :ensure_can_update
|
before_action :ensure_can_update
|
||||||
|
|
||||||
def update
|
def update
|
||||||
params.require(:step_id)
|
|
||||||
params.require(:wizard_id)
|
|
||||||
|
|
||||||
wizard = @builder.build
|
|
||||||
step = wizard.steps.select { |s| s.id == update_params[:step_id] }.first
|
|
||||||
|
|
||||||
raise Discourse::InvalidParameters.new(:step_id) if !step
|
|
||||||
|
|
||||||
update = update_params.to_h
|
update = update_params.to_h
|
||||||
|
|
||||||
update[:fields] = {}
|
update[:fields] = {}
|
||||||
if params[:fields]
|
if params[:fields]
|
||||||
field_ids = step.fields.map(&:id)
|
field_ids = @step_template['fields'].map { |f| f['id'] }
|
||||||
params[:fields].each do |k, v|
|
params[:fields].each do |k, v|
|
||||||
update[:fields][k] = v if field_ids.include? k
|
update[:fields][k] = v if field_ids.include? k
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
updater = wizard.create_updater(update[:step_id], update[:fields])
|
@builder.build
|
||||||
|
|
||||||
|
updater = @builder.wizard.create_updater(update[:step_id], update[:fields])
|
||||||
updater.update
|
updater.update
|
||||||
|
@result = updater.result
|
||||||
|
|
||||||
if updater.success?
|
if updater.success?
|
||||||
result = success_json
|
wizard_id = update_params[:wizard_id]
|
||||||
result.merge!(updater.result) if updater.result
|
builder = CustomWizard::Builder.new(wizard_id, current_user)
|
||||||
|
@wizard = builder.build
|
||||||
|
|
||||||
|
current_step = @wizard.find_step(update[:step_id])
|
||||||
|
current_submission = @wizard.current_submission
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
if current_step.conditional_final_step && !current_step.last_step
|
||||||
|
current_step.force_final = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if current_step.final?
|
||||||
|
builder.template.actions.each do |action_template|
|
||||||
|
if action_template['run_after'] === 'wizard_completion'
|
||||||
|
CustomWizard::Action.new(
|
||||||
|
action: action_template,
|
||||||
|
wizard: @wizard,
|
||||||
|
data: current_submission
|
||||||
|
).perform
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@wizard.save_submission(current_submission)
|
||||||
|
|
||||||
|
if redirect = get_redirect
|
||||||
|
updater.result[:redirect_on_complete] = redirect
|
||||||
|
end
|
||||||
|
|
||||||
|
@wizard.final_cleanup!
|
||||||
|
|
||||||
|
result[:final] = true
|
||||||
|
else
|
||||||
|
result[:final] = false
|
||||||
|
result[:next_step_id] = current_step.next.id
|
||||||
|
end
|
||||||
|
|
||||||
|
result.merge!(updater.result) if updater.result.present?
|
||||||
result[:refresh_required] = true if updater.refresh_required?
|
result[:refresh_required] = true if updater.refresh_required?
|
||||||
|
result[:wizard] = ::CustomWizard::WizardSerializer.new(
|
||||||
|
@wizard,
|
||||||
|
scope: Guardian.new(current_user),
|
||||||
|
root: false
|
||||||
|
).as_json
|
||||||
|
|
||||||
render json: result
|
render json: result
|
||||||
else
|
else
|
||||||
|
@ -43,21 +79,31 @@ class CustomWizard::StepsController < ::ApplicationController
|
||||||
private
|
private
|
||||||
|
|
||||||
def ensure_can_update
|
def ensure_can_update
|
||||||
@builder = CustomWizard::Builder.new(
|
@builder = CustomWizard::Builder.new(update_params[:wizard_id], current_user)
|
||||||
update_params[:wizard_id].underscore,
|
raise Discourse::InvalidParameters.new(:wizard_id) if @builder.template.nil?
|
||||||
current_user
|
raise Discourse::InvalidAccess.new if !@builder.wizard || !@builder.wizard.can_access?
|
||||||
)
|
|
||||||
|
|
||||||
if @builder.nil?
|
@step_template = @builder.template.steps.select do |s|
|
||||||
raise Discourse::InvalidParameters.new(:wizard_id)
|
s['id'] == update_params[:step_id]
|
||||||
end
|
end.first
|
||||||
|
raise Discourse::InvalidParameters.new(:step_id) if !@step_template
|
||||||
if !@builder.wizard || !@builder.wizard.can_access?
|
raise Discourse::InvalidAccess.new if !@builder.check_condition(@step_template)
|
||||||
raise Discourse::InvalidAccess.new
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_params
|
def update_params
|
||||||
params.permit(:wizard_id, :step_id)
|
@update_params || begin
|
||||||
|
params.require(:step_id)
|
||||||
|
params.require(:wizard_id)
|
||||||
|
params.permit(:wizard_id, :step_id).transform_values { |v| v.underscore }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_redirect
|
||||||
|
return @result[:redirect_on_next] if @result[:redirect_on_next].present?
|
||||||
|
|
||||||
|
current_submission = @wizard.current_submission
|
||||||
|
return nil unless current_submission.present?
|
||||||
|
## route_to set by actions, redirect_on_complete set by actions, redirect_to set at wizard entry
|
||||||
|
current_submission[:route_to] || current_submission[:redirect_on_complete] || current_submission[:redirect_to]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -65,10 +65,7 @@ class CustomWizard::WizardController < ::ApplicationController
|
||||||
result.merge!(redirect_to: submission['redirect_to'])
|
result.merge!(redirect_to: submission['redirect_to'])
|
||||||
end
|
end
|
||||||
|
|
||||||
if user.custom_fields['redirect_to_wizard'] === wizard.id
|
wizard.final_cleanup!
|
||||||
user.custom_fields.delete('redirect_to_wizard')
|
|
||||||
user.save_custom_fields(true)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
render json: result
|
render json: result
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"result": {
|
"result": {
|
||||||
"line": 89.56
|
"line": 90.52
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
module CustomWizardFieldExtension
|
|
||||||
attr_reader :raw,
|
|
||||||
:label,
|
|
||||||
:description,
|
|
||||||
:image,
|
|
||||||
:key,
|
|
||||||
:validations,
|
|
||||||
:min_length,
|
|
||||||
:max_length,
|
|
||||||
:char_counter,
|
|
||||||
:file_types,
|
|
||||||
:format,
|
|
||||||
:limit,
|
|
||||||
:property,
|
|
||||||
:content,
|
|
||||||
:number
|
|
||||||
|
|
||||||
def initialize(attrs)
|
|
||||||
super
|
|
||||||
@raw = attrs || {}
|
|
||||||
@description = attrs[:description]
|
|
||||||
@image = attrs[:image]
|
|
||||||
@key = attrs[:key]
|
|
||||||
@validations = attrs[:validations]
|
|
||||||
@min_length = attrs[:min_length]
|
|
||||||
@max_length = attrs[:max_length]
|
|
||||||
@char_counter = attrs[:char_counter]
|
|
||||||
@file_types = attrs[:file_types]
|
|
||||||
@format = attrs[:format]
|
|
||||||
@limit = attrs[:limit]
|
|
||||||
@property = attrs[:property]
|
|
||||||
@content = attrs[:content]
|
|
||||||
@number = attrs[:number]
|
|
||||||
end
|
|
||||||
|
|
||||||
def label
|
|
||||||
@label ||= PrettyText.cook(@raw[:label])
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,4 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
module CustomWizardStepExtension
|
|
||||||
attr_accessor :title, :description, :key, :permitted, :permitted_message
|
|
||||||
end
|
|
|
@ -6,12 +6,12 @@ class CustomWizard::Action
|
||||||
:guardian,
|
:guardian,
|
||||||
:result
|
:result
|
||||||
|
|
||||||
def initialize(params)
|
def initialize(opts)
|
||||||
@wizard = params[:wizard]
|
@wizard = opts[:wizard]
|
||||||
@action = params[:action]
|
@action = opts[:action]
|
||||||
@user = params[:user]
|
@user = @wizard.user
|
||||||
@guardian = Guardian.new(@user)
|
@guardian = Guardian.new(@user)
|
||||||
@data = params[:data]
|
@data = opts[:data]
|
||||||
@log = []
|
@log = []
|
||||||
@result = CustomWizard::ActionResult.new
|
@result = CustomWizard::ActionResult.new
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class CustomWizard::Builder
|
class CustomWizard::Builder
|
||||||
attr_accessor :wizard, :updater, :submissions
|
attr_accessor :wizard, :updater, :template
|
||||||
|
|
||||||
def initialize(wizard_id, user = nil)
|
def initialize(wizard_id, user = nil)
|
||||||
template = CustomWizard::Template.find(wizard_id)
|
@template = CustomWizard::Template.create(wizard_id)
|
||||||
return nil if template.blank?
|
return nil if @template.nil?
|
||||||
|
@wizard = CustomWizard::Wizard.new(template.data, user)
|
||||||
@wizard = CustomWizard::Wizard.new(template, user)
|
|
||||||
@steps = template['steps'] || []
|
|
||||||
@actions = template['actions'] || []
|
|
||||||
@submissions = @wizard.submissions
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.sorted_handlers
|
def self.sorted_handlers
|
||||||
|
@ -28,7 +24,7 @@ class CustomWizard::Builder
|
||||||
def mapper
|
def mapper
|
||||||
CustomWizard::Mapper.new(
|
CustomWizard::Mapper.new(
|
||||||
user: @wizard.user,
|
user: @wizard.user,
|
||||||
data: @submissions.last
|
data: @wizard.current_submission
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -38,103 +34,38 @@ class CustomWizard::Builder
|
||||||
|
|
||||||
build_opts[:reset] = build_opts[:reset] || @wizard.restart_on_revisit
|
build_opts[:reset] = build_opts[:reset] || @wizard.restart_on_revisit
|
||||||
|
|
||||||
@steps.each do |step_template|
|
@template.steps.each do |step_template|
|
||||||
|
next if !check_condition(step_template)
|
||||||
|
|
||||||
@wizard.append_step(step_template['id']) do |step|
|
@wizard.append_step(step_template['id']) do |step|
|
||||||
step.permitted = true
|
step = check_if_permitted(step, step_template)
|
||||||
|
next if !step.permitted
|
||||||
|
|
||||||
if step_template['required_data']
|
save_permitted_params(step_template, params)
|
||||||
step = ensure_required_data(step, step_template)
|
step = add_step_attributes(step, step_template)
|
||||||
end
|
step = append_step_fields(step, step_template, build_opts)
|
||||||
|
|
||||||
if !step.permitted
|
|
||||||
if step_template['required_data_message']
|
|
||||||
step.permitted_message = step_template['required_data_message']
|
|
||||||
end
|
|
||||||
next
|
|
||||||
end
|
|
||||||
|
|
||||||
step.title = step_template['title'] if step_template['title']
|
|
||||||
step.banner = step_template['banner'] if step_template['banner']
|
|
||||||
step.key = step_template['key'] if step_template['key']
|
|
||||||
|
|
||||||
if step_template['description']
|
|
||||||
step.description = mapper.interpolate(
|
|
||||||
step_template['description'],
|
|
||||||
user: true,
|
|
||||||
value: true
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
if permitted_params = step_template['permitted_params']
|
|
||||||
save_permitted_params(permitted_params, params)
|
|
||||||
end
|
|
||||||
|
|
||||||
if step_template['fields'] && step_template['fields'].length
|
|
||||||
step_template['fields'].each_with_index do |field_template, index|
|
|
||||||
append_field(step, step_template, field_template, build_opts, index)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
step.on_update do |updater|
|
step.on_update do |updater|
|
||||||
@updater = updater
|
@updater = updater
|
||||||
user = @wizard.user
|
@submission = (@wizard.current_submission || {})
|
||||||
|
.merge(@updater.submission)
|
||||||
|
.with_indifferent_access
|
||||||
|
|
||||||
updater.validate
|
@updater.validate
|
||||||
|
next if @updater.errors.any?
|
||||||
|
|
||||||
next if updater.errors.any?
|
apply_step_handlers
|
||||||
|
next if @updater.errors.any?
|
||||||
|
|
||||||
CustomWizard::Builder.step_handlers.each do |handler|
|
run_step_actions
|
||||||
if handler[:wizard_id] == @wizard.id
|
|
||||||
handler[:block].call(self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
next if updater.errors.any?
|
if @updater.errors.empty?
|
||||||
|
if route_to = @submission['route_to']
|
||||||
submission = updater.submission
|
@submission.delete('route_to')
|
||||||
|
|
||||||
if current_submission = @wizard.current_submission
|
|
||||||
submission = current_submission.merge(submission)
|
|
||||||
end
|
|
||||||
|
|
||||||
final_step = updater.step.next.nil?
|
|
||||||
|
|
||||||
if @actions.present?
|
|
||||||
@actions.each do |action|
|
|
||||||
|
|
||||||
if (action['run_after'] === updater.step.id) ||
|
|
||||||
(final_step && (!action['run_after'] || (action['run_after'] === 'wizard_completion')))
|
|
||||||
|
|
||||||
CustomWizard::Action.new(
|
|
||||||
wizard: @wizard,
|
|
||||||
action: action,
|
|
||||||
user: user,
|
|
||||||
data: submission
|
|
||||||
).perform
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if updater.errors.empty?
|
|
||||||
if route_to = submission['route_to']
|
|
||||||
submission.delete('route_to')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if @wizard.save_submissions
|
@wizard.save_submission(@submission)
|
||||||
save_submissions(submission, final_step)
|
@updater.result[:redirect_on_next] = route_to if route_to
|
||||||
end
|
|
||||||
|
|
||||||
if final_step
|
|
||||||
if @wizard.id == @wizard.user.custom_fields['redirect_to_wizard']
|
|
||||||
@wizard.user.custom_fields.delete('redirect_to_wizard')
|
|
||||||
@wizard.user.save_custom_fields(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
redirect_url = route_to || submission['redirect_on_complete'] || submission["redirect_to"]
|
|
||||||
updater.result[:redirect_on_complete] = redirect_url
|
|
||||||
elsif route_to
|
|
||||||
updater.result[:redirect_on_next] = route_to
|
|
||||||
end
|
|
||||||
|
|
||||||
true
|
true
|
||||||
else
|
else
|
||||||
|
@ -144,25 +75,21 @@ class CustomWizard::Builder
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@wizard.update_step_order!
|
||||||
@wizard
|
@wizard
|
||||||
end
|
end
|
||||||
|
|
||||||
def append_field(step, step_template, field_template, build_opts, index)
|
def append_field(step, step_template, field_template, build_opts)
|
||||||
params = {
|
params = {
|
||||||
id: field_template['id'],
|
id: field_template['id'],
|
||||||
type: field_template['type'],
|
type: field_template['type'],
|
||||||
required: field_template['required'],
|
required: field_template['required']
|
||||||
number: index + 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
params[:label] = field_template['label'] if field_template['label']
|
%w(label description image key validations min_length max_length char_counter).each do |key|
|
||||||
params[:description] = field_template['description'] if field_template['description']
|
params[key.to_sym] = field_template[key] if field_template[key]
|
||||||
params[:image] = field_template['image'] if field_template['image']
|
end
|
||||||
params[:key] = field_template['key'] if field_template['key']
|
|
||||||
params[:validations] = field_template['validations'] if field_template['validations']
|
|
||||||
params[:min_length] = field_template['min_length'] if field_template['min_length']
|
|
||||||
params[:max_length] = field_template['max_length'] if field_template['max_length']
|
|
||||||
params[:char_counter] = field_template['char_counter'] if field_template['char_counter']
|
|
||||||
params[:value] = prefill_field(field_template, step_template)
|
params[:value] = prefill_field(field_template, step_template)
|
||||||
|
|
||||||
if !build_opts[:reset] && (submission = @wizard.current_submission)
|
if !build_opts[:reset] && (submission = @wizard.current_submission)
|
||||||
|
@ -209,7 +136,7 @@ class CustomWizard::Builder
|
||||||
content = CustomWizard::Mapper.new(
|
content = CustomWizard::Mapper.new(
|
||||||
inputs: content_inputs,
|
inputs: content_inputs,
|
||||||
user: @wizard.user,
|
user: @wizard.user,
|
||||||
data: @submissions.last,
|
data: @wizard.current_submission,
|
||||||
opts: {
|
opts: {
|
||||||
with_type: true
|
with_type: true
|
||||||
}
|
}
|
||||||
|
@ -240,6 +167,16 @@ class CustomWizard::Builder
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if field_template['index'].present?
|
||||||
|
index = CustomWizard::Mapper.new(
|
||||||
|
inputs: field_template['index'],
|
||||||
|
user: @wizard.user,
|
||||||
|
data: @wizard.current_submission
|
||||||
|
).perform
|
||||||
|
|
||||||
|
params[:index] = index.to_i unless index.nil?
|
||||||
|
end
|
||||||
|
|
||||||
field = step.add_field(params)
|
field = step.add_field(params)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -248,41 +185,91 @@ class CustomWizard::Builder
|
||||||
CustomWizard::Mapper.new(
|
CustomWizard::Mapper.new(
|
||||||
inputs: prefill,
|
inputs: prefill,
|
||||||
user: @wizard.user,
|
user: @wizard.user,
|
||||||
data: @submissions.last
|
data: @wizard.current_submission
|
||||||
).perform
|
).perform
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def check_condition(template)
|
||||||
|
if template['condition'].present?
|
||||||
|
CustomWizard::Mapper.new(
|
||||||
|
inputs: template['condition'],
|
||||||
|
user: @wizard.user,
|
||||||
|
data: @wizard.current_submission
|
||||||
|
).perform
|
||||||
|
else
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_if_permitted(step, step_template)
|
||||||
|
step.permitted = true
|
||||||
|
|
||||||
|
if step_template['required_data']
|
||||||
|
step = ensure_required_data(step, step_template)
|
||||||
|
end
|
||||||
|
|
||||||
|
if !step.permitted
|
||||||
|
if step_template['required_data_message']
|
||||||
|
step.permitted_message = step_template['required_data_message']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
step
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_step_attributes(step, step_template)
|
||||||
|
%w(index title banner key force_final).each do |attr|
|
||||||
|
step.send("#{attr}=", step_template[attr]) if step_template[attr]
|
||||||
|
end
|
||||||
|
|
||||||
|
if step_template['description']
|
||||||
|
step.description = mapper.interpolate(
|
||||||
|
step_template['description'],
|
||||||
|
user: true,
|
||||||
|
value: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
step
|
||||||
|
end
|
||||||
|
|
||||||
|
def append_step_fields(step, step_template, build_opts)
|
||||||
|
if step_template['fields'] && step_template['fields'].length
|
||||||
|
step_template['fields'].each do |field_template|
|
||||||
|
next if !check_condition(field_template)
|
||||||
|
append_field(step, step_template, field_template, build_opts)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
step.update_field_order!
|
||||||
|
step
|
||||||
|
end
|
||||||
|
|
||||||
def standardise_boolean(value)
|
def standardise_boolean(value)
|
||||||
ActiveRecord::Type::Boolean.new.cast(value)
|
ActiveRecord::Type::Boolean.new.cast(value)
|
||||||
end
|
end
|
||||||
|
|
||||||
def save_submissions(submission, final_step)
|
def save_permitted_params(step_template, params)
|
||||||
if final_step
|
return unless step_template['permitted_params'].present?
|
||||||
submission['submitted_at'] = Time.now.iso8601
|
|
||||||
end
|
|
||||||
|
|
||||||
if submission.present?
|
permitted_params = step_template['permitted_params']
|
||||||
@submissions.pop(1) if @wizard.unfinished?
|
|
||||||
@submissions.push(submission)
|
|
||||||
@wizard.set_submissions(@submissions)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def save_permitted_params(permitted_params, params)
|
|
||||||
permitted_data = {}
|
permitted_data = {}
|
||||||
|
submission_key = nil
|
||||||
|
params_key = nil
|
||||||
|
submission = @wizard.current_submission || {}
|
||||||
|
|
||||||
permitted_params.each do |pp|
|
permitted_params.each do |pp|
|
||||||
pair = pp['pairs'].first
|
pair = pp['pairs'].first
|
||||||
params_key = pair['key'].to_sym
|
params_key = pair['key'].to_sym
|
||||||
submission_key = pair['value'].to_sym
|
submission_key = pair['value'].to_sym
|
||||||
permitted_data[submission_key] = params[params_key] if params[params_key]
|
|
||||||
|
if submission_key && params_key
|
||||||
|
submission[submission_key] = params[params_key]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if permitted_data.present?
|
@wizard.save_submission(submission)
|
||||||
current_data = @submissions.last || {}
|
|
||||||
save_submissions(current_data.merge(permitted_data), false)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def ensure_required_data(step, step_template)
|
def ensure_required_data(step, step_template)
|
||||||
|
@ -291,13 +278,13 @@ class CustomWizard::Builder
|
||||||
pair['key'].present? && pair['value'].present?
|
pair['key'].present? && pair['value'].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
if pairs.any? && !@submissions.last
|
if pairs.any? && !@wizard.current_submission
|
||||||
step.permitted = false
|
step.permitted = false
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
pairs.each do |pair|
|
pairs.each do |pair|
|
||||||
pair['key'] = @submissions.last[pair['key']]
|
pair['key'] = @wizard.current_submission[pair['key']]
|
||||||
end
|
end
|
||||||
|
|
||||||
if !mapper.validate_pairs(pairs)
|
if !mapper.validate_pairs(pairs)
|
||||||
|
@ -308,4 +295,26 @@ class CustomWizard::Builder
|
||||||
|
|
||||||
step
|
step
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def apply_step_handlers
|
||||||
|
CustomWizard::Builder.step_handlers.each do |handler|
|
||||||
|
if handler[:wizard_id] == @wizard.id
|
||||||
|
handler[:block].call(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run_step_actions
|
||||||
|
if @template.actions.present?
|
||||||
|
@template.actions.each do |action_template|
|
||||||
|
if action_template['run_after'] === updater.step.id
|
||||||
|
CustomWizard::Action.new(
|
||||||
|
action: action_template,
|
||||||
|
wizard: @wizard,
|
||||||
|
data: @submission
|
||||||
|
).perform
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,55 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class CustomWizard::Field
|
class CustomWizard::Field
|
||||||
|
include ActiveModel::SerializerSupport
|
||||||
|
|
||||||
|
attr_reader :raw,
|
||||||
|
:id,
|
||||||
|
:type,
|
||||||
|
:required,
|
||||||
|
:value,
|
||||||
|
:label,
|
||||||
|
:description,
|
||||||
|
:image,
|
||||||
|
:key,
|
||||||
|
:validations,
|
||||||
|
:min_length,
|
||||||
|
:max_length,
|
||||||
|
:char_counter,
|
||||||
|
:file_types,
|
||||||
|
:format,
|
||||||
|
:limit,
|
||||||
|
:property,
|
||||||
|
:content
|
||||||
|
|
||||||
|
attr_accessor :index,
|
||||||
|
:step
|
||||||
|
|
||||||
|
def initialize(attrs)
|
||||||
|
@raw = attrs || {}
|
||||||
|
@id = attrs[:id]
|
||||||
|
@index = attrs[:index]
|
||||||
|
@type = attrs[:type]
|
||||||
|
@required = !!attrs[:required]
|
||||||
|
@value = attrs[:value]
|
||||||
|
@description = attrs[:description]
|
||||||
|
@image = attrs[:image]
|
||||||
|
@key = attrs[:key]
|
||||||
|
@validations = attrs[:validations]
|
||||||
|
@min_length = attrs[:min_length]
|
||||||
|
@max_length = attrs[:max_length]
|
||||||
|
@char_counter = attrs[:char_counter]
|
||||||
|
@file_types = attrs[:file_types]
|
||||||
|
@format = attrs[:format]
|
||||||
|
@limit = attrs[:limit]
|
||||||
|
@property = attrs[:property]
|
||||||
|
@content = attrs[:content]
|
||||||
|
end
|
||||||
|
|
||||||
|
def label
|
||||||
|
@label ||= PrettyText.cook(@raw[:label])
|
||||||
|
end
|
||||||
|
|
||||||
def self.types
|
def self.types
|
||||||
@types ||= {
|
@types ||= {
|
||||||
text: {
|
text: {
|
||||||
|
|
56
lib/custom_wizard/step.rb
Normale Datei
56
lib/custom_wizard/step.rb
Normale Datei
|
@ -0,0 +1,56 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class CustomWizard::Step
|
||||||
|
include ActiveModel::SerializerSupport
|
||||||
|
|
||||||
|
attr_reader :id,
|
||||||
|
:updater
|
||||||
|
|
||||||
|
attr_accessor :index,
|
||||||
|
:title,
|
||||||
|
:description,
|
||||||
|
:key,
|
||||||
|
:permitted,
|
||||||
|
:permitted_message,
|
||||||
|
:fields,
|
||||||
|
:next,
|
||||||
|
:previous,
|
||||||
|
:banner,
|
||||||
|
:disabled,
|
||||||
|
:description_vars,
|
||||||
|
:last_step,
|
||||||
|
:force_final,
|
||||||
|
:conditional_final_step,
|
||||||
|
:wizard
|
||||||
|
|
||||||
|
def initialize(id)
|
||||||
|
@id = id
|
||||||
|
@fields = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_field(attrs)
|
||||||
|
field = ::CustomWizard::Field.new(attrs)
|
||||||
|
field.index = (@fields.size == 1 ? 0 : @fields.size) if field.index.nil?
|
||||||
|
field.step = self
|
||||||
|
@fields << field
|
||||||
|
field
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_fields?
|
||||||
|
@fields.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_update(&block)
|
||||||
|
@updater = block
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_field_order!
|
||||||
|
@fields.sort_by!(&:index)
|
||||||
|
end
|
||||||
|
|
||||||
|
def final?
|
||||||
|
return true if force_final && conditional_final_step
|
||||||
|
return true if last_step
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,14 +2,15 @@
|
||||||
class CustomWizard::StepUpdater
|
class CustomWizard::StepUpdater
|
||||||
include ActiveModel::Model
|
include ActiveModel::Model
|
||||||
|
|
||||||
attr_accessor :refresh_required, :submission, :result, :step
|
attr_accessor :refresh_required, :result
|
||||||
|
attr_reader :step, :submission
|
||||||
|
|
||||||
def initialize(current_user, wizard, step, submission)
|
def initialize(current_user, wizard, step, submission)
|
||||||
@current_user = current_user
|
@current_user = current_user
|
||||||
@wizard = wizard
|
@wizard = wizard
|
||||||
@step = step
|
@step = step
|
||||||
@refresh_required = false
|
@refresh_required = false
|
||||||
@submission = submission.to_h.with_indifferent_access
|
@submission = submission.with_indifferent_access
|
||||||
@result = {}
|
@result = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,14 @@ class CustomWizard::Template
|
||||||
include HasErrors
|
include HasErrors
|
||||||
|
|
||||||
attr_reader :data,
|
attr_reader :data,
|
||||||
:opts
|
:opts,
|
||||||
|
:steps,
|
||||||
|
:actions
|
||||||
|
|
||||||
def initialize(data)
|
def initialize(data)
|
||||||
@data = data
|
@data = data
|
||||||
|
@steps = data['steps'] || []
|
||||||
|
@actions = data['actions'] || []
|
||||||
end
|
end
|
||||||
|
|
||||||
def save(opts = {})
|
def save(opts = {})
|
||||||
|
@ -31,6 +35,14 @@ class CustomWizard::Template
|
||||||
new(data).save(opts)
|
new(data).save(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.create(wizard_id)
|
||||||
|
if data = find(wizard_id)
|
||||||
|
new(data)
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def self.find(wizard_id)
|
def self.find(wizard_id)
|
||||||
PluginStore.get(CustomWizard::PLUGIN_NAME, wizard_id)
|
PluginStore.get(CustomWizard::PLUGIN_NAME, wizard_id)
|
||||||
end
|
end
|
||||||
|
@ -88,6 +100,12 @@ class CustomWizard::Template
|
||||||
if step[:raw_description]
|
if step[:raw_description]
|
||||||
step[:description] = PrettyText.cook(step[:raw_description])
|
step[:description] = PrettyText.cook(step[:raw_description])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
remove_non_mapped_index(step)
|
||||||
|
|
||||||
|
step[:fields].each do |field|
|
||||||
|
remove_non_mapped_index(field)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -118,4 +136,10 @@ class CustomWizard::Template
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def remove_non_mapped_index(object)
|
||||||
|
if !object[:index].is_a?(Array)
|
||||||
|
object.delete(:index)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -26,9 +26,10 @@ class CustomWizard::Wizard
|
||||||
:needs_groups,
|
:needs_groups,
|
||||||
:steps,
|
:steps,
|
||||||
:step_ids,
|
:step_ids,
|
||||||
|
:first_step,
|
||||||
|
:start,
|
||||||
:actions,
|
:actions,
|
||||||
:user,
|
:user
|
||||||
:first_step
|
|
||||||
|
|
||||||
def initialize(attrs = {}, user = nil)
|
def initialize(attrs = {}, user = nil)
|
||||||
@user = user
|
@user = user
|
||||||
|
@ -68,8 +69,8 @@ class CustomWizard::Wizard
|
||||||
val.nil? ? false : ActiveRecord::Type::Boolean.new.cast(val)
|
val.nil? ? false : ActiveRecord::Type::Boolean.new.cast(val)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_step(step_name)
|
def create_step(step_id)
|
||||||
::Wizard::Step.new(step_name)
|
::CustomWizard::Step.new(step_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def append_step(step)
|
def append_step(step)
|
||||||
|
@ -77,37 +78,58 @@ class CustomWizard::Wizard
|
||||||
|
|
||||||
yield step if block_given?
|
yield step if block_given?
|
||||||
|
|
||||||
last_step = steps.last
|
|
||||||
steps << step
|
steps << step
|
||||||
|
step.wizard = self
|
||||||
|
step.index = (steps.size == 1 ? 0 : steps.size) if step.index.nil?
|
||||||
|
end
|
||||||
|
|
||||||
if steps.size == 1
|
def update_step_order!
|
||||||
@first_step = step
|
steps.sort_by!(&:index)
|
||||||
step.index = 0
|
|
||||||
elsif last_step.present?
|
steps.each_with_index do |step, index|
|
||||||
last_step.next = step
|
if index === 0
|
||||||
step.previous = last_step
|
@first_step = step
|
||||||
step.index = last_step.index + 1
|
@start = step.id
|
||||||
|
else
|
||||||
|
last_step = steps[index - 1]
|
||||||
|
last_step.next = step
|
||||||
|
step.previous = last_step
|
||||||
|
end
|
||||||
|
|
||||||
|
step.index = index
|
||||||
|
|
||||||
|
if index === (steps.length - 1)
|
||||||
|
step.conditional_final_step = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if index === (step_ids.length - 1)
|
||||||
|
step.last_step = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if step.previous && step.previous.id === last_completed_step_id
|
||||||
|
@start = step.id
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def start
|
def last_completed_step_id
|
||||||
return nil if !user
|
if user && unfinished? && last_completed_step = ::UserHistory.where(
|
||||||
|
|
||||||
if unfinished? && last_completed_step = ::UserHistory.where(
|
|
||||||
acting_user_id: user.id,
|
acting_user_id: user.id,
|
||||||
action: ::UserHistory.actions[:custom_wizard_step],
|
action: ::UserHistory.actions[:custom_wizard_step],
|
||||||
context: id,
|
context: id,
|
||||||
subject: steps.map(&:id)
|
subject: step_ids
|
||||||
).order("created_at").last
|
).order("created_at").last
|
||||||
|
|
||||||
step_id = last_completed_step.subject
|
last_completed_step.subject
|
||||||
last_index = steps.index { |s| s.id == step_id }
|
|
||||||
steps[last_index + 1]
|
|
||||||
else
|
else
|
||||||
@first_step
|
nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def find_step(step_id)
|
||||||
|
steps.select { |step| step.id === step_id }.first
|
||||||
|
end
|
||||||
|
|
||||||
def create_updater(step_id, submission)
|
def create_updater(step_id, submission)
|
||||||
step = @steps.find { |s| s.id == step_id }
|
step = @steps.find { |s| s.id == step_id }
|
||||||
wizard = self
|
wizard = self
|
||||||
|
@ -200,12 +222,13 @@ class CustomWizard::Wizard
|
||||||
end
|
end
|
||||||
|
|
||||||
def submissions
|
def submissions
|
||||||
Array.wrap(PluginStore.get("#{id}_submissions", user.id))
|
return nil unless user.present?
|
||||||
|
@submissions ||= Array.wrap(PluginStore.get("#{id}_submissions", user.id))
|
||||||
end
|
end
|
||||||
|
|
||||||
def current_submission
|
def current_submission
|
||||||
if submissions.present? && !submissions.last.key?("submitted_at")
|
if submissions.present? && submissions.last.present? && !submissions.last.key?("submitted_at")
|
||||||
submissions.last
|
submissions.last.with_indifferent_access
|
||||||
else
|
else
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
@ -213,6 +236,27 @@ class CustomWizard::Wizard
|
||||||
|
|
||||||
def set_submissions(submissions)
|
def set_submissions(submissions)
|
||||||
PluginStore.set("#{id}_submissions", user.id, Array.wrap(submissions))
|
PluginStore.set("#{id}_submissions", user.id, Array.wrap(submissions))
|
||||||
|
@submissions = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def save_submission(submission)
|
||||||
|
return nil unless save_submissions
|
||||||
|
|
||||||
|
submissions.pop(1) if unfinished?
|
||||||
|
submissions.push(submission)
|
||||||
|
set_submissions(submissions)
|
||||||
|
end
|
||||||
|
|
||||||
|
def final_cleanup!
|
||||||
|
if id == user.custom_fields['redirect_to_wizard']
|
||||||
|
user.custom_fields.delete('redirect_to_wizard')
|
||||||
|
user.save_custom_fields(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
if submission = current_submission
|
||||||
|
submission['submitted_at'] = Time.now.iso8601
|
||||||
|
save_submission(submission)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.submissions(wizard_id, user)
|
def self.submissions(wizard_id, user)
|
||||||
|
@ -276,7 +320,7 @@ class CustomWizard::Wizard
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.set_submission_redirect(user, wizard_id, url)
|
def self.set_submission_redirect(user, wizard_id, url)
|
||||||
PluginStore.set("#{wizard_id.underscore}_submissions", user.id, [{ redirect_to: url }])
|
set_submissions(wizard_id, user, [{ redirect_to: url }])
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.set_wizard_redirect(wizard_id, user)
|
def self.set_wizard_redirect(wizard_id, user)
|
||||||
|
|
|
@ -66,6 +66,7 @@ after_initialize do
|
||||||
../lib/custom_wizard/mapper.rb
|
../lib/custom_wizard/mapper.rb
|
||||||
../lib/custom_wizard/log.rb
|
../lib/custom_wizard/log.rb
|
||||||
../lib/custom_wizard/step_updater.rb
|
../lib/custom_wizard/step_updater.rb
|
||||||
|
../lib/custom_wizard/step.rb
|
||||||
../lib/custom_wizard/template.rb
|
../lib/custom_wizard/template.rb
|
||||||
../lib/custom_wizard/wizard.rb
|
../lib/custom_wizard/wizard.rb
|
||||||
../lib/custom_wizard/api/api.rb
|
../lib/custom_wizard/api/api.rb
|
||||||
|
@ -89,8 +90,6 @@ after_initialize do
|
||||||
../extensions/extra_locales_controller.rb
|
../extensions/extra_locales_controller.rb
|
||||||
../extensions/invites_controller.rb
|
../extensions/invites_controller.rb
|
||||||
../extensions/users_controller.rb
|
../extensions/users_controller.rb
|
||||||
../extensions/wizard_field.rb
|
|
||||||
../extensions/wizard_step.rb
|
|
||||||
../extensions/custom_field/preloader.rb
|
../extensions/custom_field/preloader.rb
|
||||||
../extensions/custom_field/serializer.rb
|
../extensions/custom_field/serializer.rb
|
||||||
].each do |path|
|
].each do |path|
|
||||||
|
@ -172,8 +171,6 @@ after_initialize do
|
||||||
::ExtraLocalesController.prepend ExtraLocalesControllerCustomWizard
|
::ExtraLocalesController.prepend ExtraLocalesControllerCustomWizard
|
||||||
::InvitesController.prepend InvitesControllerCustomWizard
|
::InvitesController.prepend InvitesControllerCustomWizard
|
||||||
::UsersController.prepend CustomWizardUsersController
|
::UsersController.prepend CustomWizardUsersController
|
||||||
::Wizard::Field.prepend CustomWizardFieldExtension
|
|
||||||
::Wizard::Step.prepend CustomWizardStepExtension
|
|
||||||
|
|
||||||
full_path = "#{Rails.root}/plugins/discourse-custom-wizard/assets/stylesheets/wizard/wizard_custom.scss"
|
full_path = "#{Rails.root}/plugins/discourse-custom-wizard/assets/stylesheets/wizard/wizard_custom.scss"
|
||||||
if Stylesheet::Importer.respond_to?(:plugin_assets)
|
if Stylesheet::Importer.respond_to?(:plugin_assets)
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class CustomWizard::FieldSerializer < ::WizardFieldSerializer
|
class CustomWizard::FieldSerializer < ::ApplicationSerializer
|
||||||
|
|
||||||
attributes :image,
|
attributes :id,
|
||||||
|
:index,
|
||||||
|
:type,
|
||||||
|
:required,
|
||||||
|
:value,
|
||||||
|
:label,
|
||||||
|
:placeholder,
|
||||||
|
:description,
|
||||||
|
:image,
|
||||||
:file_types,
|
:file_types,
|
||||||
:format,
|
:format,
|
||||||
:limit,
|
:limit,
|
||||||
|
@ -10,19 +18,54 @@ class CustomWizard::FieldSerializer < ::WizardFieldSerializer
|
||||||
:content,
|
:content,
|
||||||
:validations,
|
:validations,
|
||||||
:max_length,
|
:max_length,
|
||||||
:char_counter,
|
:char_counter
|
||||||
:number
|
|
||||||
|
def id
|
||||||
|
object.id
|
||||||
|
end
|
||||||
|
|
||||||
|
def index
|
||||||
|
object.index
|
||||||
|
end
|
||||||
|
|
||||||
|
def type
|
||||||
|
object.type
|
||||||
|
end
|
||||||
|
|
||||||
|
def required
|
||||||
|
object.required
|
||||||
|
end
|
||||||
|
|
||||||
|
def value
|
||||||
|
object.value
|
||||||
|
end
|
||||||
|
|
||||||
|
def include_value?
|
||||||
|
object.value.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def i18n_key
|
||||||
|
@i18n_key ||= "wizard.step.#{object.step.id}.fields.#{object.id}".underscore
|
||||||
|
end
|
||||||
|
|
||||||
def label
|
def label
|
||||||
return object.label if object.label.present?
|
return object.label if object.label.present?
|
||||||
I18n.t("#{object.key || i18n_key}.label", default: '')
|
I18n.t("#{object.key || i18n_key}.label", default: '')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def include_label?
|
||||||
|
label.present?
|
||||||
|
end
|
||||||
|
|
||||||
def description
|
def description
|
||||||
return object.description if object.description.present?
|
return object.description if object.description.present?
|
||||||
I18n.t("#{object.key || i18n_key}.description", default: '', base_url: Discourse.base_url)
|
I18n.t("#{object.key || i18n_key}.description", default: '', base_url: Discourse.base_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def include_description?
|
||||||
|
description.present?
|
||||||
|
end
|
||||||
|
|
||||||
def image
|
def image
|
||||||
object.image
|
object.image
|
||||||
end
|
end
|
||||||
|
@ -35,6 +78,10 @@ class CustomWizard::FieldSerializer < ::WizardFieldSerializer
|
||||||
I18n.t("#{object.key || i18n_key}.placeholder", default: '')
|
I18n.t("#{object.key || i18n_key}.placeholder", default: '')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def include_placeholder?
|
||||||
|
placeholder.present?
|
||||||
|
end
|
||||||
|
|
||||||
def file_types
|
def file_types
|
||||||
object.file_types
|
object.file_types
|
||||||
end
|
end
|
||||||
|
@ -55,10 +102,6 @@ class CustomWizard::FieldSerializer < ::WizardFieldSerializer
|
||||||
object.content
|
object.content
|
||||||
end
|
end
|
||||||
|
|
||||||
def include_choices?
|
|
||||||
object.choices.present?
|
|
||||||
end
|
|
||||||
|
|
||||||
def validations
|
def validations
|
||||||
validations = {}
|
validations = {}
|
||||||
object.validations&.each do |type, props|
|
object.validations&.each do |type, props|
|
||||||
|
@ -77,8 +120,4 @@ class CustomWizard::FieldSerializer < ::WizardFieldSerializer
|
||||||
def char_counter
|
def char_counter
|
||||||
object.char_counter
|
object.char_counter
|
||||||
end
|
end
|
||||||
|
|
||||||
def number
|
|
||||||
object.number
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,7 +30,7 @@ class CustomWizard::WizardSerializer < CustomWizard::BasicWizardSerializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def start
|
def start
|
||||||
object.start.id
|
object.start
|
||||||
end
|
end
|
||||||
|
|
||||||
def include_start?
|
def include_start?
|
||||||
|
|
|
@ -1,20 +1,74 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class CustomWizard::StepSerializer < ::WizardStepSerializer
|
class CustomWizard::StepSerializer < ::ApplicationSerializer
|
||||||
|
|
||||||
|
attributes :id,
|
||||||
|
:index,
|
||||||
|
:next,
|
||||||
|
:previous,
|
||||||
|
:description,
|
||||||
|
:title,
|
||||||
|
:banner,
|
||||||
|
:permitted,
|
||||||
|
:permitted_message,
|
||||||
|
:final
|
||||||
|
|
||||||
attributes :permitted, :permitted_message
|
|
||||||
has_many :fields, serializer: ::CustomWizard::FieldSerializer, embed: :objects
|
has_many :fields, serializer: ::CustomWizard::FieldSerializer, embed: :objects
|
||||||
|
|
||||||
|
def id
|
||||||
|
object.id
|
||||||
|
end
|
||||||
|
|
||||||
|
def index
|
||||||
|
object.index
|
||||||
|
end
|
||||||
|
|
||||||
|
def next
|
||||||
|
object.next.id if object.next.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def include_next?
|
||||||
|
object.next.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def previous
|
||||||
|
object.previous.id if object.previous.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def include_previous?
|
||||||
|
object.previous.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def i18n_key
|
||||||
|
@i18n_key ||= "wizard.step.#{object.id}".underscore
|
||||||
|
end
|
||||||
|
|
||||||
def title
|
def title
|
||||||
return PrettyText.cook(object.title) if object.title
|
return PrettyText.cook(object.title) if object.title
|
||||||
PrettyText.cook(I18n.t("#{object.key || i18n_key}.title", default: ''))
|
PrettyText.cook(I18n.t("#{object.key || i18n_key}.title", default: ''))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def include_title?
|
||||||
|
title.present?
|
||||||
|
end
|
||||||
|
|
||||||
def description
|
def description
|
||||||
return object.description if object.description
|
return object.description if object.description
|
||||||
PrettyText.cook(I18n.t("#{object.key || i18n_key}.description", default: '', base_url: Discourse.base_url))
|
PrettyText.cook(I18n.t("#{object.key || i18n_key}.description", default: '', base_url: Discourse.base_url))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def include_description?
|
||||||
|
description.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def banner
|
||||||
|
object.banner
|
||||||
|
end
|
||||||
|
|
||||||
|
def include_banner?
|
||||||
|
object.banner.present?
|
||||||
|
end
|
||||||
|
|
||||||
def permitted
|
def permitted
|
||||||
object.permitted
|
object.permitted
|
||||||
end
|
end
|
||||||
|
@ -22,4 +76,8 @@ class CustomWizard::StepSerializer < ::WizardStepSerializer
|
||||||
def permitted_message
|
def permitted_message
|
||||||
object.permitted_message
|
object.permitted_message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def final
|
||||||
|
object.final?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,19 +6,25 @@ describe CustomWizard::Action do
|
||||||
fab!(:category) { Fabricate(:category, name: 'cat1', slug: 'cat-slug') }
|
fab!(:category) { Fabricate(:category, name: 'cat1', slug: 'cat-slug') }
|
||||||
fab!(:group) { Fabricate(:group) }
|
fab!(:group) { Fabricate(:group) }
|
||||||
|
|
||||||
|
let(:wizard_template) {
|
||||||
|
JSON.parse(
|
||||||
|
File.open(
|
||||||
|
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||||
|
).read
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
let(:open_composer) {
|
let(:open_composer) {
|
||||||
JSON.parse(File.open(
|
JSON.parse(
|
||||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/actions/open_composer.json"
|
File.open(
|
||||||
).read)
|
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/actions/open_composer.json"
|
||||||
|
).read
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
before do
|
before do
|
||||||
Group.refresh_automatic_group!(:trust_level_2)
|
Group.refresh_automatic_group!(:trust_level_2)
|
||||||
CustomWizard::Template.save(
|
CustomWizard::Template.save(wizard_template, skip_jobs: true)
|
||||||
JSON.parse(File.open(
|
|
||||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
|
||||||
).read),
|
|
||||||
skip_jobs: true)
|
|
||||||
@template = CustomWizard::Template.find('super_mega_fun_wizard')
|
@template = CustomWizard::Template.find('super_mega_fun_wizard')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -156,7 +162,6 @@ describe CustomWizard::Action do
|
||||||
action = CustomWizard::Action.new(
|
action = CustomWizard::Action.new(
|
||||||
wizard: wizard,
|
wizard: wizard,
|
||||||
action: open_composer,
|
action: open_composer,
|
||||||
user: user,
|
|
||||||
data: {}
|
data: {}
|
||||||
)
|
)
|
||||||
action.perform
|
action.perform
|
||||||
|
@ -179,8 +184,7 @@ describe CustomWizard::Action do
|
||||||
|
|
||||||
it 'creates a group' do
|
it 'creates a group' do
|
||||||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||||
step_id = wizard.steps[0].id
|
wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
|
||||||
updater = wizard.create_updater(step_id, step_1_field_1: "Text input").update
|
|
||||||
expect(Group.where(name: wizard.current_submission['action_9']).exists?).to eq(true)
|
expect(Group.where(name: wizard.current_submission['action_9']).exists?).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -210,6 +214,6 @@ describe CustomWizard::Action do
|
||||||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||||
updater = wizard.create_updater(wizard.steps.last.id, {})
|
updater = wizard.create_updater(wizard.steps.last.id, {})
|
||||||
updater.update
|
updater.update
|
||||||
expect(updater.result[:redirect_on_complete]).to eq("https://google.com")
|
expect(updater.result[:redirect_on_next]).to eq("https://google.com")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,21 +16,35 @@ describe CustomWizard::Builder do
|
||||||
fab!(:group) { Fabricate(:group) }
|
fab!(:group) { Fabricate(:group) }
|
||||||
|
|
||||||
let(:required_data_json) {
|
let(:required_data_json) {
|
||||||
JSON.parse(File.open(
|
JSON.parse(
|
||||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/required_data.json"
|
File.open(
|
||||||
).read)
|
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/required_data.json"
|
||||||
|
).read
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let(:permitted_json) {
|
let(:permitted_json) {
|
||||||
JSON.parse(File.open(
|
JSON.parse(
|
||||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
|
File.open(
|
||||||
).read)
|
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
|
||||||
|
).read
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let(:permitted_param_json) {
|
let(:permitted_param_json) {
|
||||||
JSON.parse(File.open(
|
JSON.parse(
|
||||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/permitted_params.json"
|
File.open(
|
||||||
).read)
|
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/permitted_params.json"
|
||||||
|
).read
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let(:user_condition_json) {
|
||||||
|
JSON.parse(
|
||||||
|
File.open(
|
||||||
|
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/condition/user_condition.json"
|
||||||
|
).read
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -263,6 +277,23 @@ describe CustomWizard::Builder do
|
||||||
expect(wizard.current_submission['saved_param']).to eq('param_value')
|
expect(wizard.current_submission['saved_param']).to eq('param_value')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "with condition" do
|
||||||
|
before do
|
||||||
|
@template[:steps][0][:condition] = user_condition_json['condition']
|
||||||
|
CustomWizard::Template.save(@template.as_json)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "adds step when condition is passed" do
|
||||||
|
wizard = CustomWizard::Builder.new(@template[:id], trusted_user).build
|
||||||
|
expect(wizard.steps.first.id).to eq(@template[:steps][0]['id'])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does not add step when condition is not passed" do
|
||||||
|
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||||
|
expect(wizard.steps.first.id).to eq(@template[:steps][1]['id'])
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'building field' do
|
context 'building field' do
|
||||||
|
@ -284,6 +315,23 @@ describe CustomWizard::Builder do
|
||||||
.fields.length
|
.fields.length
|
||||||
).to eq(4)
|
).to eq(4)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "with condition" do
|
||||||
|
before do
|
||||||
|
@template[:steps][0][:fields][0][:condition] = user_condition_json['condition']
|
||||||
|
CustomWizard::Template.save(@template.as_json)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "adds field when condition is passed" do
|
||||||
|
wizard = CustomWizard::Builder.new(@template[:id], trusted_user).build
|
||||||
|
expect(wizard.steps.first.fields.first.id).to eq(@template[:steps][0][:fields][0]['id'])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does not add field when condition is not passed" do
|
||||||
|
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||||
|
expect(wizard.steps.first.fields.first.id).to eq(@template[:steps][0][:fields][1]['id'])
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'on update' do
|
context 'on update' do
|
||||||
|
|
|
@ -2,6 +2,12 @@
|
||||||
require_relative '../../plugin_helper'
|
require_relative '../../plugin_helper'
|
||||||
|
|
||||||
describe CustomWizard::Field do
|
describe CustomWizard::Field do
|
||||||
|
let(:field_hash) do
|
||||||
|
JSON.parse(File.open(
|
||||||
|
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/field/field.json"
|
||||||
|
).read).with_indifferent_access
|
||||||
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
CustomWizard::Field.register(
|
CustomWizard::Field.register(
|
||||||
'location',
|
'location',
|
||||||
|
@ -13,6 +19,19 @@ describe CustomWizard::Field do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "initialize custom field attributes" do
|
||||||
|
field = CustomWizard::Field.new(field_hash)
|
||||||
|
expect(field.id).to eq("field_id")
|
||||||
|
expect(field.index).to eq(0)
|
||||||
|
expect(field.label).to eq("<p>Field Label</p>")
|
||||||
|
expect(field.image).to eq("field_image_url.png")
|
||||||
|
expect(field.description).to eq("Field description")
|
||||||
|
expect(field.required).to eq(true)
|
||||||
|
expect(field.key).to eq("field.locale.key")
|
||||||
|
expect(field.type).to eq("field_type")
|
||||||
|
expect(field.content).to eq([])
|
||||||
|
end
|
||||||
|
|
||||||
it "registers custom field types" do
|
it "registers custom field types" do
|
||||||
expect(CustomWizard::Field.types[:location].present?).to eq(true)
|
expect(CustomWizard::Field.types[:location].present?).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
36
spec/components/custom_wizard/step_spec.rb
Normale Datei
36
spec/components/custom_wizard/step_spec.rb
Normale Datei
|
@ -0,0 +1,36 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
require_relative '../../plugin_helper'
|
||||||
|
|
||||||
|
describe CustomWizard::Step do
|
||||||
|
let(:step_hash) do
|
||||||
|
JSON.parse(
|
||||||
|
File.open(
|
||||||
|
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/step.json"
|
||||||
|
).read
|
||||||
|
).with_indifferent_access
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:field_hash) do
|
||||||
|
JSON.parse(
|
||||||
|
File.open(
|
||||||
|
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/field/field.json"
|
||||||
|
).read
|
||||||
|
).with_indifferent_access
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
@step = CustomWizard::Step.new(step_hash[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "adds fields" do
|
||||||
|
@step.add_field(field_hash)
|
||||||
|
expect(@step.fields.size).to eq(1)
|
||||||
|
expect(@step.fields.first.index).to eq(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "adds fields with custom indexes" do
|
||||||
|
field_hash[:index] = 2
|
||||||
|
@step.add_field(field_hash)
|
||||||
|
expect(@step.fields.first.index).to eq(2)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,4 +1,5 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative '../../plugin_helper'
|
require_relative '../../plugin_helper'
|
||||||
|
|
||||||
describe CustomWizard::Wizard do
|
describe CustomWizard::Wizard do
|
||||||
|
@ -7,27 +8,33 @@ describe CustomWizard::Wizard do
|
||||||
fab!(:admin_user) { Fabricate(:user, admin: true) }
|
fab!(:admin_user) { Fabricate(:user, admin: true) }
|
||||||
|
|
||||||
let(:template_json) {
|
let(:template_json) {
|
||||||
JSON.parse(File.open(
|
JSON.parse(
|
||||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
File.open(
|
||||||
).read)
|
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||||
|
).read
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let(:permitted_json) {
|
let(:permitted_json) {
|
||||||
JSON.parse(File.open(
|
JSON.parse(
|
||||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
|
File.open(
|
||||||
).read)
|
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
|
||||||
|
).read
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
before do
|
before do
|
||||||
Group.refresh_automatic_group!(:trust_level_3)
|
Group.refresh_automatic_group!(:trust_level_3)
|
||||||
|
|
||||||
@permitted_template = template_json.dup
|
@permitted_template = template_json.dup
|
||||||
@permitted_template["permitted"] = permitted_json["permitted"]
|
@permitted_template["permitted"] = permitted_json["permitted"]
|
||||||
|
|
||||||
@wizard = CustomWizard::Wizard.new(template_json, user)
|
@wizard = CustomWizard::Wizard.new(template_json, user)
|
||||||
|
end
|
||||||
|
|
||||||
|
def append_steps
|
||||||
template_json['steps'].each do |step_template|
|
template_json['steps'].each do |step_template|
|
||||||
@wizard.append_step(step_template['id'])
|
@wizard.append_step(step_template['id'])
|
||||||
end
|
end
|
||||||
|
@wizard.update_step_order!
|
||||||
end
|
end
|
||||||
|
|
||||||
def progress_step(step_id, acting_user: user, wizard: @wizard)
|
def progress_step(step_id, acting_user: user, wizard: @wizard)
|
||||||
|
@ -37,16 +44,48 @@ describe CustomWizard::Wizard do
|
||||||
context: wizard.id,
|
context: wizard.id,
|
||||||
subject: step_id
|
subject: step_id
|
||||||
)
|
)
|
||||||
|
@wizard.update_step_order!
|
||||||
end
|
end
|
||||||
|
|
||||||
it "appends steps from a template" do
|
it "appends steps" do
|
||||||
|
append_steps
|
||||||
expect(@wizard.steps.length).to eq(3)
|
expect(@wizard.steps.length).to eq(3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "appends steps with indexes" do
|
||||||
|
append_steps
|
||||||
|
expect(@wizard.steps.first.index).to eq(0)
|
||||||
|
expect(@wizard.steps.last.index).to eq(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "appends steps with custom indexes" do
|
||||||
|
template_json['steps'][0]['index'] = 2
|
||||||
|
template_json['steps'][1]['index'] = 1
|
||||||
|
template_json['steps'][2]['index'] = 0
|
||||||
|
|
||||||
|
template_json['steps'].each do |step_template|
|
||||||
|
@wizard.append_step(step_template['id']) do |step|
|
||||||
|
step.index = step_template['index'] if step_template['index']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(@wizard.steps.first.index).to eq(2)
|
||||||
|
expect(@wizard.steps.last.index).to eq(0)
|
||||||
|
|
||||||
|
@wizard.update_step_order!
|
||||||
|
|
||||||
|
expect(@wizard.steps.first.id).to eq("step_3")
|
||||||
|
expect(@wizard.steps.last.id).to eq("step_1")
|
||||||
|
|
||||||
|
expect(@wizard.steps.first.next.id).to eq("step_2")
|
||||||
|
expect(@wizard.steps.last.next).to eq(nil)
|
||||||
|
end
|
||||||
|
|
||||||
it "determines the user's current step" do
|
it "determines the user's current step" do
|
||||||
expect(@wizard.start.id).to eq('step_1')
|
append_steps
|
||||||
|
expect(@wizard.start).to eq('step_1')
|
||||||
progress_step('step_1')
|
progress_step('step_1')
|
||||||
expect(@wizard.start.id).to eq('step_2')
|
expect(@wizard.start).to eq('step_2')
|
||||||
end
|
end
|
||||||
|
|
||||||
it "creates a step updater" do
|
it "creates a step updater" do
|
||||||
|
@ -57,6 +96,7 @@ describe CustomWizard::Wizard do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "determines whether a wizard is unfinished" do
|
it "determines whether a wizard is unfinished" do
|
||||||
|
append_steps
|
||||||
expect(@wizard.unfinished?).to eq(true)
|
expect(@wizard.unfinished?).to eq(true)
|
||||||
progress_step("step_1")
|
progress_step("step_1")
|
||||||
expect(@wizard.unfinished?).to eq(true)
|
expect(@wizard.unfinished?).to eq(true)
|
||||||
|
@ -67,6 +107,7 @@ describe CustomWizard::Wizard do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "determines whether a wizard has been completed by a user" do
|
it "determines whether a wizard has been completed by a user" do
|
||||||
|
append_steps
|
||||||
expect(@wizard.completed?).to eq(false)
|
expect(@wizard.completed?).to eq(false)
|
||||||
progress_step("step_1")
|
progress_step("step_1")
|
||||||
progress_step("step_2")
|
progress_step("step_2")
|
||||||
|
@ -75,6 +116,8 @@ describe CustomWizard::Wizard do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is not completed if steps submitted before after time" do
|
it "is not completed if steps submitted before after time" do
|
||||||
|
append_steps
|
||||||
|
|
||||||
progress_step("step_1")
|
progress_step("step_1")
|
||||||
progress_step("step_2")
|
progress_step("step_2")
|
||||||
progress_step("step_3")
|
progress_step("step_3")
|
||||||
|
@ -83,7 +126,6 @@ describe CustomWizard::Wizard do
|
||||||
template_json['after_time_scheduled'] = Time.now + 3.hours
|
template_json['after_time_scheduled'] = Time.now + 3.hours
|
||||||
|
|
||||||
wizard = CustomWizard::Wizard.new(template_json, user)
|
wizard = CustomWizard::Wizard.new(template_json, user)
|
||||||
|
|
||||||
expect(wizard.completed?).to eq(false)
|
expect(wizard.completed?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -125,6 +167,8 @@ describe CustomWizard::Wizard do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "lets a permitted user access a complete wizard with multiple submissions" do
|
it "lets a permitted user access a complete wizard with multiple submissions" do
|
||||||
|
append_steps
|
||||||
|
|
||||||
progress_step("step_1", acting_user: trusted_user)
|
progress_step("step_1", acting_user: trusted_user)
|
||||||
progress_step("step_2", acting_user: trusted_user)
|
progress_step("step_2", acting_user: trusted_user)
|
||||||
progress_step("step_3", acting_user: trusted_user)
|
progress_step("step_3", acting_user: trusted_user)
|
||||||
|
@ -135,6 +179,8 @@ describe CustomWizard::Wizard do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does not let an unpermitted user access a complete wizard without multiple submissions" do
|
it "does not let an unpermitted user access a complete wizard without multiple submissions" do
|
||||||
|
append_steps
|
||||||
|
|
||||||
progress_step("step_1", acting_user: trusted_user)
|
progress_step("step_1", acting_user: trusted_user)
|
||||||
progress_step("step_2", acting_user: trusted_user)
|
progress_step("step_2", acting_user: trusted_user)
|
||||||
progress_step("step_3", acting_user: trusted_user)
|
progress_step("step_3", acting_user: trusted_user)
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
require_relative '../plugin_helper'
|
|
||||||
|
|
||||||
describe CustomWizardFieldExtension do
|
|
||||||
let(:field_hash) do
|
|
||||||
JSON.parse(File.open(
|
|
||||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/field/field.json"
|
|
||||||
).read).with_indifferent_access
|
|
||||||
end
|
|
||||||
|
|
||||||
it "adds custom field attributes" do
|
|
||||||
field = Wizard::Field.new(field_hash)
|
|
||||||
expect(field.id).to eq("field_id")
|
|
||||||
expect(field.label).to eq("<p>Field Label</p>")
|
|
||||||
expect(field.image).to eq("field_image_url.png")
|
|
||||||
expect(field.description).to eq("Field description")
|
|
||||||
expect(field.required).to eq(true)
|
|
||||||
expect(field.key).to eq("field.locale.key")
|
|
||||||
expect(field.type).to eq("field_type")
|
|
||||||
expect(field.content).to eq([])
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,24 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
require_relative '../plugin_helper'
|
|
||||||
|
|
||||||
describe CustomWizardStepExtension do
|
|
||||||
let(:step_hash) do
|
|
||||||
JSON.parse(File.open(
|
|
||||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/step.json"
|
|
||||||
).read).with_indifferent_access
|
|
||||||
end
|
|
||||||
|
|
||||||
it "adds custom step attributes" do
|
|
||||||
step = Wizard::Step.new(step_hash[:id])
|
|
||||||
[
|
|
||||||
:title,
|
|
||||||
:description,
|
|
||||||
:key,
|
|
||||||
:permitted,
|
|
||||||
:permitted_message
|
|
||||||
].each do |attr|
|
|
||||||
step.send("#{attr.to_s}=", step_hash[attr])
|
|
||||||
expect(step.send(attr)).to eq(step_hash[attr])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
17
spec/fixtures/condition/user_condition.json
gevendort
Normale Datei
17
spec/fixtures/condition/user_condition.json
gevendort
Normale Datei
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"condition": [
|
||||||
|
{
|
||||||
|
"type": "validation",
|
||||||
|
"pairs": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"key": "username",
|
||||||
|
"key_type": "user_field",
|
||||||
|
"value": "angus",
|
||||||
|
"value_type": "text",
|
||||||
|
"connector": "equal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
17
spec/fixtures/condition/wizard_field_condition.json
gevendort
Normale Datei
17
spec/fixtures/condition/wizard_field_condition.json
gevendort
Normale Datei
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"condition": [
|
||||||
|
{
|
||||||
|
"type": "validation",
|
||||||
|
"pairs": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"key": "step_1_field_1",
|
||||||
|
"key_type": "wizard_field",
|
||||||
|
"value": "Condition will pass",
|
||||||
|
"value_type": "text",
|
||||||
|
"connector": "equal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
1
spec/fixtures/field/field.json
gevendort
1
spec/fixtures/field/field.json
gevendort
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"id": "field_id",
|
"id": "field_id",
|
||||||
|
"index": 0,
|
||||||
"label": "Field Label",
|
"label": "Field Label",
|
||||||
"image": "field_image_url.png",
|
"image": "field_image_url.png",
|
||||||
"description": "Field description",
|
"description": "Field description",
|
||||||
|
|
1
spec/fixtures/step/step.json
gevendort
1
spec/fixtures/step/step.json
gevendort
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"id": "step_1",
|
"id": "step_1",
|
||||||
|
"index": 0,
|
||||||
"title": "Text",
|
"title": "Text",
|
||||||
"description": "Step description",
|
"description": "Step description",
|
||||||
"image": "step_image_url.png",
|
"image": "step_image_url.png",
|
||||||
|
|
2
spec/fixtures/wizard.json
gevendort
2
spec/fixtures/wizard.json
gevendort
|
@ -535,7 +535,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "action_10",
|
"id": "action_10",
|
||||||
"run_after": "wizard_completion",
|
"run_after": "step_3",
|
||||||
"type": "route_to",
|
"type": "route_to",
|
||||||
"url": [
|
"url": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,12 +11,49 @@ describe CustomWizard::StepsController do
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
before do
|
fab!(:user2) {
|
||||||
CustomWizard::Template.save(
|
Fabricate(
|
||||||
JSON.parse(File.open(
|
:user,
|
||||||
|
username: 'bob',
|
||||||
|
email: "bob@email.com",
|
||||||
|
trust_level: TrustLevel[2]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let(:wizard_template) {
|
||||||
|
JSON.parse(
|
||||||
|
File.open(
|
||||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||||
).read),
|
).read
|
||||||
skip_jobs: true)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let(:wizard_field_condition_template) {
|
||||||
|
JSON.parse(
|
||||||
|
File.open(
|
||||||
|
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/condition/wizard_field_condition.json"
|
||||||
|
).read
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let(:user_condition_template) {
|
||||||
|
JSON.parse(
|
||||||
|
File.open(
|
||||||
|
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/condition/user_condition.json"
|
||||||
|
).read
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let(:permitted_json) {
|
||||||
|
JSON.parse(
|
||||||
|
File.open(
|
||||||
|
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
|
||||||
|
).read
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
before do
|
||||||
|
CustomWizard::Template.save(wizard_template, skip_jobs: true)
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -27,17 +64,189 @@ describe CustomWizard::StepsController do
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
|
expect(response.parsed_body['wizard']['start']).to eq("step_2")
|
||||||
|
|
||||||
wizard = CustomWizard::Builder.new("super_mega_fun_wizard", user).build
|
wizard_id = response.parsed_body['wizard']['id']
|
||||||
expect(wizard.current_submission['step_1_field_1']).to eq("Text input")
|
wizard = CustomWizard::Wizard.create(wizard_id, user)
|
||||||
expect(wizard.start.id).to eq("step_2")
|
expect(wizard.submissions.last['step_1_field_1']).to eq("Text input")
|
||||||
|
end
|
||||||
|
|
||||||
|
context "raises an error" do
|
||||||
|
it "when the wizard doesnt exist" do
|
||||||
|
put '/w/not-super-mega-fun-wizard/steps/step_1.json'
|
||||||
|
expect(response.status).to eq(400)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "when the user cant access the wizard" do
|
||||||
|
new_template = wizard_template.dup
|
||||||
|
new_template["permitted"] = permitted_json["permitted"]
|
||||||
|
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||||
|
|
||||||
|
put '/w/super-mega-fun-wizard/steps/step_1.json'
|
||||||
|
expect(response.status).to eq(403)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "when the step doesnt exist" do
|
||||||
|
put '/w/super-mega-fun-wizard/steps/step_10.json'
|
||||||
|
expect(response.status).to eq(400)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "when user cant see the step due to conditions" do
|
||||||
|
sign_in(user2)
|
||||||
|
|
||||||
|
new_wizard_template = wizard_template.dup
|
||||||
|
new_wizard_template['steps'][0]['condition'] = user_condition_template['condition']
|
||||||
|
CustomWizard::Template.save(new_wizard_template, skip_jobs: true)
|
||||||
|
|
||||||
|
put '/w/super-mega-fun-wizard/steps/step_1.json'
|
||||||
|
expect(response.status).to eq(403)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "works if the step has no fields" do
|
it "works if the step has no fields" do
|
||||||
put '/w/super-mega-fun-wizard/steps/step_1.json'
|
put '/w/super-mega-fun-wizard/steps/step_1.json'
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
|
expect(response.parsed_body['wizard']['start']).to eq("step_2")
|
||||||
|
end
|
||||||
|
|
||||||
wizard = CustomWizard::Builder.new("super_mega_fun_wizard", user).build
|
it "returns an updated wizard when condition passes" do
|
||||||
expect(wizard.start.id).to eq("step_2")
|
new_template = wizard_template.dup
|
||||||
|
new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
|
||||||
|
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||||
|
|
||||||
|
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||||
|
fields: {
|
||||||
|
step_1_field_1: "Condition will pass"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(response.parsed_body['wizard']['start']).to eq("step_2")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns an updated wizard when condition doesnt pass" do
|
||||||
|
new_template = wizard_template.dup
|
||||||
|
new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
|
||||||
|
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||||
|
|
||||||
|
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||||
|
fields: {
|
||||||
|
step_1_field_1: "Condition wont pass"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(response.parsed_body['wizard']['start']).to eq("step_3")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "runs completion actions if user has completed wizard" do
|
||||||
|
new_template = wizard_template.dup
|
||||||
|
|
||||||
|
## route_to action
|
||||||
|
new_template['actions'].last['run_after'] = 'wizard_completion'
|
||||||
|
new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
|
||||||
|
new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
|
||||||
|
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||||
|
|
||||||
|
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||||
|
fields: {
|
||||||
|
step_1_field_1: "Condition wont pass"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(response.parsed_body['redirect_on_complete']).to eq("https://google.com")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "saves results of completion actions if user has completed wizard" do
|
||||||
|
new_template = wizard_template.dup
|
||||||
|
|
||||||
|
## Create group action
|
||||||
|
new_template['actions'].first['run_after'] = 'wizard_completion'
|
||||||
|
new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
|
||||||
|
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||||
|
|
||||||
|
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||||
|
fields: {
|
||||||
|
step_1_field_1: "My cool group"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
|
||||||
|
put '/w/super-mega-fun-wizard/steps/step_3.json'
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
|
||||||
|
wizard_id = response.parsed_body['wizard']['id']
|
||||||
|
wizard = CustomWizard::Wizard.create(wizard_id, user)
|
||||||
|
group_name = wizard.submissions.last['action_9']
|
||||||
|
group = Group.find_by(name: group_name)
|
||||||
|
expect(group.full_name).to eq("My cool group")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns a final step without conditions" do
|
||||||
|
put '/w/super-mega-fun-wizard/steps/step_1.json'
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(response.parsed_body['final']).to eq(false)
|
||||||
|
|
||||||
|
put '/w/super-mega-fun-wizard/steps/step_2.json'
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(response.parsed_body['final']).to eq(false)
|
||||||
|
|
||||||
|
put '/w/super-mega-fun-wizard/steps/step_3.json'
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(response.parsed_body['final']).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the correct final step when the conditional final step and last step are the same" do
|
||||||
|
new_template = wizard_template.dup
|
||||||
|
new_template['steps'][0]['condition'] = user_condition_template['condition']
|
||||||
|
new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
|
||||||
|
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||||
|
|
||||||
|
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||||
|
fields: {
|
||||||
|
step_1_field_1: "Condition will pass"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(response.parsed_body['final']).to eq(false)
|
||||||
|
|
||||||
|
put '/w/super-mega-fun-wizard/steps/step_2.json'
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(response.parsed_body['final']).to eq(false)
|
||||||
|
|
||||||
|
put '/w/super-mega-fun-wizard/steps/step_3.json'
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(response.parsed_body['final']).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the correct final step when the conditional final step and last step are different" do
|
||||||
|
new_template = wizard_template.dup
|
||||||
|
new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
|
||||||
|
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||||
|
|
||||||
|
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||||
|
fields: {
|
||||||
|
step_1_field_1: "Condition will not pass"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(response.parsed_body['final']).to eq(false)
|
||||||
|
|
||||||
|
put '/w/super-mega-fun-wizard/steps/step_2.json'
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(response.parsed_body['final']).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the correct final step when the conditional final step is determined in the same action" do
|
||||||
|
new_template = wizard_template.dup
|
||||||
|
new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
|
||||||
|
new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
|
||||||
|
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||||
|
|
||||||
|
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||||
|
fields: {
|
||||||
|
step_1_field_1: "Condition will not pass"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(response.parsed_body['final']).to eq(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,9 +20,11 @@ describe CustomWizard::FieldSerializer do
|
||||||
each_serializer: CustomWizard::FieldSerializer,
|
each_serializer: CustomWizard::FieldSerializer,
|
||||||
scope: Guardian.new(user)
|
scope: Guardian.new(user)
|
||||||
).as_json
|
).as_json
|
||||||
expect(json_array.length).to eq(4)
|
|
||||||
|
expect(json_array.size).to eq(4)
|
||||||
expect(json_array[0][:label]).to eq("<p>Text</p>")
|
expect(json_array[0][:label]).to eq("<p>Text</p>")
|
||||||
expect(json_array[0][:description]).to eq("Text field description.")
|
expect(json_array[0][:description]).to eq("Text field description.")
|
||||||
|
expect(json_array[3][:index]).to eq(3)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should return optional field attributes" do
|
it "should return optional field attributes" do
|
||||||
|
@ -32,7 +34,6 @@ describe CustomWizard::FieldSerializer do
|
||||||
scope: Guardian.new(user)
|
scope: Guardian.new(user)
|
||||||
).as_json
|
).as_json
|
||||||
expect(json_array[0][:format]).to eq("YYYY-MM-DD")
|
expect(json_array[0][:format]).to eq("YYYY-MM-DD")
|
||||||
expect(json_array[3][:number]).to eq(4)
|
|
||||||
expect(json_array[6][:file_types]).to eq(".jpg,.jpeg,.png")
|
expect(json_array[6][:file_types]).to eq(".jpg,.jpeg,.png")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require_relative '../../plugin_helper'
|
|
||||||
|
|
||||||
describe CustomWizard::StepSerializer do
|
|
||||||
fab!(:user) { Fabricate(:user) }
|
|
||||||
|
|
||||||
let(:required_data_json) {
|
|
||||||
JSON.parse(File.open(
|
|
||||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/required_data.json"
|
|
||||||
).read)
|
|
||||||
}
|
|
||||||
|
|
||||||
before do
|
|
||||||
CustomWizard::Template.save(
|
|
||||||
JSON.parse(File.open(
|
|
||||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
|
||||||
).read),
|
|
||||||
skip_jobs: true)
|
|
||||||
@wizard = CustomWizard::Builder.new("super_mega_fun_wizard", user).build
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return basic step attributes' do
|
|
||||||
json_array = ActiveModel::ArraySerializer.new(
|
|
||||||
@wizard.steps,
|
|
||||||
each_serializer: CustomWizard::StepSerializer,
|
|
||||||
scope: Guardian.new(user)
|
|
||||||
).as_json
|
|
||||||
expect(json_array[0][:wizard_step][:title]).to eq("Text")
|
|
||||||
expect(json_array[0][:wizard_step][:description]).to eq("Text inputs!")
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return fields' do
|
|
||||||
json_array = ActiveModel::ArraySerializer.new(
|
|
||||||
@wizard.steps,
|
|
||||||
each_serializer: CustomWizard::StepSerializer,
|
|
||||||
scope: Guardian.new(user)
|
|
||||||
).as_json
|
|
||||||
expect(json_array[0][:wizard_step][:fields].length).to eq(4)
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with required data' do
|
|
||||||
before do
|
|
||||||
@template[:steps][0][:required_data] = required_data_json['required_data']
|
|
||||||
@template[:steps][0][:required_data_message] = required_data_json['required_data_message']
|
|
||||||
CustomWizard::Template.save(@template.as_json)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return permitted attributes' do
|
|
||||||
json_array = ActiveModel::ArraySerializer.new(
|
|
||||||
@wizard.steps,
|
|
||||||
each_serializer: CustomWizard::StepSerializer,
|
|
||||||
scope: Guardian.new(user)
|
|
||||||
).as_json
|
|
||||||
expect(json_array[0][:wizard_step][:permitted]).to eq(false)
|
|
||||||
expect(json_array[0][:wizard_step][:permitted_message]).to eq("Missing required data")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
67
spec/serializers/custom_wizard/wizard_step_serializer_spec.rb
Normale Datei
67
spec/serializers/custom_wizard/wizard_step_serializer_spec.rb
Normale Datei
|
@ -0,0 +1,67 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require_relative '../../plugin_helper'
|
||||||
|
|
||||||
|
describe CustomWizard::StepSerializer do
|
||||||
|
fab!(:user) { Fabricate(:user) }
|
||||||
|
|
||||||
|
let(:wizard_template) {
|
||||||
|
JSON.parse(
|
||||||
|
File.open(
|
||||||
|
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||||
|
).read
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let(:required_data_json) {
|
||||||
|
JSON.parse(
|
||||||
|
File.open(
|
||||||
|
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/required_data.json"
|
||||||
|
).read
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
before do
|
||||||
|
CustomWizard::Template.save(wizard_template, skip_jobs: true)
|
||||||
|
@wizard = CustomWizard::Builder.new("super_mega_fun_wizard", user).build
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should return basic step attributes' do
|
||||||
|
json_array = ActiveModel::ArraySerializer.new(
|
||||||
|
@wizard.steps,
|
||||||
|
each_serializer: described_class,
|
||||||
|
scope: Guardian.new(user)
|
||||||
|
).as_json
|
||||||
|
expect(json_array[0][:title]).to eq("<p>Text</p>")
|
||||||
|
expect(json_array[0][:description]).to eq("<p>Text inputs!</p>")
|
||||||
|
expect(json_array[1][:index]).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should return fields' do
|
||||||
|
json_array = ActiveModel::ArraySerializer.new(
|
||||||
|
@wizard.steps,
|
||||||
|
each_serializer: described_class,
|
||||||
|
scope: Guardian.new(user)
|
||||||
|
).as_json
|
||||||
|
expect(json_array[0][:fields].length).to eq(4)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with required data' do
|
||||||
|
before do
|
||||||
|
wizard_template['steps'][0]['required_data'] = required_data_json['required_data']
|
||||||
|
wizard_template['steps'][0]['required_data_message'] = required_data_json['required_data_message']
|
||||||
|
CustomWizard::Template.save(wizard_template)
|
||||||
|
@wizard = CustomWizard::Builder.new("super_mega_fun_wizard", user).build
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should return permitted attributes' do
|
||||||
|
json_array = ActiveModel::ArraySerializer.new(
|
||||||
|
@wizard.steps,
|
||||||
|
each_serializer: described_class,
|
||||||
|
scope: Guardian.new(user)
|
||||||
|
).as_json
|
||||||
|
expect(json_array[0][:permitted]).to eq(false)
|
||||||
|
expect(json_array[0][:permitted_message]).to eq("Missing required data")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Laden …
In neuem Issue referenzieren