{{/if}}
@@ -105,8 +125,9 @@
{{#each step.fields as |field|}}
{{wizard-custom-field
field=field
+ step=step
currentFieldId=currentField.id
fieldTypes=fieldTypes
removeField="removeField"
wizardFields=wizardFields}}
-{{/each}}
\ No newline at end of file
+{{/each}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-mapper-connector.hbs b/assets/javascripts/discourse/templates/components/wizard-mapper-connector.hbs
index f0689f9e..7b610e2b 100644
--- a/assets/javascripts/discourse/templates/components/wizard-mapper-connector.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-mapper-connector.hbs
@@ -9,4 +9,4 @@
{{connectorLabel}}
{{/if}}
-{{/if}}
\ No newline at end of file
+{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-mapper-pair.hbs b/assets/javascripts/discourse/templates/components/wizard-mapper-pair.hbs
index 272b0b58..ffb9eaf2 100644
--- a/assets/javascripts/discourse/templates/components/wizard-mapper-pair.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-mapper-pair.hbs
@@ -32,4 +32,4 @@
{{#if showRemove}}
{{d-icon "times"}}
-{{/if}}
\ No newline at end of file
+{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-mapper-selector-type.hbs b/assets/javascripts/discourse/templates/components/wizard-mapper-selector-type.hbs
index 2ef7f2a3..32c4c26e 100644
--- a/assets/javascripts/discourse/templates/components/wizard-mapper-selector-type.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-mapper-selector-type.hbs
@@ -1 +1 @@
-{{item.label}}
\ No newline at end of file
+{{item.label}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-message.hbs b/assets/javascripts/discourse/templates/components/wizard-message.hbs
index 4c48002a..380fc5b3 100644
--- a/assets/javascripts/discourse/templates/components/wizard-message.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-message.hbs
@@ -23,4 +23,4 @@
{{documentation}}
-{{/if}}
\ No newline at end of file
+{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-text-editor.hbs b/assets/javascripts/discourse/templates/components/wizard-text-editor.hbs
index a37f37a3..c657049d 100644
--- a/assets/javascripts/discourse/templates/components/wizard-text-editor.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-text-editor.hbs
@@ -38,4 +38,4 @@
{{/if}}
{{/if}}
-
\ No newline at end of file
+
diff --git a/assets/javascripts/wizard/controllers/custom-step.js.es6 b/assets/javascripts/wizard/controllers/custom-step.js.es6
index bf415bc9..b44c0fca 100644
--- a/assets/javascripts/wizard/controllers/custom-step.js.es6
+++ b/assets/javascripts/wizard/controllers/custom-step.js.es6
@@ -4,14 +4,15 @@ import getUrl from "discourse-common/lib/get-url";
export default StepController.extend({
actions: {
goNext(response) {
- const next = this.get("step.next");
+ let nextStepId = response["next_step_id"];
+
if (response.redirect_on_next) {
window.location.href = response.redirect_on_next;
} else if (response.refresh_required) {
- const id = this.get("wizard.id");
- window.location.href = getUrl(`/w/${id}/steps/${next}`);
+ const wizardId = this.get("wizard.id");
+ window.location.href = getUrl(`/w/${wizardId}/steps/${nextStepId}`);
} else {
- this.transitionToRoute("custom.step", next);
+ this.transitionToRoute("custom.step", nextStepId);
}
},
diff --git a/assets/javascripts/wizard/initializers/custom-wizard-step.js.es6 b/assets/javascripts/wizard/initializers/custom-wizard-step.js.es6
index 18fa9c70..fbbe7d8b 100644
--- a/assets/javascripts/wizard/initializers/custom-wizard-step.js.es6
+++ b/assets/javascripts/wizard/initializers/custom-wizard-step.js.es6
@@ -8,6 +8,9 @@ export default {
const CustomWizard = requirejs(
"discourse/plugins/discourse-custom-wizard/wizard/models/custom"
).default;
+ const updateCachedWizard = requirejs(
+ "discourse/plugins/discourse-custom-wizard/wizard/models/custom"
+ ).updateCachedWizard;
const StepModel = requirejs("wizard/models/step").default;
const StepComponent = requirejs("wizard/components/wizard-step").default;
const ajax = requirejs("wizard/lib/ajax").ajax;
@@ -18,6 +21,7 @@ export default {
"discourse/plugins/discourse-custom-wizard/wizard/lib/text-lite"
).cook;
const { schedule } = requirejs("@ember/runloop");
+ const { alias, not } = requirejs("@ember/object/computed");
StepModel.reopen({
save() {
@@ -155,12 +159,17 @@ export default {
this.sendAction("showMessage", message);
}.observes("step.message"),
+ showNextButton: not("step.final"),
+ showDoneButton: alias("step.final"),
+
advance() {
this.set("saving", true);
this.get("step")
.save()
.then((response) => {
- if (this.get("finalStep")) {
+ updateCachedWizard(CustomWizard.build(response["wizard"]));
+
+ if (response["final"]) {
CustomWizard.finished(response);
} else {
this.sendAction("goNext", response);
@@ -178,7 +187,6 @@ export default {
},
done() {
- this.set("finalStep", true);
this.send("nextStep");
},
diff --git a/assets/javascripts/wizard/models/custom.js.es6 b/assets/javascripts/wizard/models/custom.js.es6
index 4e214eed..31a403da 100644
--- a/assets/javascripts/wizard/models/custom.js.es6
+++ b/assets/javascripts/wizard/models/custom.js.es6
@@ -31,63 +31,45 @@ CustomWizard.reopenClass({
}
window.location.href = getUrl(url);
},
-});
-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) => {
- const wizard = result;
- if (!wizard) {
+ build(wizardJson) {
+ if (!wizardJson) {
return null;
}
- if (!wizard.completed) {
- wizard.steps = wizard.steps.map((step) => {
- const stepObj = Step.create(step);
+ if (!wizardJson.completed && wizardJson.steps) {
+ wizardJson.steps = wizardJson.steps
+ .map((step) => {
+ const stepObj = Step.create(step);
- stepObj.fields.sort((a, b) => {
- return parseFloat(a.number) - parseFloat(b.number);
+ stepObj.fields.sort((a, b) => {
+ 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 categoriesById = {};
- let categories = wizard.categories.map((c) => {
+ let categories = wizardJson.categories.map((c) => {
if (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(
"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;
diff --git a/assets/javascripts/wizard/routes/custom-index.js.es6 b/assets/javascripts/wizard/routes/custom-index.js.es6
index 05f2f2bf..a8abc152 100644
--- a/assets/javascripts/wizard/routes/custom-index.js.es6
+++ b/assets/javascripts/wizard/routes/custom-index.js.es6
@@ -1,22 +1,19 @@
+import { getCachedWizard } from "../models/custom";
+
export default Ember.Route.extend({
beforeModel() {
- const appModel = this.modelFor("custom");
- if (
- appModel &&
- appModel.permitted &&
- !appModel.completed &&
- appModel.start
- ) {
- this.replaceWith("custom.step", appModel.start);
+ const wizard = getCachedWizard();
+ if (wizard && wizard.permitted && !wizard.completed && wizard.start) {
+ this.replaceWith("custom.step", wizard.start);
}
},
model() {
- return this.modelFor("custom");
+ return getCachedWizard();
},
setupController(controller, model) {
- if (model) {
+ if (model && model.id) {
const completed = model.get("completed");
const permitted = model.get("permitted");
const wizardId = model.get("id");
diff --git a/assets/javascripts/wizard/routes/custom-step.js.es6 b/assets/javascripts/wizard/routes/custom-step.js.es6
index 3cb5db6e..8088727a 100644
--- a/assets/javascripts/wizard/routes/custom-step.js.es6
+++ b/assets/javascripts/wizard/routes/custom-step.js.es6
@@ -1,28 +1,33 @@
import WizardI18n from "../lib/wizard-i18n";
+import { getCachedWizard } from "../models/custom";
export default Ember.Route.extend({
- model(params) {
- const appModel = this.modelFor("custom");
- const allSteps = appModel.steps;
- if (allSteps) {
- const step = allSteps.findBy("id", params.step_id);
- return step ? step : allSteps[0];
- }
+ beforeModel() {
+ this.set("wizard", getCachedWizard());
+ },
- 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) {
if (model.completed) {
return this.transitionTo("index");
}
- return model.set("wizardId", this.modelFor("custom").id);
+ return model.set("wizardId", this.wizard.id);
},
setupController(controller, model) {
let props = {
step: model,
- wizard: this.modelFor("custom"),
+ wizard: this.wizard,
};
if (!model.permitted) {
diff --git a/assets/javascripts/wizard/routes/custom.js.es6 b/assets/javascripts/wizard/routes/custom.js.es6
index 041ea967..367fa36b 100644
--- a/assets/javascripts/wizard/routes/custom.js.es6
+++ b/assets/javascripts/wizard/routes/custom.js.es6
@@ -1,6 +1,6 @@
/* eslint no-undef: 0*/
-import { findCustomWizard } from "../models/custom";
+import { findCustomWizard, updateCachedWizard } from "../models/custom";
import { ajax } from "wizard/lib/ajax";
export default Ember.Route.extend({
@@ -12,7 +12,9 @@ export default Ember.Route.extend({
return findCustomWizard(params.wizard_id, this.get("queryParams"));
},
- afterModel() {
+ afterModel(model) {
+ updateCachedWizard(model);
+
return ajax({
url: `/site/settings`,
type: "GET",
@@ -25,11 +27,11 @@ export default Ember.Route.extend({
const background = model ? model.get("background") : "AliceBlue";
Ember.run.scheduleOnce("afterRender", this, function () {
$("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({
customWizard: true,
logoUrl: Wizard.SiteSettings.logo_small,
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-checkbox.hbs b/assets/javascripts/wizard/templates/components/wizard-field-checkbox.hbs
index 8cbc2b3e..053e0218 100644
--- a/assets/javascripts/wizard/templates/components/wizard-field-checkbox.hbs
+++ b/assets/javascripts/wizard/templates/components/wizard-field-checkbox.hbs
@@ -1 +1 @@
-{{input type="checkbox" id=field.id checked=field.value tabindex=field.tabindex}}
\ No newline at end of file
+{{input type="checkbox" id=field.id checked=field.value tabindex=field.tabindex}}
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-date-time.hbs b/assets/javascripts/wizard/templates/components/wizard-field-date-time.hbs
index 0b4f7916..dae4523d 100644
--- a/assets/javascripts/wizard/templates/components/wizard-field-date-time.hbs
+++ b/assets/javascripts/wizard/templates/components/wizard-field-date-time.hbs
@@ -2,4 +2,4 @@
date=dateTime
onChange=(action "onChange")
tabindex=field.tabindex
-}}
\ No newline at end of file
+}}
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-date.hbs b/assets/javascripts/wizard/templates/components/wizard-field-date.hbs
index 7b914807..4ac6571b 100644
--- a/assets/javascripts/wizard/templates/components/wizard-field-date.hbs
+++ b/assets/javascripts/wizard/templates/components/wizard-field-date.hbs
@@ -2,4 +2,4 @@
date=date
onChange=(action "onChange")
tabindex=field.tabindex
-}}
\ No newline at end of file
+}}
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs b/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs
index fbb68721..7ce4c298 100644
--- a/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs
+++ b/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs
@@ -5,4 +5,4 @@
tabindex=field.tabindex
options=(hash
none="select_kit.default_header_text"
- )}}
\ No newline at end of file
+ )}}
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-group.hbs b/assets/javascripts/wizard/templates/components/wizard-field-group.hbs
index 0c3b102d..92c08e2b 100644
--- a/assets/javascripts/wizard/templates/components/wizard-field-group.hbs
+++ b/assets/javascripts/wizard/templates/components/wizard-field-group.hbs
@@ -7,4 +7,4 @@
onChange=(action (mut field.value))
options=(hash
none="group.select"
- )}}
\ No newline at end of file
+ )}}
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-number.hbs b/assets/javascripts/wizard/templates/components/wizard-field-number.hbs
index 8704dac0..f5d6543c 100644
--- a/assets/javascripts/wizard/templates/components/wizard-field-number.hbs
+++ b/assets/javascripts/wizard/templates/components/wizard-field-number.hbs
@@ -1 +1 @@
-{{input type="number" step="0.01" id=field.id value=field.value tabindex=field.tabindex}}
\ No newline at end of file
+{{input type="number" step="0.01" id=field.id value=field.value tabindex=field.tabindex}}
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-time.hbs b/assets/javascripts/wizard/templates/components/wizard-field-time.hbs
index dafa9e59..d4cc425a 100644
--- a/assets/javascripts/wizard/templates/components/wizard-field-time.hbs
+++ b/assets/javascripts/wizard/templates/components/wizard-field-time.hbs
@@ -2,4 +2,4 @@
date=time
onChange=(action "onChange")
tabindex=field.tabindex
-}}
\ No newline at end of file
+}}
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-url.hbs b/assets/javascripts/wizard/templates/components/wizard-field-url.hbs
index e9015010..c7e1a508 100644
--- a/assets/javascripts/wizard/templates/components/wizard-field-url.hbs
+++ b/assets/javascripts/wizard/templates/components/wizard-field-url.hbs
@@ -1 +1 @@
-{{input type="text" id=field.id value=field.value tabindex=field.tabindex}}
\ No newline at end of file
+{{input type="text" id=field.id value=field.value tabindex=field.tabindex}}
diff --git a/assets/javascripts/wizard/templates/components/wizard-similar-topics.hbs b/assets/javascripts/wizard/templates/components/wizard-similar-topics.hbs
index e3163517..d1ee1fa1 100644
--- a/assets/javascripts/wizard/templates/components/wizard-similar-topics.hbs
+++ b/assets/javascripts/wizard/templates/components/wizard-similar-topics.hbs
@@ -8,4 +8,4 @@
{{wizard-i18n "realtime_validations.similar_topics.show"}}
-{{/if}}
\ No newline at end of file
+{{/if}}
diff --git a/assets/javascripts/wizard/templates/components/wizard-time-input.hbs b/assets/javascripts/wizard/templates/components/wizard-time-input.hbs
index 6207851b..73fa4968 100644
--- a/assets/javascripts/wizard/templates/components/wizard-time-input.hbs
+++ b/assets/javascripts/wizard/templates/components/wizard-time-input.hbs
@@ -10,4 +10,4 @@
autoInsertNoneItem=false
translatedFilterPlaceholder="--:--"
)
-}}
\ No newline at end of file
+}}
diff --git a/assets/stylesheets/common/wizard-admin.scss b/assets/stylesheets/common/wizard-admin.scss
index d7d00795..3c4b78da 100644
--- a/assets/stylesheets/common/wizard-admin.scss
+++ b/assets/stylesheets/common/wizard-admin.scss
@@ -303,6 +303,16 @@
max-width: 250px !important;
min-width: 250px !important;
}
+
+ &.force-final {
+ padding: 1em;
+ background-color: var(--primary-very-low);
+
+ label,
+ span {
+ font-size: 1em;
+ }
+ }
}
&.full,
diff --git a/assets/stylesheets/wizard/custom/base.scss b/assets/stylesheets/wizard/custom/base.scss
index 7aa884e9..39ab061a 100644
--- a/assets/stylesheets/wizard/custom/base.scss
+++ b/assets/stylesheets/wizard/custom/base.scss
@@ -50,6 +50,11 @@ textarea {
border-color: var(--danger);
box-shadow: shadow("focus-danger");
}
+
+ &[type="checkbox"] {
+ margin-bottom: 0;
+ margin-right: 10px;
+ }
}
.spinner {
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 82340d5e..d353c2df 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -56,6 +56,8 @@ en:
undo: "Undo"
clear: "Clear"
select_type: "Select a type"
+ condition: "Condition"
+ index: "Index"
message:
wizard:
@@ -157,6 +159,9 @@ en:
not_permitted_message: "Message shown when required data not present"
permitted_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:
header: "Fields"
diff --git a/controllers/custom_wizard/admin/manager.rb b/controllers/custom_wizard/admin/manager.rb
index 1596c233..2277de48 100644
--- a/controllers/custom_wizard/admin/manager.rb
+++ b/controllers/custom_wizard/admin/manager.rb
@@ -41,7 +41,7 @@ class CustomWizard::AdminManagerController < CustomWizard::AdminController
end
begin
- template_json = JSON.parse file
+ template_json = JSON.parse(file)
rescue JSON::ParserError
return render_error(I18n.t('wizard.import.error.invalid_json'))
end
diff --git a/controllers/custom_wizard/admin/wizard.rb b/controllers/custom_wizard/admin/wizard.rb
index 658f6682..0af55d95 100644
--- a/controllers/custom_wizard/admin/wizard.rb
+++ b/controllers/custom_wizard/admin/wizard.rb
@@ -37,7 +37,7 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
wizard_id = template.save(create: params[:create])
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
render json: success_json.merge(wizard_id: wizard_id)
end
@@ -83,15 +83,19 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
permitted: mapped_params,
steps: [
:id,
+ :index,
:title,
:key,
:banner,
:raw_description,
:required_data_message,
+ :force_final,
required_data: mapped_params,
permitted_params: mapped_params,
+ condition: mapped_params,
fields: [
:id,
+ :index,
:label,
:image,
:description,
@@ -107,6 +111,8 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
:property,
prefill: mapped_params,
content: mapped_params,
+ condition: mapped_params,
+ index: mapped_params,
validations: {},
]
],
diff --git a/controllers/custom_wizard/steps.rb b/controllers/custom_wizard/steps.rb
index 9c6eec47..277b94b2 100644
--- a/controllers/custom_wizard/steps.rb
+++ b/controllers/custom_wizard/steps.rb
@@ -4,31 +4,67 @@ class CustomWizard::StepsController < ::ApplicationController
before_action :ensure_can_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[: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|
update[:fields][k] = v if field_ids.include? k
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
+ @result = updater.result
if updater.success?
- result = success_json
- result.merge!(updater.result) if updater.result
+ wizard_id = update_params[:wizard_id]
+ 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[:wizard] = ::CustomWizard::WizardSerializer.new(
+ @wizard,
+ scope: Guardian.new(current_user),
+ root: false
+ ).as_json
render json: result
else
@@ -43,21 +79,31 @@ class CustomWizard::StepsController < ::ApplicationController
private
def ensure_can_update
- @builder = CustomWizard::Builder.new(
- update_params[:wizard_id].underscore,
- current_user
- )
+ @builder = CustomWizard::Builder.new(update_params[:wizard_id], current_user)
+ raise Discourse::InvalidParameters.new(:wizard_id) if @builder.template.nil?
+ raise Discourse::InvalidAccess.new if !@builder.wizard || !@builder.wizard.can_access?
- if @builder.nil?
- raise Discourse::InvalidParameters.new(:wizard_id)
- end
-
- if !@builder.wizard || !@builder.wizard.can_access?
- raise Discourse::InvalidAccess.new
- end
+ @step_template = @builder.template.steps.select do |s|
+ s['id'] == update_params[:step_id]
+ end.first
+ raise Discourse::InvalidParameters.new(:step_id) if !@step_template
+ raise Discourse::InvalidAccess.new if !@builder.check_condition(@step_template)
end
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
diff --git a/controllers/custom_wizard/wizard.rb b/controllers/custom_wizard/wizard.rb
index 5cbeb6e3..73c1f592 100644
--- a/controllers/custom_wizard/wizard.rb
+++ b/controllers/custom_wizard/wizard.rb
@@ -65,10 +65,7 @@ class CustomWizard::WizardController < ::ApplicationController
result.merge!(redirect_to: submission['redirect_to'])
end
- if user.custom_fields['redirect_to_wizard'] === wizard.id
- user.custom_fields.delete('redirect_to_wizard')
- user.save_custom_fields(true)
- end
+ wizard.final_cleanup!
end
render json: result
diff --git a/coverage/.last_run.json b/coverage/.last_run.json
index 42d46659..3e7f27f6 100644
--- a/coverage/.last_run.json
+++ b/coverage/.last_run.json
@@ -1,5 +1,5 @@
{
"result": {
- "line": 89.56
+ "line": 90.52
}
}
diff --git a/extensions/wizard_field.rb b/extensions/wizard_field.rb
deleted file mode 100644
index 2042872f..00000000
--- a/extensions/wizard_field.rb
+++ /dev/null
@@ -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
diff --git a/extensions/wizard_step.rb b/extensions/wizard_step.rb
deleted file mode 100644
index 4ae0224c..00000000
--- a/extensions/wizard_step.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-# frozen_string_literal: true
-module CustomWizardStepExtension
- attr_accessor :title, :description, :key, :permitted, :permitted_message
-end
diff --git a/lib/custom_wizard/action.rb b/lib/custom_wizard/action.rb
index 0d01c8f0..d68e978b 100644
--- a/lib/custom_wizard/action.rb
+++ b/lib/custom_wizard/action.rb
@@ -6,12 +6,12 @@ class CustomWizard::Action
:guardian,
:result
- def initialize(params)
- @wizard = params[:wizard]
- @action = params[:action]
- @user = params[:user]
+ def initialize(opts)
+ @wizard = opts[:wizard]
+ @action = opts[:action]
+ @user = @wizard.user
@guardian = Guardian.new(@user)
- @data = params[:data]
+ @data = opts[:data]
@log = []
@result = CustomWizard::ActionResult.new
end
@@ -89,11 +89,13 @@ class CustomWizard::Action
return
end
+ params[:target_group_names] = []
+ params[:target_usernames] = []
targets.each do |target|
if Group.find_by(name: target)
- params[:target_group_names] = target
+ params[:target_group_names] << target
elsif User.find_by_username(target)
- params[:target_usernames] = target
+ params[:target_usernames] << target
else
#
end
@@ -497,7 +499,13 @@ class CustomWizard::Action
).perform
params[:raw] = action['post_builder'] ?
- mapper.interpolate(action['post_template']) :
+ mapper.interpolate(
+ action['post_template'],
+ user: true,
+ value: true,
+ wizard: true,
+ template: true
+ ) :
data[action['post']]
params[:import_mode] = ActiveRecord::Type::Boolean.new.cast(action['suppress_notifications'])
diff --git a/lib/custom_wizard/builder.rb b/lib/custom_wizard/builder.rb
index 9a2d1060..813680c6 100644
--- a/lib/custom_wizard/builder.rb
+++ b/lib/custom_wizard/builder.rb
@@ -1,15 +1,11 @@
# frozen_string_literal: true
class CustomWizard::Builder
- attr_accessor :wizard, :updater, :submissions
+ attr_accessor :wizard, :updater, :template
def initialize(wizard_id, user = nil)
- template = CustomWizard::Template.find(wizard_id)
- return nil if template.blank?
-
- @wizard = CustomWizard::Wizard.new(template, user)
- @steps = template['steps'] || []
- @actions = template['actions'] || []
- @submissions = @wizard.submissions
+ @template = CustomWizard::Template.create(wizard_id)
+ return nil if @template.nil?
+ @wizard = CustomWizard::Wizard.new(template.data, user)
end
def self.sorted_handlers
@@ -28,7 +24,7 @@ class CustomWizard::Builder
def mapper
CustomWizard::Mapper.new(
user: @wizard.user,
- data: @submissions.last
+ data: @wizard.current_submission
)
end
@@ -38,103 +34,38 @@ class CustomWizard::Builder
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|
- step.permitted = true
+ step = check_if_permitted(step, step_template)
+ next if !step.permitted
- 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
- 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
+ save_permitted_params(step_template, params)
+ step = add_step_attributes(step, step_template)
+ step = append_step_fields(step, step_template, build_opts)
step.on_update do |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|
- if handler[:wizard_id] == @wizard.id
- handler[:block].call(self)
- end
- end
+ run_step_actions
- next if updater.errors.any?
-
- submission = updater.submission
-
- 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')
+ if @updater.errors.empty?
+ if route_to = @submission['route_to']
+ @submission.delete('route_to')
end
- if @wizard.save_submissions
- save_submissions(submission, final_step)
- 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
+ @wizard.save_submission(@submission)
+ @updater.result[:redirect_on_next] = route_to if route_to
true
else
@@ -144,25 +75,21 @@ class CustomWizard::Builder
end
end
+ @wizard.update_step_order!
@wizard
end
- def append_field(step, step_template, field_template, build_opts, index)
+ def append_field(step, step_template, field_template, build_opts)
params = {
id: field_template['id'],
type: field_template['type'],
- required: field_template['required'],
- number: index + 1
+ required: field_template['required']
}
- params[:label] = field_template['label'] if field_template['label']
- params[:description] = field_template['description'] if field_template['description']
- params[:image] = field_template['image'] if field_template['image']
- 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']
+ %w(label description image key validations min_length max_length char_counter).each do |key|
+ params[key.to_sym] = field_template[key] if field_template[key]
+ end
+
params[:value] = prefill_field(field_template, step_template)
if !build_opts[:reset] && (submission = @wizard.current_submission)
@@ -209,7 +136,7 @@ class CustomWizard::Builder
content = CustomWizard::Mapper.new(
inputs: content_inputs,
user: @wizard.user,
- data: @submissions.last,
+ data: @wizard.current_submission,
opts: {
with_type: true
}
@@ -240,6 +167,26 @@ class CustomWizard::Builder
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
+
+ if field_template['description'].present?
+ params[:description] = mapper.interpolate(
+ field_template['description'],
+ user: true,
+ value: true,
+ wizard: true,
+ template: true
+ )
+ end
+
field = step.add_field(params)
end
@@ -248,41 +195,99 @@ class CustomWizard::Builder
CustomWizard::Mapper.new(
inputs: prefill,
user: @wizard.user,
- data: @submissions.last
+ data: @wizard.current_submission
).perform
end
end
+ def check_condition(template)
+ if template['condition'].present?
+ result = CustomWizard::Mapper.new(
+ inputs: template['condition'],
+ user: @wizard.user,
+ data: @wizard.current_submission,
+ opts: {
+ multiple: true
+ }
+ ).perform
+
+ result.any?
+ 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,
+ wizard: true,
+ template: true
+ )
+ step.description = PrettyText.cook(step.description)
+ 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)
ActiveRecord::Type::Boolean.new.cast(value)
end
- def save_submissions(submission, final_step)
- if final_step
- submission['submitted_at'] = Time.now.iso8601
- end
+ def save_permitted_params(step_template, params)
+ return unless step_template['permitted_params'].present?
- if submission.present?
- @submissions.pop(1) if @wizard.unfinished?
- @submissions.push(submission)
- @wizard.set_submissions(@submissions)
- end
- end
-
- def save_permitted_params(permitted_params, params)
+ permitted_params = step_template['permitted_params']
permitted_data = {}
+ submission_key = nil
+ params_key = nil
+ submission = @wizard.current_submission || {}
permitted_params.each do |pp|
pair = pp['pairs'].first
params_key = pair['key'].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
- if permitted_data.present?
- current_data = @submissions.last || {}
- save_submissions(current_data.merge(permitted_data), false)
- end
+ @wizard.save_submission(submission)
end
def ensure_required_data(step, step_template)
@@ -291,13 +296,13 @@ class CustomWizard::Builder
pair['key'].present? && pair['value'].present?
end
- if pairs.any? && !@submissions.last
+ if pairs.any? && !@wizard.current_submission
step.permitted = false
break
end
pairs.each do |pair|
- pair['key'] = @submissions.last[pair['key']]
+ pair['key'] = @wizard.current_submission[pair['key']]
end
if !mapper.validate_pairs(pairs)
@@ -308,4 +313,26 @@ class CustomWizard::Builder
step
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
diff --git a/lib/custom_wizard/field.rb b/lib/custom_wizard/field.rb
index 1ddbcd3b..51e3d4de 100644
--- a/lib/custom_wizard/field.rb
+++ b/lib/custom_wizard/field.rb
@@ -1,5 +1,55 @@
# frozen_string_literal: true
+
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
@types ||= {
text: {
diff --git a/lib/custom_wizard/step.rb b/lib/custom_wizard/step.rb
new file mode 100644
index 00000000..5ffd8024
--- /dev/null
+++ b/lib/custom_wizard/step.rb
@@ -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
diff --git a/lib/custom_wizard/step_updater.rb b/lib/custom_wizard/step_updater.rb
index 3a9d65f1..ab86f3fa 100644
--- a/lib/custom_wizard/step_updater.rb
+++ b/lib/custom_wizard/step_updater.rb
@@ -2,14 +2,15 @@
class CustomWizard::StepUpdater
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)
@current_user = current_user
@wizard = wizard
@step = step
@refresh_required = false
- @submission = submission.to_h.with_indifferent_access
+ @submission = submission.with_indifferent_access
@result = {}
end
diff --git a/lib/custom_wizard/template.rb b/lib/custom_wizard/template.rb
index 91b97f73..a1c0aad0 100644
--- a/lib/custom_wizard/template.rb
+++ b/lib/custom_wizard/template.rb
@@ -4,10 +4,14 @@ class CustomWizard::Template
include HasErrors
attr_reader :data,
- :opts
+ :opts,
+ :steps,
+ :actions
def initialize(data)
@data = data
+ @steps = data['steps'] || []
+ @actions = data['actions'] || []
end
def save(opts = {})
@@ -31,6 +35,14 @@ class CustomWizard::Template
new(data).save(opts)
end
+ def self.create(wizard_id)
+ if data = find(wizard_id)
+ new(data)
+ else
+ nil
+ end
+ end
+
def self.find(wizard_id)
PluginStore.get(CustomWizard::PLUGIN_NAME, wizard_id)
end
@@ -86,7 +98,13 @@ class CustomWizard::Template
def prepare_data
@data[:steps].each do |step|
if step[:raw_description]
- step[:description] = PrettyText.cook(step[:raw_description])
+ step[:description] = step[:raw_description]
+ end
+
+ remove_non_mapped_index(step)
+
+ step[:fields].each do |field|
+ remove_non_mapped_index(field)
end
end
end
@@ -118,4 +136,10 @@ class CustomWizard::Template
end
end
end
+
+ def remove_non_mapped_index(object)
+ if !object[:index].is_a?(Array)
+ object.delete(:index)
+ end
+ end
end
diff --git a/lib/custom_wizard/wizard.rb b/lib/custom_wizard/wizard.rb
index 3e12c31d..82693eed 100644
--- a/lib/custom_wizard/wizard.rb
+++ b/lib/custom_wizard/wizard.rb
@@ -26,9 +26,10 @@ class CustomWizard::Wizard
:needs_groups,
:steps,
:step_ids,
+ :first_step,
+ :start,
:actions,
- :user,
- :first_step
+ :user
def initialize(attrs = {}, user = nil)
@user = user
@@ -68,8 +69,8 @@ class CustomWizard::Wizard
val.nil? ? false : ActiveRecord::Type::Boolean.new.cast(val)
end
- def create_step(step_name)
- ::Wizard::Step.new(step_name)
+ def create_step(step_id)
+ ::CustomWizard::Step.new(step_id)
end
def append_step(step)
@@ -77,37 +78,58 @@ class CustomWizard::Wizard
yield step if block_given?
- last_step = steps.last
steps << step
+ step.wizard = self
+ step.index = (steps.size == 1 ? 0 : steps.size) if step.index.nil?
+ end
- if steps.size == 1
- @first_step = step
- step.index = 0
- elsif last_step.present?
- last_step.next = step
- step.previous = last_step
- step.index = last_step.index + 1
+ def update_step_order!
+ steps.sort_by!(&:index)
+
+ steps.each_with_index do |step, index|
+ if index === 0
+ @first_step = step
+ @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
- def start
- return nil if !user
-
- if unfinished? && last_completed_step = ::UserHistory.where(
+ def last_completed_step_id
+ if user && unfinished? && last_completed_step = ::UserHistory.where(
acting_user_id: user.id,
action: ::UserHistory.actions[:custom_wizard_step],
context: id,
- subject: steps.map(&:id)
+ subject: step_ids
).order("created_at").last
- step_id = last_completed_step.subject
- last_index = steps.index { |s| s.id == step_id }
- steps[last_index + 1]
+ last_completed_step.subject
else
- @first_step
+ nil
end
end
+ def find_step(step_id)
+ steps.select { |step| step.id === step_id }.first
+ end
+
def create_updater(step_id, submission)
step = @steps.find { |s| s.id == step_id }
wizard = self
@@ -200,12 +222,13 @@ class CustomWizard::Wizard
end
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
def current_submission
- if submissions.present? && !submissions.last.key?("submitted_at")
- submissions.last
+ if submissions.present? && submissions.last.present? && !submissions.last.key?("submitted_at")
+ submissions.last.with_indifferent_access
else
nil
end
@@ -213,6 +236,27 @@ class CustomWizard::Wizard
def set_submissions(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
def self.submissions(wizard_id, user)
@@ -276,7 +320,7 @@ class CustomWizard::Wizard
end
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
def self.set_wizard_redirect(wizard_id, user)
diff --git a/plugin.rb b/plugin.rb
index 56673a13..e3d32129 100644
--- a/plugin.rb
+++ b/plugin.rb
@@ -66,6 +66,7 @@ after_initialize do
../lib/custom_wizard/mapper.rb
../lib/custom_wizard/log.rb
../lib/custom_wizard/step_updater.rb
+ ../lib/custom_wizard/step.rb
../lib/custom_wizard/template.rb
../lib/custom_wizard/wizard.rb
../lib/custom_wizard/api/api.rb
@@ -89,8 +90,6 @@ after_initialize do
../extensions/extra_locales_controller.rb
../extensions/invites_controller.rb
../extensions/users_controller.rb
- ../extensions/wizard_field.rb
- ../extensions/wizard_step.rb
../extensions/custom_field/preloader.rb
../extensions/custom_field/serializer.rb
].each do |path|
@@ -172,8 +171,6 @@ after_initialize do
::ExtraLocalesController.prepend ExtraLocalesControllerCustomWizard
::InvitesController.prepend InvitesControllerCustomWizard
::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"
if Stylesheet::Importer.respond_to?(:plugin_assets)
diff --git a/serializers/custom_wizard/wizard_field_serializer.rb b/serializers/custom_wizard/wizard_field_serializer.rb
index f9c42e6c..19025dff 100644
--- a/serializers/custom_wizard/wizard_field_serializer.rb
+++ b/serializers/custom_wizard/wizard_field_serializer.rb
@@ -1,8 +1,16 @@
# 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,
:format,
:limit,
@@ -10,19 +18,54 @@ class CustomWizard::FieldSerializer < ::WizardFieldSerializer
:content,
:validations,
:max_length,
- :char_counter,
- :number
+ :char_counter
+
+ 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
return object.label if object.label.present?
I18n.t("#{object.key || i18n_key}.label", default: '')
end
+ def include_label?
+ label.present?
+ end
+
def description
return object.description if object.description.present?
I18n.t("#{object.key || i18n_key}.description", default: '', base_url: Discourse.base_url)
end
+ def include_description?
+ description.present?
+ end
+
def image
object.image
end
@@ -35,6 +78,10 @@ class CustomWizard::FieldSerializer < ::WizardFieldSerializer
I18n.t("#{object.key || i18n_key}.placeholder", default: '')
end
+ def include_placeholder?
+ placeholder.present?
+ end
+
def file_types
object.file_types
end
@@ -55,10 +102,6 @@ class CustomWizard::FieldSerializer < ::WizardFieldSerializer
object.content
end
- def include_choices?
- object.choices.present?
- end
-
def validations
validations = {}
object.validations&.each do |type, props|
@@ -77,8 +120,4 @@ class CustomWizard::FieldSerializer < ::WizardFieldSerializer
def char_counter
object.char_counter
end
-
- def number
- object.number
- end
end
diff --git a/serializers/custom_wizard/wizard_serializer.rb b/serializers/custom_wizard/wizard_serializer.rb
index 9ab7291f..f858c195 100644
--- a/serializers/custom_wizard/wizard_serializer.rb
+++ b/serializers/custom_wizard/wizard_serializer.rb
@@ -30,7 +30,7 @@ class CustomWizard::WizardSerializer < CustomWizard::BasicWizardSerializer
end
def start
- object.start.id
+ object.start
end
def include_start?
diff --git a/serializers/custom_wizard/wizard_step_serializer.rb b/serializers/custom_wizard/wizard_step_serializer.rb
index b5648e46..85f527bb 100644
--- a/serializers/custom_wizard/wizard_step_serializer.rb
+++ b/serializers/custom_wizard/wizard_step_serializer.rb
@@ -1,20 +1,74 @@
# 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
+ 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
return PrettyText.cook(object.title) if object.title
PrettyText.cook(I18n.t("#{object.key || i18n_key}.title", default: ''))
end
+ def include_title?
+ title.present?
+ end
+
def description
return object.description if object.description
PrettyText.cook(I18n.t("#{object.key || i18n_key}.description", default: '', base_url: Discourse.base_url))
end
+ def include_description?
+ description.present?
+ end
+
+ def banner
+ object.banner
+ end
+
+ def include_banner?
+ object.banner.present?
+ end
+
def permitted
object.permitted
end
@@ -22,4 +76,8 @@ class CustomWizard::StepSerializer < ::WizardStepSerializer
def permitted_message
object.permitted_message
end
+
+ def final
+ object.final?
+ end
end
diff --git a/spec/components/custom_wizard/action_spec.rb b/spec/components/custom_wizard/action_spec.rb
index bbf266d4..28f2cab8 100644
--- a/spec/components/custom_wizard/action_spec.rb
+++ b/spec/components/custom_wizard/action_spec.rb
@@ -6,19 +6,25 @@ describe CustomWizard::Action do
fab!(:category) { Fabricate(:category, name: 'cat1', slug: 'cat-slug') }
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) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/actions/open_composer.json"
- ).read)
+ JSON.parse(
+ File.open(
+ "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/actions/open_composer.json"
+ ).read
+ )
}
before do
Group.refresh_automatic_group!(:trust_level_2)
- CustomWizard::Template.save(
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read),
- skip_jobs: true)
+ CustomWizard::Template.save(wizard_template, skip_jobs: true)
@template = CustomWizard::Template.find('super_mega_fun_wizard')
end
@@ -68,26 +74,52 @@ describe CustomWizard::Action do
end
end
- it 'sends a message' do
- User.create(username: 'angus1', email: "angus1@email.com")
+ context 'sending a message' do
+ it 'works' do
+ User.create(username: 'angus1', email: "angus1@email.com")
- wizard = CustomWizard::Builder.new(@template[:id], user).build
- wizard.create_updater(wizard.steps[0].id, {}).update
- wizard.create_updater(wizard.steps[1].id, {}).update
+ wizard = CustomWizard::Builder.new(@template[:id], user).build
+ wizard.create_updater(wizard.steps[0].id, {}).update
+ wizard.create_updater(wizard.steps[1].id, {}).update
- topic = Topic.where(
- archetype: Archetype.private_message,
- title: "Message title"
- )
+ topic = Topic.where(
+ archetype: Archetype.private_message,
+ title: "Message title"
+ )
- post = Post.where(
- topic_id: topic.pluck(:id),
- raw: "I will interpolate some wizard fields"
- )
+ post = Post.where(
+ topic_id: topic.pluck(:id),
+ raw: "I will interpolate some wizard fields"
+ )
- expect(topic.exists?).to eq(true)
- expect(topic.first.topic_allowed_users.first.user.username).to eq('angus1')
- expect(post.exists?).to eq(true)
+ expect(topic.exists?).to eq(true)
+ expect(topic.first.topic_allowed_users.first.user.username).to eq('angus1')
+ expect(post.exists?).to eq(true)
+ end
+
+ it 'allows using multiple PM targets' do
+ User.create(username: 'angus1', email: "angus1@email.com")
+ User.create(username: 'faiz', email: "faiz@email.com")
+ Group.create(name: "cool_group")
+ Group.create(name: 'cool_group_1')
+ wizard = CustomWizard::Builder.new(@template[:id], user).build
+ wizard.create_updater(wizard.steps[0].id, {}).update
+ wizard.create_updater(wizard.steps[1].id, {}).update
+
+ topic = Topic.where(
+ archetype: Archetype.private_message,
+ title: "Multiple Recipients title"
+ )
+
+ post = Post.where(
+ topic_id: topic.pluck(:id),
+ raw: "I will interpolate some wizard fields"
+ )
+ expect(topic.exists?).to eq(true)
+ expect(topic.first.all_allowed_users.map(&:username)).to include('angus1', 'faiz')
+ expect(topic.first.allowed_groups.map(&:name)).to include('cool_group', 'cool_group_1')
+ expect(post.exists?).to eq(true)
+ end
end
it 'updates a profile' do
@@ -130,7 +162,6 @@ describe CustomWizard::Action do
action = CustomWizard::Action.new(
wizard: wizard,
action: open_composer,
- user: user,
data: {}
)
action.perform
@@ -153,8 +184,7 @@ describe CustomWizard::Action do
it 'creates a group' do
wizard = CustomWizard::Builder.new(@template[:id], user).build
- step_id = wizard.steps[0].id
- updater = wizard.create_updater(step_id, step_1_field_1: "Text input").update
+ wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
expect(Group.where(name: wizard.current_submission['action_9']).exists?).to eq(true)
end
@@ -184,6 +214,6 @@ describe CustomWizard::Action do
wizard = CustomWizard::Builder.new(@template[:id], user).build
updater = wizard.create_updater(wizard.steps.last.id, {})
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
diff --git a/spec/components/custom_wizard/builder_spec.rb b/spec/components/custom_wizard/builder_spec.rb
index 54a6ceff..d9d3524e 100644
--- a/spec/components/custom_wizard/builder_spec.rb
+++ b/spec/components/custom_wizard/builder_spec.rb
@@ -16,21 +16,35 @@ describe CustomWizard::Builder do
fab!(:group) { Fabricate(:group) }
let(:required_data_json) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/required_data.json"
- ).read)
+ JSON.parse(
+ File.open(
+ "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/required_data.json"
+ ).read
+ )
}
let(:permitted_json) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
- ).read)
+ JSON.parse(
+ File.open(
+ "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
+ ).read
+ )
}
let(:permitted_param_json) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/permitted_params.json"
- ).read)
+ JSON.parse(
+ File.open(
+ "#{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
@@ -263,6 +277,23 @@ describe CustomWizard::Builder do
expect(wizard.current_submission['saved_param']).to eq('param_value')
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
context 'building field' do
@@ -284,6 +315,23 @@ describe CustomWizard::Builder do
.fields.length
).to eq(4)
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
context 'on update' do
diff --git a/spec/components/custom_wizard/field_spec.rb b/spec/components/custom_wizard/field_spec.rb
index f6c2d68b..871c42cd 100644
--- a/spec/components/custom_wizard/field_spec.rb
+++ b/spec/components/custom_wizard/field_spec.rb
@@ -2,6 +2,12 @@
require_relative '../../plugin_helper'
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
CustomWizard::Field.register(
'location',
@@ -13,6 +19,19 @@ describe CustomWizard::Field do
)
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("
Field Label
")
+ 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
expect(CustomWizard::Field.types[:location].present?).to eq(true)
end
diff --git a/spec/components/custom_wizard/mapper_spec.rb b/spec/components/custom_wizard/mapper_spec.rb
index aa34f9f1..434f0001 100644
--- a/spec/components/custom_wizard/mapper_spec.rb
+++ b/spec/components/custom_wizard/mapper_spec.rb
@@ -130,21 +130,63 @@ describe CustomWizard::Mapper do
end
end
- it "validates valid data" do
- expect(CustomWizard::Mapper.new(
- inputs: inputs['validation'],
- data: data,
- user: user1
- ).perform).to eq(true)
- end
+ context "conditional validation" do
+ it "validates valid data" do
+ expect(CustomWizard::Mapper.new(
+ inputs: inputs['validation'],
+ data: data,
+ user: user1
+ ).perform).to eq(true)
+ end
- it "does not validate invalid data" do
- data["input_2"] = "value 3"
- expect(CustomWizard::Mapper.new(
- inputs: inputs['validation'],
- data: data,
- user: user1
- ).perform).to eq(false)
+ it "does not validate invalid data" do
+ data["input_2"] = "value 3"
+ expect(CustomWizard::Mapper.new(
+ inputs: inputs['validation'],
+ data: data,
+ user: user1
+ ).perform).to eq(false)
+ end
+
+ context "using or condition" do
+ it "validates the data when all of the conditions are met" do
+ expect(CustomWizard::Mapper.new(
+ inputs: inputs['validation_multiple_pairs'],
+ data: data,
+ user: user1,
+ opts: {
+ multiple: true
+ }
+ ).perform.any?).to eq(true)
+ end
+
+ it "validates the data when one of the conditions are met" do
+ custom_data = data.dup
+ custom_data['input_1'] = 'value 3'
+ expect(CustomWizard::Mapper.new(
+ inputs: inputs['validation_multiple_pairs'],
+ data: custom_data,
+ user: user1,
+ opts: {
+ multiple: true
+ }
+ ).perform.any?).to eq(true)
+ end
+
+ it "doesn't validate the data when none of the conditions are met" do
+ custom_data = data.dup
+ custom_data['input_1'] = 'value 3'
+ custom_data['input_2'] = 'value 4'
+ expect(CustomWizard::Mapper.new(
+ inputs: inputs['validation_multiple_pairs'],
+ data: custom_data,
+ user: user1,
+ opts: {
+ multiple: true
+ }
+ ).perform.any?).to eq(false)
+ end
+ end
end
it "maps text fields" do
diff --git a/spec/components/custom_wizard/step_spec.rb b/spec/components/custom_wizard/step_spec.rb
new file mode 100644
index 00000000..bf4613a4
--- /dev/null
+++ b/spec/components/custom_wizard/step_spec.rb
@@ -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
diff --git a/spec/components/custom_wizard/wizard_spec.rb b/spec/components/custom_wizard/wizard_spec.rb
index 57d15241..aed44fe6 100644
--- a/spec/components/custom_wizard/wizard_spec.rb
+++ b/spec/components/custom_wizard/wizard_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require_relative '../../plugin_helper'
describe CustomWizard::Wizard do
@@ -7,27 +8,33 @@ describe CustomWizard::Wizard do
fab!(:admin_user) { Fabricate(:user, admin: true) }
let(:template_json) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read)
+ JSON.parse(
+ File.open(
+ "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
+ ).read
+ )
}
let(:permitted_json) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
- ).read)
+ JSON.parse(
+ File.open(
+ "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
+ ).read
+ )
}
before do
Group.refresh_automatic_group!(:trust_level_3)
-
@permitted_template = template_json.dup
@permitted_template["permitted"] = permitted_json["permitted"]
-
@wizard = CustomWizard::Wizard.new(template_json, user)
+ end
+
+ def append_steps
template_json['steps'].each do |step_template|
@wizard.append_step(step_template['id'])
end
+ @wizard.update_step_order!
end
def progress_step(step_id, acting_user: user, wizard: @wizard)
@@ -37,16 +44,48 @@ describe CustomWizard::Wizard do
context: wizard.id,
subject: step_id
)
+ @wizard.update_step_order!
end
- it "appends steps from a template" do
+ it "appends steps" do
+ append_steps
expect(@wizard.steps.length).to eq(3)
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
- expect(@wizard.start.id).to eq('step_1')
+ append_steps
+ expect(@wizard.start).to eq('step_1')
progress_step('step_1')
- expect(@wizard.start.id).to eq('step_2')
+ expect(@wizard.start).to eq('step_2')
end
it "creates a step updater" do
@@ -57,6 +96,7 @@ describe CustomWizard::Wizard do
end
it "determines whether a wizard is unfinished" do
+ append_steps
expect(@wizard.unfinished?).to eq(true)
progress_step("step_1")
expect(@wizard.unfinished?).to eq(true)
@@ -67,6 +107,7 @@ describe CustomWizard::Wizard do
end
it "determines whether a wizard has been completed by a user" do
+ append_steps
expect(@wizard.completed?).to eq(false)
progress_step("step_1")
progress_step("step_2")
@@ -75,6 +116,8 @@ describe CustomWizard::Wizard do
end
it "is not completed if steps submitted before after time" do
+ append_steps
+
progress_step("step_1")
progress_step("step_2")
progress_step("step_3")
@@ -83,7 +126,6 @@ describe CustomWizard::Wizard do
template_json['after_time_scheduled'] = Time.now + 3.hours
wizard = CustomWizard::Wizard.new(template_json, user)
-
expect(wizard.completed?).to eq(false)
end
@@ -125,6 +167,8 @@ describe CustomWizard::Wizard do
end
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_2", acting_user: trusted_user)
progress_step("step_3", acting_user: trusted_user)
@@ -135,6 +179,8 @@ describe CustomWizard::Wizard do
end
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_2", acting_user: trusted_user)
progress_step("step_3", acting_user: trusted_user)
diff --git a/spec/extensions/wizard_field_spec.rb b/spec/extensions/wizard_field_spec.rb
deleted file mode 100644
index 370c25e7..00000000
--- a/spec/extensions/wizard_field_spec.rb
+++ /dev/null
@@ -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("
Because I couldn’t think of another name for this step
"
+ "description": "Because I couldn't think of another name for this step :)"
},
{
"id": "step_3",
@@ -160,7 +160,7 @@
"type": "user_selector"
}
],
- "description": "
Unfortunately not the edible type
"
+ "description": "Unfortunately not the edible type :sushi: "
}
],
"actions": [
@@ -464,6 +464,34 @@
}
]
},
+ {
+ "id": "action_11",
+ "run_after": "step_2",
+ "type": "send_message",
+ "post_builder": true,
+ "post_template": "I will interpolate some wizard fields w{step_1_field_1} w{step_1_field_2}",
+ "title": [
+ {
+ "type": "assignment",
+ "output": "Multiple Recipients title",
+ "output_type": "text",
+ "output_connector": "set"
+ }
+ ],
+ "recipient": [
+ {
+ "type": "assignment",
+ "output_type": "user",
+ "output_connector": "set",
+ "output": [
+ "angus1",
+ "faiz",
+ "cool_group",
+ "cool_group_1"
+ ]
+ }
+ ]
+ },
{
"id": "action_3",
"run_after": "step_2",
@@ -507,7 +535,7 @@
},
{
"id": "action_10",
- "run_after": "wizard_completion",
+ "run_after": "step_3",
"type": "route_to",
"url": [
{
diff --git a/spec/requests/custom_wizard/steps_controller_spec.rb b/spec/requests/custom_wizard/steps_controller_spec.rb
index dd3b52a1..c58f13a2 100644
--- a/spec/requests/custom_wizard/steps_controller_spec.rb
+++ b/spec/requests/custom_wizard/steps_controller_spec.rb
@@ -11,12 +11,49 @@ describe CustomWizard::StepsController do
)
}
- before do
- CustomWizard::Template.save(
- JSON.parse(File.open(
+ fab!(:user2) {
+ Fabricate(
+ :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"
- ).read),
- skip_jobs: true)
+ ).read
+ )
+ }
+
+ 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)
end
@@ -27,17 +64,189 @@ describe CustomWizard::StepsController do
}
}
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
- expect(wizard.current_submission['step_1_field_1']).to eq("Text input")
- expect(wizard.start.id).to eq("step_2")
+ wizard_id = response.parsed_body['wizard']['id']
+ wizard = CustomWizard::Wizard.create(wizard_id, user)
+ 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
it "works if the step has no fields" do
put '/w/super-mega-fun-wizard/steps/step_1.json'
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
- expect(wizard.start.id).to eq("step_2")
+ it "returns an updated wizard when condition passes" 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 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
diff --git a/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb b/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb
index 944947e6..1fa9671c 100644
--- a/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb
+++ b/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb
@@ -20,9 +20,11 @@ describe CustomWizard::FieldSerializer do
each_serializer: CustomWizard::FieldSerializer,
scope: Guardian.new(user)
).as_json
- expect(json_array.length).to eq(4)
+
+ expect(json_array.size).to eq(4)
expect(json_array[0][:label]).to eq("
Text
")
expect(json_array[0][:description]).to eq("Text field description.")
+ expect(json_array[3][:index]).to eq(3)
end
it "should return optional field attributes" do
@@ -32,7 +34,6 @@ describe CustomWizard::FieldSerializer do
scope: Guardian.new(user)
).as_json
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")
end
end
diff --git a/spec/serializers/custom_wizard/wizard_step_serializer.rb b/spec/serializers/custom_wizard/wizard_step_serializer.rb
deleted file mode 100644
index c2d82962..00000000
--- a/spec/serializers/custom_wizard/wizard_step_serializer.rb
+++ /dev/null
@@ -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
diff --git a/spec/serializers/custom_wizard/wizard_step_serializer_spec.rb b/spec/serializers/custom_wizard/wizard_step_serializer_spec.rb
new file mode 100644
index 00000000..35ce0fd2
--- /dev/null
+++ b/spec/serializers/custom_wizard/wizard_step_serializer_spec.rb
@@ -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("
Text
")
+ expect(json_array[0][:description]).to eq("
Text inputs!
")
+ 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