diff --git a/.github/workflows/plugin-linting.yml b/.github/workflows/plugin-linting.yml
index a121658d..a79c5462 100644
--- a/.github/workflows/plugin-linting.yml
+++ b/.github/workflows/plugin-linting.yml
@@ -6,6 +6,8 @@ on:
- master
- main
pull_request:
+ schedule:
+ - cron: '0 0 * * *'
jobs:
build:
diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml
index ce6112af..fcf1a1d0 100644
--- a/.github/workflows/plugin-tests.yml
+++ b/.github/workflows/plugin-tests.yml
@@ -6,6 +6,8 @@ on:
- master
- main
pull_request:
+ schedule:
+ - cron: '0 */12 * * *'
jobs:
build:
@@ -51,23 +53,27 @@ jobs:
repository: discourse/discourse
fetch-depth: 1
+ - name: Fetch Repo Name
+ id: repo-name
+ run: echo "::set-output name=value::$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')"
+
- name: Install plugin
uses: actions/checkout@v2
with:
- path: plugins/${{ github.event.repository.name }}
+ path: plugins/${{ steps.repo-name.outputs.value }}
fetch-depth: 1
- name: Check spec existence
id: check_spec
uses: andstor/file-existence-action@v1
with:
- files: "plugins/${{ github.event.repository.name }}/spec"
+ files: "plugins/${{ steps.repo-name.outputs.value }}/spec"
- name: Check qunit existence
id: check_qunit
uses: andstor/file-existence-action@v1
with:
- files: "plugins/${{ github.event.repository.name }}/test/javascripts"
+ files: "plugins/${{ steps.repo-name.outputs.value }}/test/javascripts"
- name: Setup Git
run: |
@@ -100,7 +106,7 @@ jobs:
- name: Lint English locale
if: matrix.build_type == 'backend'
- run: bundle exec ruby script/i18n_lint.rb "plugins/${{ github.event.repository.name }}/locales/{client,server}.en.yml"
+ run: bundle exec ruby script/i18n_lint.rb "plugins/${{ steps.repo-name.outputs.value }}/locales/{client,server}.en.yml"
- name: Get yarn cache directory
id: yarn-cache-dir
@@ -123,15 +129,11 @@ jobs:
bin/rake db:create
bin/rake db:migrate
- - name: Plugin RSpec
+ - name: Plugin RSpec with Coverage
if: matrix.build_type == 'backend' && steps.check_spec.outputs.files_exists == 'true'
- run: bin/rake plugin:spec[${{ github.event.repository.name }}]
+ run: SIMPLECOV=1 bin/rake plugin:spec[${{ steps.repo-name.outputs.value }}]
- name: Plugin QUnit
if: matrix.build_type == 'frontend' && steps.check_qunit.outputs.files_exists == 'true'
- run: bundle exec rake plugin:qunit['${{ github.event.repository.name }}','1200000']
+ run: bundle exec rake plugin:qunit['${{ steps.repo-name.outputs.value }}','1200000']
timeout-minutes: 30
-
- - name: Simplecov Report
- if: matrix.build_type == 'backend'
- run: COVERAGE=1 bin/rake plugin:spec[${{ github.event.repository.name }}]
diff --git a/assets/javascripts/discourse/components/custom-field-input.js.es6 b/assets/javascripts/discourse/components/custom-field-input.js.es6
index f2dca4c7..e49c6f1d 100644
--- a/assets/javascripts/discourse/components/custom-field-input.js.es6
+++ b/assets/javascripts/discourse/components/custom-field-input.js.es6
@@ -1,6 +1,6 @@
import Component from "@ember/component";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
-import { alias, or } from "@ember/object/computed";
+import { alias, equal, or } from "@ember/object/computed";
import I18n from "I18n";
const generateContent = function (array, type) {
@@ -29,6 +29,7 @@ export default Component.extend({
loading: or("saving", "destroying"),
destroyDisabled: alias("loading"),
closeDisabled: alias("loading"),
+ isExternal: equal("field.id", "external"),
didInsertElement() {
this.set("originalField", JSON.parse(JSON.stringify(this.field)));
@@ -61,13 +62,14 @@ export default Component.extend({
@discourseComputed(
"saving",
+ "isExternal",
"field.name",
"field.klass",
"field.type",
"field.serializers"
)
- saveDisabled(saving) {
- if (saving) {
+ saveDisabled(saving, isExternal) {
+ if (saving || isExternal) {
return true;
}
diff --git a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 b/assets/javascripts/discourse/components/wizard-custom-action.js.es6
index c8309f10..feb83754 100644
--- a/assets/javascripts/discourse/components/wizard-custom-action.js.es6
+++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6
@@ -62,6 +62,11 @@ export default Component.extend(UndoChanges, {
return key;
},
+ @discourseComputed("action.type")
+ customFieldsContext(type) {
+ return `action.${type}`;
+ },
+
@discourseComputed("wizard.steps")
runAfterContent(steps) {
let content = steps.map(function (step) {
diff --git a/assets/javascripts/discourse/components/wizard-custom-field.js.es6 b/assets/javascripts/discourse/components/wizard-custom-field.js.es6
index b5f6b0ee..b5c10c65 100644
--- a/assets/javascripts/discourse/components/wizard-custom-field.js.es6
+++ b/assets/javascripts/discourse/components/wizard-custom-field.js.es6
@@ -25,6 +25,7 @@ export default Component.extend(UndoChanges, {
showContent: or("isCategory", "isTag", "isGroup", "isDropdown"),
showLimit: or("isCategory", "isTag"),
isTextType: or("isText", "isTextarea", "isComposer"),
+ isComposerPreview: equal("field.type", "composer_preview"),
categoryPropertyTypes: selectKitContent(["id", "slug"]),
showAdvanced: alias("field.type"),
messageUrl: "https://thepavilion.io/t/2809",
diff --git a/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6 b/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6
index 6d65d782..7d9b0bbd 100644
--- a/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6
+++ b/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6
@@ -6,11 +6,24 @@ import {
} from "discourse-common/utils/decorators";
import { getOwner } from "discourse-common/lib/get-owner";
import { defaultSelectionType, selectionTypes } from "../lib/wizard-mapper";
-import { generateName, snakeCase, userProperties } from "../lib/wizard";
+import {
+ generateName,
+ sentenceCase,
+ snakeCase,
+ userProperties,
+} from "../lib/wizard";
import Component from "@ember/component";
import { bind, later } from "@ember/runloop";
import I18n from "I18n";
+const customFieldActionMap = {
+ topic: ["create_topic", "send_message"],
+ post: ["create_topic", "send_message"],
+ category: ["create_category"],
+ group: ["create_group"],
+ user: ["update_profile"],
+};
+
export default Component.extend({
classNameBindings: [":mapper-selector", "activeType"],
@@ -188,11 +201,19 @@ export default Component.extend({
customFields
) {
let content;
+ let context;
+ let contextType;
+
+ if (this.options.context) {
+ let contextAttrs = this.options.context.split(".");
+ context = contextAttrs[0];
+ contextType = contextAttrs[1];
+ }
if (activeType === "wizardField") {
content = wizardFields;
- if (this.options.context === "field") {
+ if (context === "field") {
content = content.filter((field) => field.id !== currentFieldId);
}
}
@@ -204,7 +225,7 @@ export default Component.extend({
type: a.type,
}));
- if (this.options.context === "action") {
+ if (context === "action") {
content = content.filter((a) => a.id !== currentActionId);
}
}
@@ -218,7 +239,7 @@ export default Component.extend({
.concat(userFields || []);
if (
- this.options.context === "action" &&
+ context === "action" &&
this.inputType === "association" &&
this.selectorType === "key"
) {
@@ -234,7 +255,17 @@ export default Component.extend({
}
if (activeType === "customField") {
- content = customFields;
+ content = customFields
+ .filter((f) => {
+ return (
+ f.type !== "json" &&
+ customFieldActionMap[f.klass].includes(contextType)
+ );
+ })
+ .map((f) => ({
+ id: f.name,
+ name: `${sentenceCase(f.klass)} ${f.name} (${f.type})`,
+ }));
}
return content;
diff --git a/assets/javascripts/discourse/controllers/admin-wizards-custom-fields.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-custom-fields.js.es6
index 2081cfe3..404c6afd 100644
--- a/assets/javascripts/discourse/controllers/admin-wizards-custom-fields.js.es6
+++ b/assets/javascripts/discourse/controllers/admin-wizards-custom-fields.js.es6
@@ -3,12 +3,12 @@ import CustomWizardCustomField from "../models/custom-wizard-custom-field";
export default Controller.extend({
messageKey: "create",
- fieldKeys: ["klass", "type", "serializers", "name"],
+ fieldKeys: ["klass", "type", "name", "serializers"],
documentationUrl: "https://thepavilion.io/t/3572",
actions: {
addField() {
- this.get("customFields").pushObject(
+ this.get("customFields").unshiftObject(
CustomWizardCustomField.create({ edit: true })
);
},
diff --git a/assets/javascripts/discourse/lib/wizard.js.es6 b/assets/javascripts/discourse/lib/wizard.js.es6
index 1896b1fe..98bdbfdd 100644
--- a/assets/javascripts/discourse/lib/wizard.js.es6
+++ b/assets/javascripts/discourse/lib/wizard.js.es6
@@ -120,4 +120,5 @@ export {
listProperties,
notificationLevels,
wizardFieldList,
+ sentenceCase,
};
diff --git a/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6
index abcaeb9e..73168ff3 100644
--- a/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6
+++ b/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6
@@ -1,17 +1,19 @@
import CustomWizard from "../models/custom-wizard";
import DiscourseRoute from "discourse/routes/discourse";
+const excludedMetaFields = ["route_to", "redirect_on_complete", "redirect_to"];
+
export default DiscourseRoute.extend({
model(params) {
return CustomWizard.submissions(params.wizardId);
},
setupController(controller, model) {
- if (model.submissions) {
- let fields = [];
+ if (model && model.submissions) {
+ let fields = ["username"];
model.submissions.forEach((s) => {
- Object.keys(s).forEach((k) => {
- if (fields.indexOf(k) < 0) {
+ Object.keys(s.fields).forEach((k) => {
+ if (!excludedMetaFields.includes(k) && fields.indexOf(k) < 0) {
fields.push(k);
}
});
@@ -19,9 +21,13 @@ export default DiscourseRoute.extend({
let submissions = [];
model.submissions.forEach((s) => {
- let submission = {};
- fields.forEach((f) => {
- submission[f] = s[f];
+ let submission = {
+ username: s.username,
+ };
+ Object.keys(s.fields).forEach((f) => {
+ if (fields.includes(f)) {
+ submission[f] = s.fields[f];
+ }
});
submissions.push(submission);
});
diff --git a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6
index eaa6591c..cb2d54c3 100644
--- a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6
+++ b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6
@@ -2,7 +2,6 @@ import CustomWizard from "../models/custom-wizard";
import { ajax } from "discourse/lib/ajax";
import DiscourseRoute from "discourse/routes/discourse";
import I18n from "I18n";
-import { selectKitContent } from "../lib/wizard";
export default DiscourseRoute.extend({
model(params) {
@@ -33,9 +32,7 @@ export default DiscourseRoute.extend({
wizardList: parentModel.wizard_list,
fieldTypes,
userFields: parentModel.userFields,
- customFields: selectKitContent(
- parentModel.custom_fields.map((f) => f.name)
- ),
+ customFields: parentModel.custom_fields,
apis: parentModel.apis,
themes: parentModel.themes,
wizard,
diff --git a/assets/javascripts/discourse/templates/admin-wizards-logs.hbs b/assets/javascripts/discourse/templates/admin-wizards-logs.hbs
index 18fd3fdb..b0dd3de6 100644
--- a/assets/javascripts/discourse/templates/admin-wizards-logs.hbs
+++ b/assets/javascripts/discourse/templates/admin-wizards-logs.hbs
@@ -3,7 +3,7 @@
{{d-button
label="refresh"
- icon="refresh"
+ icon="sync"
action="refresh"
class="refresh"}}
diff --git a/assets/javascripts/discourse/templates/components/custom-field-input.hbs b/assets/javascripts/discourse/templates/components/custom-field-input.hbs
index 205b1644..43a97be8 100644
--- a/assets/javascripts/discourse/templates/components/custom-field-input.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-field-input.hbs
@@ -13,6 +13,11 @@
none="admin.wizard.custom_field.type.select"
onChange=(action (mut field.type))}}
+
+ {{input
+ value=field.name
+ placeholder=(i18n "admin.wizard.custom_field.name.select")}}
+ |
{{multi-select
value=field.serializers
@@ -20,11 +25,6 @@
none="admin.wizard.custom_field.serializers.select"
onChange=(action (mut field.serializers))}}
|
-
- {{input
- value=field.name
- placeholder=(i18n "admin.wizard.custom_field.name.select")}}
- |
{{#if loading}}
{{loading-spinner size="small"}}
@@ -51,13 +51,25 @@
{{else}}
| |
|
-
- {{#each field.serializers as |serializer|}}
-
- {{/each}}
- |
|
-
- {{d-button action="edit" icon="pencil-alt"}}
+ |
+ {{#if isExternal}}
+ —
+ {{else}}
+ {{#each field.serializers as |serializer|}}
+
+ {{/each}}
+ {{/if}}
|
+ {{#if isExternal}}
+
+
+ |
+ {{else}}
+
+ {{d-button action="edit" icon="pencil-alt"}}
+ |
+ {{/if}}
{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs
index f06e0d89..4c645cf7 100644
--- a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs
@@ -738,7 +738,7 @@
wizardActionSelection="value"
userFieldSelection="value"
keyPlaceholder="admin.wizard.action.custom_fields.key"
- context="action"
+ context=customFieldsContext
)}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs
index 2677c9dd..db68170a 100644
--- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs
@@ -127,6 +127,31 @@
checked=field.char_counter}}
+
+
+
+
+
+
+
+ {{textarea
+ name="field_placeholder"
+ class="medium"
+ value=field.placeholder}}
+
+
+{{/if}}
+
+{{#if isComposerPreview}}
+
+
+
+
+
+
+ {{textarea name="preview-template" value=field.preview_template}}
+
+
{{/if}}
{{#if isUpload}}
diff --git a/assets/javascripts/wizard-custom.js b/assets/javascripts/wizard-custom.js
index 5d18328f..8b30ad94 100644
--- a/assets/javascripts/wizard-custom.js
+++ b/assets/javascripts/wizard-custom.js
@@ -1,43 +1,4 @@
-//= require discourse/app/lib/autocomplete
-//= require discourse/app/lib/utilities
-//= require discourse/app/lib/offset-calculator
-//= require discourse/app/lib/lock-on
-//= require discourse/app/lib/text-direction
-//= require discourse/app/lib/to-markdown
-//= require discourse/app/lib/load-script
-//= require discourse/app/lib/url
-//= require discourse/app/lib/ajax
-//= require discourse/app/lib/ajax-error
-//= require discourse/app/lib/page-visible
-//= require discourse/app/lib/logout
-//= require discourse/app/lib/render-tag
-//= require discourse/app/lib/notification-levels
-//= require discourse/app/lib/computed
-//= require discourse/app/lib/user-search
-//= require discourse/app/lib/text
-//= require discourse/app/lib/formatter
-//= require discourse/app/lib/quote
-//= require discourse/app/lib/link-mentions
-//= require discourse/app/lib/link-hashtags
-//= require discourse/app/lib/category-hashtags
-//= require discourse/app/lib/tag-hashtags
-//= require discourse/app/lib/uploads
-//= require discourse/app/lib/category-tag-search
-//= require discourse/app/lib/intercept-click
-//= require discourse/app/lib/show-modal
-//= require discourse/app/lib/key-value-store
-//= require discourse/app/lib/settings
-//= require discourse/app/lib/user-presence
-//= require discourse/app/lib/hash
-//= require discourse/app/lib/bookmark
-//= require discourse/app/lib/put-cursor-at-end
-//= require discourse/app/lib/safari-hacks
-//= require discourse/app/lib/preload-store
-//= require discourse/app/lib/topic-fancy-title
-//= require discourse/app/lib/cookie
-//= require discourse/app/lib/public-js-versions
-//= require discourse/app/lib/load-oneboxes
-//= require discourse/app/lib/highlight-syntax
+//= require_tree_discourse discourse/app/lib
//= require discourse/app/mixins/singleton
//= require discourse/app/mixins/upload
@@ -46,35 +7,7 @@
//= require message-bus
-//= require discourse/app/models/login-method
-//= require discourse/app/models/permission-type
-//= require discourse/app/models/archetype
-//= require discourse/app/models/rest
-//= require discourse/app/models/site
-//= require discourse/app/models/category
-//= require discourse/app/models/session
-//= require discourse/app/models/post-action-type
-//= require discourse/app/models/trust-level
-//= require discourse/app/models/store
-//= require discourse/app/models/result-set
-//= require discourse/app/models/bookmark
-//= require discourse/app/models/user
-//= require discourse/app/models/user-stream
-//= require discourse/app/models/user-action
-//= require discourse/app/models/user-action-group
-//= require discourse/app/models/user-posts-stream
-//= require discourse/app/models/badge
-//= require discourse/app/models/badge-grouping
-//= require discourse/app/models/user-badge
-//= require discourse/app/models/topic
-//= require discourse/app/models/action-summary
-//= require discourse/app/models/user-action-stat
-//= require discourse/app/models/user-drafts-stream
-//= require discourse/app/models/user-draft
-//= require discourse/app/models/composer
-//= require discourse/app/models/draft
-//= require discourse/app/models/group
-//= require discourse/app/models/group-history
+//= require_tree_discourse discourse/app/models
//= require discourse/app/helpers/category-link
//= require discourse/app/helpers/user-avatar
diff --git a/assets/javascripts/wizard/components/wizard-date-input.js.es6 b/assets/javascripts/wizard/components/wizard-date-input.js.es6
index 93c7ed2d..bb11b655 100644
--- a/assets/javascripts/wizard/components/wizard-date-input.js.es6
+++ b/assets/javascripts/wizard/components/wizard-date-input.js.es6
@@ -1,3 +1,42 @@
import DateInput from "discourse/components/date-input";
+import loadScript from "discourse/lib/load-script";
+import discourseComputed from "discourse-common/utils/decorators";
+import I18n from "I18n";
+/* global Pikaday:true */
-export default DateInput.extend();
+export default DateInput.extend({
+ useNativePicker: false,
+
+ @discourseComputed()
+ placeholder() {
+ return this.format;
+ },
+
+ _loadPikadayPicker(container) {
+ return loadScript("/javascripts/pikaday.js").then(() => {
+ let defaultOptions = {
+ field: this.element.querySelector(".date-picker"),
+ container: container || this.element.querySelector(".picker-container"),
+ bound: container === null,
+ format: this.format,
+ firstDay: 1,
+ i18n: {
+ previousMonth: I18n.t("dates.previous_month"),
+ nextMonth: I18n.t("dates.next_month"),
+ months: moment.months(),
+ weekdays: moment.weekdays(),
+ weekdaysShort: moment.weekdaysShort(),
+ },
+ onSelect: (date) => this._handleSelection(date),
+ };
+
+ if (this.relativeDate) {
+ defaultOptions = Object.assign({}, defaultOptions, {
+ minDate: moment(this.relativeDate).toDate(),
+ });
+ }
+
+ return new Pikaday(Object.assign({}, defaultOptions, this._opts()));
+ });
+ },
+});
diff --git a/assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6 b/assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6
new file mode 100644
index 00000000..b49233f2
--- /dev/null
+++ b/assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6
@@ -0,0 +1,49 @@
+import Component from "@ember/component";
+import { loadOneboxes } from "discourse/lib/load-oneboxes";
+import { schedule } from "@ember/runloop";
+import discourseDebounce from "discourse-common/lib/debounce";
+import { resolveAllShortUrls } from "pretty-text/upload-short-url";
+import { ajax } from "discourse/lib/ajax";
+import { on } from "discourse-common/utils/decorators";
+
+export default Component.extend({
+ @on("init")
+ updatePreview() {
+ if (this.isDestroyed) {
+ return;
+ }
+
+ schedule("afterRender", () => {
+ if (this._state !== "inDOM" || !this.element) {
+ return;
+ }
+
+ const $preview = $(this.element);
+
+ if ($preview.length === 0) {
+ return;
+ }
+
+ this.previewUpdated($preview);
+ });
+ },
+
+ previewUpdated($preview) {
+ // Paint oneboxes
+ const paintFunc = () => {
+ loadOneboxes(
+ $preview[0],
+ ajax,
+ null,
+ null,
+ this.siteSettings.max_oneboxes_per_post,
+ true // refresh on every load
+ );
+ };
+
+ discourseDebounce(this, paintFunc, 450);
+
+ // Short upload urls need resolution
+ resolveAllShortUrls(ajax, this.siteSettings, $preview[0]);
+ },
+});
diff --git a/assets/javascripts/wizard/custom-wizard.js.es6 b/assets/javascripts/wizard/custom-wizard.js.es6
index 63a9ea10..8c0a473c 100644
--- a/assets/javascripts/wizard/custom-wizard.js.es6
+++ b/assets/javascripts/wizard/custom-wizard.js.es6
@@ -4,6 +4,10 @@ export default Ember.Application.extend({
rootElement: "#custom-wizard-main",
Resolver: buildResolver("wizard"),
+ customEvents: {
+ paste: "paste",
+ },
+
start() {
Object.keys(requirejs._eak_seen).forEach((key) => {
if (/\/pre\-initializers\//.test(key)) {
diff --git a/assets/javascripts/wizard/initializers/custom-wizard-field.js.es6 b/assets/javascripts/wizard/initializers/custom-wizard-field.js.es6
index ef45e949..0e9e60f4 100644
--- a/assets/javascripts/wizard/initializers/custom-wizard-field.js.es6
+++ b/assets/javascripts/wizard/initializers/custom-wizard-field.js.es6
@@ -15,7 +15,7 @@ export default {
);
const DEditor = requirejs("discourse/components/d-editor").default;
const { clipboardHelpers } = requirejs("discourse/lib/utilities");
- const { toMarkdown } = requirejs("discourse/lib/to-markdown");
+ const toMarkdown = requirejs("discourse/lib/to-markdown").default;
/*
* unit: custom_wizard:templates_and_builder
@@ -192,7 +192,7 @@ export default {
markdown = pre.match(/\S$/) ? ` ${markdown}` : markdown;
}
- this.appEvents.trigger("composer:insert-text", {
+ this.appEvents.trigger("wizard-editor:insert-text", {
fieldId: this.fieldId,
text: markdown,
});
diff --git a/assets/javascripts/wizard/initializers/custom-wizard.js.es6 b/assets/javascripts/wizard/initializers/custom-wizard.js.es6
index ab7c9146..ca396081 100644
--- a/assets/javascripts/wizard/initializers/custom-wizard.js.es6
+++ b/assets/javascripts/wizard/initializers/custom-wizard.js.es6
@@ -26,7 +26,9 @@ export default {
const setDefaultOwner = requirejs("discourse-common/lib/get-owner")
.setDefaultOwner;
const messageBus = requirejs("message-bus-client").default;
-
+ const getToken = requirejs("wizard/lib/ajax").getToken;
+ const setEnvironment = requirejs("discourse-common/config/environment")
+ .setEnvironment;
const container = app.__container__;
Discourse.Model = EmberObject.extend();
Discourse.__container__ = container;
@@ -89,6 +91,7 @@ export default {
const session = container.lookup("session:main");
const setupData = document.getElementById("data-discourse-setup").dataset;
session.set("highlightJsPath", setupData.highlightJsPath);
+ setEnvironment(setupData.environment);
Router.reopen({
rootURL: getUrl("/w/"),
@@ -107,5 +110,9 @@ export default {
},
model() {},
});
+
+ $.ajaxPrefilter(function (_, __, jqXHR) {
+ jqXHR.setRequestHeader("X-CSRF-Token", getToken());
+ });
},
};
diff --git a/assets/javascripts/wizard/lib/text-lite.js.es6 b/assets/javascripts/wizard/lib/text-lite.js.es6
index 4f9064a5..c93f6708 100644
--- a/assets/javascripts/wizard/lib/text-lite.js.es6
+++ b/assets/javascripts/wizard/lib/text-lite.js.es6
@@ -1,8 +1,17 @@
import loadScript from "./load-script";
-import { default as PrettyText } from "pretty-text/pretty-text";
+import { default as PrettyText, buildOptions } from "pretty-text/pretty-text";
import Handlebars from "handlebars";
+import getURL from "discourse-common/lib/get-url";
+import { getOwner } from "discourse-common/lib/get-owner";
export function cook(text, options) {
+ if (!options) {
+ options = buildOptions({
+ getURL: getURL,
+ siteSettings: getOwner(this).lookup("site-settings:main"),
+ });
+ }
+
return new Handlebars.SafeString(new PrettyText(options).cook(text));
}
diff --git a/assets/javascripts/wizard/lib/wizard-i18n.js.es6 b/assets/javascripts/wizard/lib/wizard-i18n.js.es6
index 17242e58..fdefab77 100644
--- a/assets/javascripts/wizard/lib/wizard-i18n.js.es6
+++ b/assets/javascripts/wizard/lib/wizard-i18n.js.es6
@@ -1,7 +1,10 @@
import I18n from "I18n";
const getThemeId = () => {
- let themeId = parseInt($("meta[name=discourse_theme_ids]")[0].content, 10);
+ let themeId = parseInt(
+ document.querySelector("meta[name=discourse_theme_id]").content,
+ 10
+ );
if (!isNaN(themeId)) {
return themeId.toString();
diff --git a/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs b/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs
index 1e7c27df..c4bf1c74 100644
--- a/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs
+++ b/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs
@@ -1,7 +1,7 @@
{{d-editor
tabindex=field.tabindex
value=composer.reply
- placeholder=replyPlaceholder
+ placeholderTranslated=replyPlaceholder
previewUpdated=(action "previewUpdated")
markdownOptions=markdownOptions
extraButtons=(action "extraButtons")
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-composer-preview.hbs b/assets/javascripts/wizard/templates/components/wizard-field-composer-preview.hbs
new file mode 100644
index 00000000..508cf31d
--- /dev/null
+++ b/assets/javascripts/wizard/templates/components/wizard-field-composer-preview.hbs
@@ -0,0 +1,5 @@
+
+
+ {{html-safe field.preview_template}}
+
+
diff --git a/assets/javascripts/wizard/templates/components/wizard-field-date.hbs b/assets/javascripts/wizard/templates/components/wizard-field-date.hbs
index 4ac6571b..ed4d14e3 100644
--- a/assets/javascripts/wizard/templates/components/wizard-field-date.hbs
+++ b/assets/javascripts/wizard/templates/components/wizard-field-date.hbs
@@ -2,4 +2,5 @@
date=date
onChange=(action "onChange")
tabindex=field.tabindex
+ format=field.format
}}
diff --git a/assets/stylesheets/common/wizard-admin.scss b/assets/stylesheets/common/wizard-admin.scss
index 3c4b78da..66cc6b43 100644
--- a/assets/stylesheets/common/wizard-admin.scss
+++ b/assets/stylesheets/common/wizard-admin.scss
@@ -15,7 +15,7 @@
}
.wizard-message {
- background-color: $primary-low;
+ background-color: var(--primary-low);
width: 100%;
padding: 10px;
box-sizing: border-box;
@@ -37,7 +37,7 @@
}
a + a {
- border-left: 1px solid $primary-medium;
+ border-left: 1px solid var(--primary-medium);
padding-left: 5px;
margin-left: 5px;
}
@@ -89,7 +89,7 @@
.wizard-settings-parent {
padding: 20px;
- border: 1px solid $primary-low;
+ border: 1px solid var(--primary-low);
}
.wizard-settings-group {
@@ -115,7 +115,7 @@
.wizard-custom-field {
background: transparent;
- background-color: dark-light-diff($primary, $secondary, 96%, -65%);
+ background-color: var(--primary-very-low);
padding: 20px;
}
@@ -182,7 +182,7 @@
a {
padding: 6px 12px;
font-size: 1rem;
- background-color: $primary-low;
+ background-color: var(--primary-low);
}
button {
@@ -256,6 +256,10 @@
width: 100%;
box-sizing: border-box;
margin-bottom: 0;
+
+ &.medium {
+ width: 40%;
+ }
}
input[type="number"] {
@@ -263,7 +267,7 @@
}
input[disabled] {
- background-color: $primary-low;
+ background-color: var(--primary-low);
cursor: not-allowed;
}
@@ -434,8 +438,8 @@
display: none;
margin: 0 0 10px 0;
padding: 10px;
- background-color: $secondary;
- border: 1px solid $primary-medium;
+ background-color: var(--secondary);
+ border: 1px solid var(--primary-medium);
max-width: 100%;
&.force-preview {
@@ -454,17 +458,17 @@
.btn {
margin-right: 10px;
- color: $primary;
+ color: var(--primary);
&:hover {
- color: $secondary;
+ color: var(--secondary);
}
}
.wizard-editor-gutter-popover {
position: absolute;
padding: 10px;
- background-color: $secondary;
+ background-color: var(--secondary);
box-shadow: shadow("card");
z-index: 200;
top: 40px;
@@ -502,7 +506,7 @@
width: 100px;
display: inline-block;
vertical-align: top;
- background-color: $primary-low;
+ background-color: var(--primary-low);
margin-right: 10px;
margin-left: 3px;
}
@@ -587,8 +591,8 @@
.add-mapper-input .btn,
.btn-after-time,
.wizard-editor-gutter .btn {
- background-color: $secondary;
- border: 1px solid $primary-medium;
+ background-color: var(--secondary);
+ border: 1px solid var(--primary-medium);
}
.admin-wizards-custom-fields {
@@ -667,6 +671,10 @@
margin-left: 5px !important;
}
}
+
+ td.external {
+ font-style: italic;
+ }
}
}
diff --git a/assets/stylesheets/common/wizard-api.scss b/assets/stylesheets/common/wizard-api.scss
index 4fdd0fc2..9d0ad261 100644
--- a/assets/stylesheets/common/wizard-api.scss
+++ b/assets/stylesheets/common/wizard-api.scss
@@ -48,7 +48,7 @@
.wizard-api-authentication {
display: flex;
- background-color: $primary-very-low;
+ background-color: var(--primary-very-low);
padding: 20px;
margin-bottom: 20px;
@@ -68,7 +68,7 @@
}
.status {
- border-left: 1px solid $primary;
+ border-left: 1px solid var(--primary);
margin-left: 20px;
padding-left: 20px;
width: 50%;
@@ -89,7 +89,7 @@
}
.wizard-api-endpoints {
- background-color: $primary-very-low;
+ background-color: var(--primary-very-low);
padding: 20px;
margin-bottom: 20px;
diff --git a/assets/stylesheets/common/wizard-manager.scss b/assets/stylesheets/common/wizard-manager.scss
index 135ddb8c..a3b30c98 100644
--- a/assets/stylesheets/common/wizard-manager.scss
+++ b/assets/stylesheets/common/wizard-manager.scss
@@ -16,13 +16,13 @@
#import-button:enabled,
#export-button:enabled {
- background-color: $tertiary;
- color: $secondary;
+ background-color: var(--tertiary);
+ color: var(--secondary);
}
#destroy-button:enabled {
- background-color: $danger;
- color: $secondary;
+ background-color: var(--danger);
+ color: var(--secondary);
}
}
@@ -32,13 +32,13 @@
.filename {
padding: 0 10px;
- border: 1px solid $primary;
+ border: 1px solid var(--primary);
display: inline-flex;
height: 28px;
line-height: 28px;
a {
- color: $primary;
+ color: var(--primary);
margin-right: 5px;
display: inline-flex;
align-items: center;
diff --git a/assets/stylesheets/common/wizard-mapper.scss b/assets/stylesheets/common/wizard-mapper.scss
index 69e9c88e..f56a2cab 100644
--- a/assets/stylesheets/common/wizard-mapper.scss
+++ b/assets/stylesheets/common/wizard-mapper.scss
@@ -22,7 +22,7 @@
width: min-content;
margin-bottom: 10px;
height: 20px;
- border: 2px solid $primary-low;
+ border: 2px solid var(--primary-low);
}
}
@@ -33,8 +33,8 @@
position: relative;
padding: 25px 7px 7px 7px;
margin-bottom: 10px;
- background: rgba($secondary, 0.5);
- border: 2px solid $primary-low;
+ background: rgba(var(--secondary-rgb), 0.5);
+ border: 2px solid var(--primary-low);
.d-icon {
text-align: center;
@@ -45,7 +45,7 @@
}
input[disabled] {
- background-color: $primary-low;
+ background-color: var(--primary-low);
border-color: #ddd;
}
@@ -62,10 +62,10 @@
align-items: center;
justify-content: center;
transform: translateY(-50%);
- background: $secondary;
+ background: var(--secondary);
border-radius: 50%;
font-size: 0.8em;
- border: 2px solid $primary-low;
+ border: 2px solid var(--primary-low);
}
&.association,
@@ -89,8 +89,8 @@
&.single {
height: 28px;
- background: $secondary;
- border: 1px solid $primary-medium;
+ background: var(--secondary);
+ border: 1px solid var(--primary-medium);
display: flex;
align-items: center;
justify-content: center;
@@ -126,7 +126,7 @@
}
.type-selector a {
- color: $primary;
+ color: var(--primary);
margin-right: 4px;
display: flex;
align-items: center;
@@ -150,11 +150,11 @@
box-shadow: shadow("dropdown");
position: absolute;
display: flex;
- background: $secondary;
+ background: var(--secondary);
z-index: 200;
padding: 5px 7px;
flex-direction: column;
- border: 1px solid $primary-low;
+ border: 1px solid var(--primary-low);
}
.value-list .remove-value-btn {
@@ -162,7 +162,7 @@
border: none;
.d-icon {
- color: $primary;
+ color: var(--primary);
}
}
diff --git a/assets/stylesheets/wizard/custom/badges.scss b/assets/stylesheets/wizard/custom/badges.scss
index ca9c0450..53a869c0 100644
--- a/assets/stylesheets/wizard/custom/badges.scss
+++ b/assets/stylesheets/wizard/custom/badges.scss
@@ -31,7 +31,7 @@
overflow: hidden;
text-overflow: ellipsis;
.extra-info-wrapper & {
- color: $header-primary;
+ color: var(--header_primary);
}
}
@@ -108,7 +108,7 @@
text-overflow: ellipsis;
.extra-info-wrapper & {
- color: $header-primary;
+ color: var(--header_primary);
}
}
diff --git a/assets/stylesheets/wizard/custom/composer.scss b/assets/stylesheets/wizard/custom/composer.scss
index 4f5ff442..24cad2a4 100644
--- a/assets/stylesheets/wizard/custom/composer.scss
+++ b/assets/stylesheets/wizard/custom/composer.scss
@@ -199,11 +199,11 @@
left: 0;
width: 100%;
height: 100%;
- background-color: rgba($primary, 0.8);
+ background-color: rgba(var(--primary-rgb), 0.8);
}
.wizard-composer-hyperlink-contents {
- background-color: $secondary;
+ background-color: var(--secondary);
padding: 20px;
h3 {
@@ -227,7 +227,7 @@
bottom: 1px;
right: 1px;
padding: 10px;
- background-color: $secondary;
+ background-color: var(--secondary);
}
.bottom-bar {
@@ -239,31 +239,43 @@
// Markdown table styles for wizard composer preview
-.cooked table,
-.d-editor-preview table {
- border-collapse: collapse;
+.cooked,
+.d-editor-preview {
+ a.mention {
+ display: inline-block; // https://bugzilla.mozilla.org/show_bug.cgi?id=1656119
+ font-weight: bold;
+ font-size: 0.93em;
+ color: var(--primary-high-or-secondary-low);
+ padding: 0 4px 1px;
+ background: var(--primary-low);
+ border-radius: 8px;
+ }
- tr {
- border-bottom: 1px solid var(--primary-low);
- &.highlighted {
- animation: background-fade-highlight 2.5s ease-out;
+ table {
+ border-collapse: collapse;
+
+ tr {
+ border-bottom: 1px solid var(--primary-low);
+ &.highlighted {
+ animation: background-fade-highlight 2.5s ease-out;
+ }
}
- }
- thead {
- th {
- text-align: left;
- padding: 0.5em;
- font-weight: bold;
- color: var(--primary);
+ thead {
+ th {
+ text-align: left;
+ padding: 0.5em;
+ font-weight: bold;
+ color: var(--primary);
+ }
}
- }
- tbody {
- border-top: 3px solid var(--primary-low);
- }
+ tbody {
+ border-top: 3px solid var(--primary-low);
+ }
- td {
- padding: 3px 3px 3px 0.5em;
+ td {
+ padding: 3px 3px 3px 0.5em;
+ }
}
}
diff --git a/assets/stylesheets/wizard/custom/field.scss b/assets/stylesheets/wizard/custom/field.scss
index e5a7f5e2..f2bb3aa0 100644
--- a/assets/stylesheets/wizard/custom/field.scss
+++ b/assets/stylesheets/wizard/custom/field.scss
@@ -162,4 +162,15 @@
.text-field input {
margin-bottom: 0;
}
+
+ .text-field,
+ .textarea-field,
+ .composer-field {
+ input[type="text"],
+ textarea {
+ &:focus::placeholder {
+ color: transparent;
+ }
+ }
+ }
}
diff --git a/assets/stylesheets/wizard/wizard_custom.scss b/assets/stylesheets/wizard/wizard_custom.scss
index aef346fc..6290ce6e 100644
--- a/assets/stylesheets/wizard/wizard_custom.scss
+++ b/assets/stylesheets/wizard/wizard_custom.scss
@@ -2,6 +2,7 @@
@import "common/foundation/variables";
@import "common/base/code_highlighting";
@import "common/base/modal";
+@import "common/base/onebox";
@import "common/components/buttons";
@import "common/d-editor";
@import "desktop/modal";
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 0c364853..ef826cab 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -75,7 +75,7 @@ en:
edit: "You're editing an action"
documentation: "Check out the action documentation"
custom_fields:
- create: "Create, edit or destroy a custom field record"
+ create: "View, create, edit and destroy custom fields"
saved: "Saved custom field"
error: "Failed to save: {{messages}}"
documentation: Check out the custom field documentation
@@ -173,7 +173,9 @@ en:
max_length_placeholder: "Maximum length in characters"
char_counter: "Character Counter"
char_counter_placeholder: "Display Character Counter"
+ field_placeholder: "Field Placeholder"
file_types: "File Types"
+ preview_template: "Preview Template"
limit: "Limit"
property: "Property"
prefill: "Prefill"
@@ -200,6 +202,7 @@ en:
text: "Text"
textarea: Textarea
composer: Composer
+ composer_preview: Composer Preview
text_only: Text Only
number: Number
checkbox: Checkbox
@@ -322,6 +325,9 @@ en:
custom_field:
nav_label: "Custom Fields"
add: "Add"
+ external:
+ label: "from another plugin"
+ title: "This custom field has been added by another plugin. You can use it in your wizards but you can't edit the field here."
name:
label: "Name"
select: "underscored_name"
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index 4bda825a..7e507450 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -1,8 +1,8 @@
en:
admin:
wizard:
- submissions:
- no_user: "deleted (id: %{id})"
+ submission:
+ no_user: "deleted (user_id: %{user_id})"
wizard:
custom_title: "Wizard"
diff --git a/controllers/custom_wizard/admin/admin.rb b/controllers/custom_wizard/admin/admin.rb
index 8d5e3cad..c99954d6 100644
--- a/controllers/custom_wizard/admin/admin.rb
+++ b/controllers/custom_wizard/admin/admin.rb
@@ -14,7 +14,7 @@ class CustomWizard::AdminController < ::Admin::AdminController
end
def custom_field_list
- serialize_data(CustomWizard::CustomField.list, CustomWizard::CustomFieldSerializer)
+ serialize_data(CustomWizard::CustomField.full_list, CustomWizard::CustomFieldSerializer)
end
def render_error(message)
diff --git a/controllers/custom_wizard/admin/submissions.rb b/controllers/custom_wizard/admin/submissions.rb
index 5467587e..4cb2a0e4 100644
--- a/controllers/custom_wizard/admin/submissions.rb
+++ b/controllers/custom_wizard/admin/submissions.rb
@@ -13,34 +13,20 @@ class CustomWizard::AdminSubmissionsController < CustomWizard::AdminController
def show
render_json_dump(
wizard: CustomWizard::BasicWizardSerializer.new(@wizard, root: false),
- submissions: build_submissions.as_json
+ submissions: ActiveModel::ArraySerializer.new(ordered_submissions, each_serializer: CustomWizard::SubmissionSerializer)
)
end
def download
- send_data build_submissions.to_json,
+ send_data ordered_submissions.to_json,
filename: "#{Discourse.current_hostname}-wizard-submissions-#{@wizard.name}.json",
content_type: "application/json",
disposition: "attachment"
end
- private
+ protected
- def build_submissions
- PluginStoreRow.where(plugin_name: "#{@wizard.id}_submissions")
- .order('id DESC')
- .map do |row|
- value = ::JSON.parse(row.value)
-
- if user = User.find_by(id: row.key)
- username = user.username
- else
- username = I18n.t('admin.wizard.submissions.no_user', id: row.key)
- end
-
- value.map do |v|
- { username: username }.merge!(v.except("redirect_to"))
- end
- end.flatten
+ def ordered_submissions
+ CustomWizard::Submission.list(@wizard, order_by: 'id')
end
end
diff --git a/controllers/custom_wizard/steps.rb b/controllers/custom_wizard/steps.rb
index 277b94b2..66ec2da9 100644
--- a/controllers/custom_wizard/steps.rb
+++ b/controllers/custom_wizard/steps.rb
@@ -8,7 +8,7 @@ class CustomWizard::StepsController < ::ApplicationController
update[:fields] = {}
if params[:fields]
- field_ids = @step_template['fields'].map { |f| f['id'] }
+ field_ids = @builder.wizard.field_ids
params[:fields].each do |k, v|
update[:fields][k] = v if field_ids.include? k
end
@@ -23,7 +23,7 @@ class CustomWizard::StepsController < ::ApplicationController
if updater.success?
wizard_id = update_params[:wizard_id]
builder = CustomWizard::Builder.new(wizard_id, current_user)
- @wizard = builder.build
+ @wizard = builder.build(force: true)
current_step = @wizard.find_step(update[:step_id])
current_submission = @wizard.current_submission
@@ -36,15 +36,19 @@ class CustomWizard::StepsController < ::ApplicationController
if current_step.final?
builder.template.actions.each do |action_template|
if action_template['run_after'] === 'wizard_completion'
- CustomWizard::Action.new(
+ action_result = CustomWizard::Action.new(
action: action_template,
wizard: @wizard,
- data: current_submission
+ submission: current_submission
).perform
+
+ if action_result.success?
+ current_submission = action_result.submission
+ end
end
end
- @wizard.save_submission(current_submission)
+ current_submission.save
if redirect = get_redirect
updater.result[:redirect_on_complete] = redirect
@@ -54,6 +58,8 @@ class CustomWizard::StepsController < ::ApplicationController
result[:final] = true
else
+ current_submission.save
+
result[:final] = false
result[:next_step_id] = current_step.next.id
end
@@ -101,9 +107,9 @@ class CustomWizard::StepsController < ::ApplicationController
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?
+ submission = @wizard.current_submission
+ return nil unless 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]
+ submission.route_to || submission.redirect_on_complete || submission.redirect_to
end
end
diff --git a/controllers/custom_wizard/wizard.rb b/controllers/custom_wizard/wizard.rb
index 37728ecb..e0cf669d 100644
--- a/controllers/custom_wizard/wizard.rb
+++ b/controllers/custom_wizard/wizard.rb
@@ -6,7 +6,7 @@ class CustomWizard::WizardController < ::ApplicationController
before_action :ensure_plugin_enabled
helper_method :wizard_page_title
- helper_method :wizard_theme_ids
+ helper_method :wizard_theme_id
helper_method :wizard_theme_lookup
helper_method :wizard_theme_translations_lookup
@@ -20,16 +20,16 @@ class CustomWizard::WizardController < ::ApplicationController
wizard ? (wizard.name || wizard.id) : I18n.t('wizard.custom_title')
end
- def wizard_theme_ids
- wizard ? [wizard.theme_id] : nil
+ def wizard_theme_id
+ wizard ? wizard.theme_id : nil
end
def wizard_theme_lookup(name)
- Theme.lookup_field(wizard_theme_ids, mobile_view? ? :mobile : :desktop, name)
+ Theme.lookup_field(wizard_theme_id, mobile_view? ? :mobile : :desktop, name)
end
def wizard_theme_translations_lookup
- Theme.lookup_field(wizard_theme_ids, :translations, I18n.locale)
+ Theme.lookup_field(wizard_theme_id, :translations, I18n.locale)
end
def index
@@ -61,10 +61,11 @@ class CustomWizard::WizardController < ::ApplicationController
result = success_json
user = current_user
- if user
+ if user && wizard.can_access?
submission = wizard.current_submission
- if submission && submission['redirect_to']
- result.merge!(redirect_to: submission['redirect_to'])
+
+ if submission.present? && submission.redirect_to
+ result.merge!(redirect_to: submission.redirect_to)
end
wizard.final_cleanup!
diff --git a/coverage/.last_run.json b/coverage/.last_run.json
index 3e7f27f6..2d4d0378 100644
--- a/coverage/.last_run.json
+++ b/coverage/.last_run.json
@@ -1,5 +1,5 @@
{
"result": {
- "line": 90.52
+ "line": 91.83
}
}
diff --git a/extensions/custom_field/extension.rb b/extensions/custom_field/extension.rb
new file mode 100644
index 00000000..876f56d4
--- /dev/null
+++ b/extensions/custom_field/extension.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+module CustomWizardCustomFieldExtension
+ def custom_field_types
+ @custom_field_types
+ end
+end
diff --git a/extensions/extra_locales_controller.rb b/extensions/extra_locales_controller.rb
index e7c5a02e..6242f7ca 100644
--- a/extensions/extra_locales_controller.rb
+++ b/extensions/extra_locales_controller.rb
@@ -4,7 +4,8 @@ module ExtraLocalesControllerCustomWizard
super || begin
return false unless bundle =~ /wizard/ && request.referer =~ /\/w\//
path = URI(request.referer).path
- wizard_id = path.split('/w/').last
+ wizard_path = path.split('/w/').last
+ wizard_id = wizard_path.split('/').first
CustomWizard::Template.exists?(wizard_id.underscore)
end
end
diff --git a/extensions/invites_controller.rb b/extensions/invites_controller.rb
index cafb15bd..5e0094da 100644
--- a/extensions/invites_controller.rb
+++ b/extensions/invites_controller.rb
@@ -5,7 +5,7 @@ module InvitesControllerCustomWizard
wizard_id = @user.custom_fields['redirect_to_wizard']
if wizard_id && url != '/'
- CustomWizard::Wizard.set_submission_redirect(@user, wizard_id, url)
+ CustomWizard::Wizard.set_wizard_redirect(@user, wizard_id, url)
url = "/w/#{wizard_id.dasherize}"
end
end
diff --git a/jobs/clear_after_time_wizard.rb b/jobs/clear_after_time_wizard.rb
deleted file mode 100644
index 37d997db..00000000
--- a/jobs/clear_after_time_wizard.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-module Jobs
- class ClearAfterTimeWizard < ::Jobs::Base
- sidekiq_options queue: 'critical'
-
- def execute(args)
- User.human_users.each do |u|
- if u.custom_fields['redirect_to_wizard'] == args[:wizard_id]
- u.custom_fields.delete('redirect_to_wizard')
- u.save_custom_fields(true)
- end
- end
- end
- end
-end
diff --git a/jobs/set_after_time_wizard.rb b/jobs/set_after_time_wizard.rb
index 3b2e9e11..7a5b86c6 100644
--- a/jobs/set_after_time_wizard.rb
+++ b/jobs/set_after_time_wizard.rb
@@ -9,7 +9,7 @@ module Jobs
user_ids = []
User.human_users.each do |user|
- if CustomWizard::Wizard.set_wizard_redirect(wizard.id, user)
+ if CustomWizard::Wizard.set_user_redirect(wizard.id, user)
user_ids.push(user.id)
end
end
diff --git a/lib/custom_wizard/action.rb b/lib/custom_wizard/action.rb
index d68e978b..1b5770d7 100644
--- a/lib/custom_wizard/action.rb
+++ b/lib/custom_wizard/action.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
class CustomWizard::Action
- attr_accessor :data,
+ attr_accessor :submission,
:action,
:user,
:guardian,
@@ -11,7 +11,7 @@ class CustomWizard::Action
@action = opts[:action]
@user = @wizard.user
@guardian = Guardian.new(@user)
- @data = opts[:data]
+ @submission = opts[:submission]
@log = []
@result = CustomWizard::ActionResult.new
end
@@ -26,14 +26,21 @@ class CustomWizard::Action
end
if @result.success? && @result.output.present?
- data[action['id']] = @result.output
+ @submission.fields[action['id']] = @result.output
end
save_log
+
+ @result.submission = @submission
+ @result
+ end
+
+ def mapper_data
+ @mapper_data ||= @submission&.fields_and_meta || {}
end
def mapper
- @mapper ||= CustomWizard::Mapper.new(user: user, data: data)
+ @mapper ||= CustomWizard::Mapper.new(user: user, data: mapper_data)
end
def create_topic
@@ -47,7 +54,7 @@ class CustomWizard::Action
messages = creator.errors.full_messages.join(" ")
log_error("failed to create", messages)
elsif action['skip_redirect'].blank?
- data['redirect_on_complete'] = post.topic.url
+ @submission.redirect_on_complete = post.topic.url
end
if creator.errors.blank?
@@ -65,7 +72,7 @@ class CustomWizard::Action
if action['required'].present?
required = CustomWizard::Mapper.new(
inputs: action['required'],
- data: data,
+ data: mapper_data,
user: user
).perform
@@ -79,7 +86,7 @@ class CustomWizard::Action
targets = CustomWizard::Mapper.new(
inputs: action['recipient'],
- data: data,
+ data: mapper_data,
user: user,
multiple: true
).perform
@@ -115,7 +122,7 @@ class CustomWizard::Action
messages = creator.errors.full_messages.join(" ")
log_error("failed to create message", messages)
elsif action['skip_redirect'].blank?
- data['redirect_on_complete'] = post.topic.url
+ @submission.redirect_on_complete = post.topic.url
end
if creator.errors.blank?
@@ -178,7 +185,7 @@ class CustomWizard::Action
def watch_categories
watched_categories = CustomWizard::Mapper.new(
inputs: action['categories'],
- data: data,
+ data: mapper_data,
user: user
).perform
@@ -193,7 +200,7 @@ class CustomWizard::Action
mute_remainder = CustomWizard::Mapper.new(
inputs: action['mute_remainder'],
- data: data,
+ data: mapper_data,
user: user
).perform
@@ -202,7 +209,7 @@ class CustomWizard::Action
if action['usernames']
mapped_users = CustomWizard::Mapper.new(
inputs: action['usernames'],
- data: data,
+ data: mapper_data,
user: user
).perform
@@ -284,7 +291,7 @@ class CustomWizard::Action
end
route_to = Discourse.base_uri + url
- @result.output = data['route_to'] = route_to
+ @result.output = @submission.route_to = route_to
log_success("route: #{route_to}")
else
@@ -295,7 +302,7 @@ class CustomWizard::Action
def add_to_group
group_map = CustomWizard::Mapper.new(
inputs: action['group'],
- data: data,
+ data: mapper_data,
user: user,
opts: {
multiple: true
@@ -345,18 +352,18 @@ class CustomWizard::Action
else
url = CustomWizard::Mapper.new(
inputs: url_input,
- data: data,
+ data: mapper_data,
user: user
).perform
end
if action['code']
- data[action['code']] = SecureRandom.hex(8)
- url += "{action['code']}=#{data[action['code']]}"
+ @submission.fields[action['code']] = SecureRandom.hex(8)
+ url += "{action['code']}=#{@submission.fields[action['code']]}"
end
route_to = UrlHelper.encode(url)
- data['route_to'] = route_to
+ @submission.route_to = route_to
log_info("route: #{route_to}")
end
@@ -416,7 +423,7 @@ class CustomWizard::Action
def action_category
output = CustomWizard::Mapper.new(
inputs: action['category'],
- data: data,
+ data: mapper_data,
user: user
).perform
@@ -434,7 +441,7 @@ class CustomWizard::Action
def action_tags
output = CustomWizard::Mapper.new(
inputs: action['tags'],
- data: data,
+ data: mapper_data,
user: user,
).perform
@@ -451,35 +458,54 @@ class CustomWizard::Action
if (custom_fields = action['custom_fields']).present?
field_map = CustomWizard::Mapper.new(
inputs: custom_fields,
- data: data,
+ data: mapper_data,
user: user
).perform
-
- registered_fields = CustomWizard::CustomField.cached_list
+ registered_fields = CustomWizard::CustomField.full_list
field_map.each do |field|
keyArr = field[:key].split('.')
value = field[:value]
if keyArr.length > 1
- klass = keyArr.first
- name = keyArr.last
+ klass = keyArr.first.to_sym
+ name = keyArr.second
+
+ if keyArr.length === 3 && name.include?("{}")
+ name = name.gsub("{}", "")
+ json_attr = keyArr.last
+ type = :json
+ end
else
name = keyArr.first
end
- registered = registered_fields.select { |f| f[:name] == name }
- if registered.first.present?
- klass = registered.first[:klass]
+ registered = registered_fields.select { |f| f.name == name }.first
+ if registered.present?
+ klass = registered.klass
+ type = registered.type
end
- if klass === 'topic'
+ next if type === :json && json_attr.blank?
+
+ if klass === :topic
params[:topic_opts] ||= {}
params[:topic_opts][:custom_fields] ||= {}
- params[:topic_opts][:custom_fields][name] = value
+
+ if type === :json
+ params[:topic_opts][:custom_fields][name] ||= {}
+ params[:topic_opts][:custom_fields][name][json_attr] = value
+ else
+ params[:topic_opts][:custom_fields][name] = value
+ end
else
- params[:custom_fields] ||= {}
- params[:custom_fields][name] = value
+ if type === :json
+ params[:custom_fields][name] ||= {}
+ params[:custom_fields][name][json_attr] = value
+ else
+ params[:custom_fields] ||= {}
+ params[:custom_fields][name] = value
+ end
end
end
end
@@ -494,7 +520,7 @@ class CustomWizard::Action
params[:title] = CustomWizard::Mapper.new(
inputs: action['title'],
- data: data,
+ data: mapper_data,
user: user
).perform
@@ -506,7 +532,7 @@ class CustomWizard::Action
wizard: true,
template: true
) :
- data[action['post']]
+ @submission.fields[action['post']]
params[:import_mode] = ActiveRecord::Type::Boolean.new.cast(action['suppress_notifications'])
@@ -529,7 +555,7 @@ class CustomWizard::Action
unless action[field].nil? || action[field] == ""
params[field.to_sym] = CustomWizard::Mapper.new(
inputs: action[field],
- data: data,
+ data: mapper_data,
user: user
).perform
end
@@ -568,7 +594,7 @@ class CustomWizard::Action
if input.present?
value = CustomWizard::Mapper.new(
inputs: input,
- data: data,
+ data: mapper_data,
user: user
).perform
@@ -598,7 +624,7 @@ class CustomWizard::Action
if action[attr].present?
value = CustomWizard::Mapper.new(
inputs: action[attr],
- data: data,
+ data: mapper_data,
user: user
).perform
diff --git a/lib/custom_wizard/action_result.rb b/lib/custom_wizard/action_result.rb
index 07c81284..53484ceb 100644
--- a/lib/custom_wizard/action_result.rb
+++ b/lib/custom_wizard/action_result.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
class CustomWizard::ActionResult
- attr_accessor :success, :handler, :output
+ attr_accessor :success, :handler, :output, :submission
def initialize
@success = false
diff --git a/lib/custom_wizard/builder.rb b/lib/custom_wizard/builder.rb
index 6f0cb960..af70a758 100644
--- a/lib/custom_wizard/builder.rb
+++ b/lib/custom_wizard/builder.rb
@@ -24,13 +24,13 @@ class CustomWizard::Builder
def mapper
CustomWizard::Mapper.new(
user: @wizard.user,
- data: @wizard.current_submission
+ data: @wizard.current_submission&.fields_and_meta
)
end
def build(build_opts = {}, params = {})
return nil if !SiteSetting.custom_wizard_enabled || !@wizard
- return @wizard if !@wizard.can_access?
+ return @wizard if !@wizard.can_access? && !build_opts[:force]
build_opts[:reset] = build_opts[:reset] || @wizard.restart_on_revisit
@@ -47,9 +47,8 @@ class CustomWizard::Builder
step.on_update do |updater|
@updater = updater
- @submission = (@wizard.current_submission || {})
- .merge(@updater.submission)
- .with_indifferent_access
+ @submission = @wizard.current_submission
+ @submission.fields.merge!(@updater.submission)
@updater.validate
next if @updater.errors.any?
@@ -60,11 +59,11 @@ class CustomWizard::Builder
run_step_actions
if @updater.errors.empty?
- if route_to = @submission['route_to']
- @submission.delete('route_to')
- end
+ route_to = @submission.route_to
+ @submission.route_to = nil
+ @submission.save
- @wizard.save_submission(@submission)
+ @wizard.update!
@updater.result[:redirect_on_next] = route_to if route_to
true
@@ -75,7 +74,7 @@ class CustomWizard::Builder
end
end
- @wizard.update_step_order!
+ @wizard.update!
@wizard
end
@@ -100,8 +99,8 @@ class CustomWizard::Builder
params[:value] = prefill_field(field_template, step_template)
- if !build_opts[:reset] && (submission = @wizard.current_submission)
- params[:value] = submission[field_template['id']] if submission[field_template['id']]
+ if !build_opts[:reset] && (submission = @wizard.current_submission).present?
+ params[:value] = submission.fields[field_template['id']] if submission.fields[field_template['id']]
end
if field_template['type'] === 'group' && params[:value].present?
@@ -144,7 +143,7 @@ class CustomWizard::Builder
content = CustomWizard::Mapper.new(
inputs: content_inputs,
user: @wizard.user,
- data: @wizard.current_submission,
+ data: @wizard.current_submission&.fields_and_meta,
opts: {
with_type: true
}
@@ -179,7 +178,7 @@ class CustomWizard::Builder
index = CustomWizard::Mapper.new(
inputs: field_template['index'],
user: @wizard.user,
- data: @wizard.current_submission
+ data: @wizard.current_submission&.fields_and_meta
).perform
params[:index] = index.to_i unless index.nil?
@@ -195,6 +194,28 @@ class CustomWizard::Builder
)
end
+ if field_template['preview_template'].present?
+ preview_template = mapper.interpolate(
+ field_template['preview_template'],
+ user: true,
+ value: true,
+ wizard: true,
+ template: true
+ )
+
+ params[:preview_template] = PrettyText.cook(preview_template)
+ end
+
+ if field_template['placeholder'].present?
+ params[:placeholder] = mapper.interpolate(
+ field_template['placeholder'],
+ user: true,
+ value: true,
+ wizard: true,
+ template: true
+ )
+ end
+
field = step.add_field(params)
end
@@ -203,7 +224,7 @@ class CustomWizard::Builder
CustomWizard::Mapper.new(
inputs: prefill,
user: @wizard.user,
- data: @wizard.current_submission
+ data: @wizard.current_submission&.fields_and_meta
).perform
end
end
@@ -213,7 +234,7 @@ class CustomWizard::Builder
result = CustomWizard::Mapper.new(
inputs: template['condition'],
user: @wizard.user,
- data: @wizard.current_submission,
+ data: @wizard.current_submission&.fields_and_meta,
opts: {
multiple: true
}
@@ -283,19 +304,20 @@ class CustomWizard::Builder
permitted_data = {}
submission_key = nil
params_key = nil
- submission = @wizard.current_submission || {}
+ 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
- if submission_key && params_key
- submission[submission_key] = params[params_key]
+ if submission_key && params_key && params[params_key].present?
+ submission.permitted_param_keys << submission_key.to_s
+ submission.fields[submission_key] = params[params_key]
end
end
- @wizard.save_submission(submission)
+ submission.save
end
def ensure_required_data(step, step_template)
@@ -304,13 +326,13 @@ class CustomWizard::Builder
pair['key'].present? && pair['value'].present?
end
- if pairs.any? && !@wizard.current_submission
+ if pairs.any? && !@wizard.current_submission.present?
step.permitted = false
break
end
pairs.each do |pair|
- pair['key'] = @wizard.current_submission[pair['key']]
+ pair['key'] = @wizard.current_submission.fields[pair['key']]
end
if !mapper.validate_pairs(pairs)
@@ -334,11 +356,15 @@ class CustomWizard::Builder
if @template.actions.present?
@template.actions.each do |action_template|
if action_template['run_after'] === updater.step.id
- CustomWizard::Action.new(
+ result = CustomWizard::Action.new(
action: action_template,
wizard: @wizard,
- data: @submission
+ submission: @submission
).perform
+
+ if result.success?
+ @submission = result.submission
+ end
end
end
end
diff --git a/lib/custom_wizard/custom_field.rb b/lib/custom_wizard/custom_field.rb
index e3f01a1a..9cc185ba 100644
--- a/lib/custom_wizard/custom_field.rb
+++ b/lib/custom_wizard/custom_field.rb
@@ -66,10 +66,12 @@ class ::CustomWizard::CustomField
value = send(attr)
i18n_key = "wizard.custom_field.error"
- if value.blank?
- if REQUIRED.include?(attr)
- add_error(I18n.t("#{i18n_key}.required_attribute", attr: attr))
- end
+ if value.blank? && REQUIRED.include?(attr)
+ add_error(I18n.t("#{i18n_key}.required_attribute", attr: attr))
+ break
+ end
+
+ if attr == 'serializers' && !value.is_a?(Array)
next
end
@@ -140,7 +142,7 @@ class ::CustomWizard::CustomField
fields.select do |cf|
if attr == :serializers
- cf[attr].include?(value)
+ cf[attr] && cf[attr].include?(value)
else
cf[attr] == value
end
@@ -215,4 +217,32 @@ class ::CustomWizard::CustomField
def self.enabled?
any?
end
+
+ def self.external_list
+ external = []
+
+ CLASSES.keys.each do |klass|
+ field_types = klass.to_s.classify.constantize.custom_field_types
+
+ if field_types.present?
+ field_types.each do |name, type|
+ unless list.any? { |field| field.name === name }
+ field = new(
+ 'external',
+ name: name,
+ klass: klass,
+ type: type
+ )
+ external.push(field)
+ end
+ end
+ end
+ end
+
+ external
+ end
+
+ def self.full_list
+ (list + external_list).uniq
+ end
end
diff --git a/lib/custom_wizard/exceptions/exceptions.rb b/lib/custom_wizard/exceptions/exceptions.rb
new file mode 100644
index 00000000..b5014d27
--- /dev/null
+++ b/lib/custom_wizard/exceptions/exceptions.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+module CustomWizard
+ class SprocketsFileNotFound < StandardError; end
+ class SprocketsEmptyPath < StandardError; end
+end
diff --git a/lib/custom_wizard/field.rb b/lib/custom_wizard/field.rb
index aacb163b..a7b8ef72 100644
--- a/lib/custom_wizard/field.rb
+++ b/lib/custom_wizard/field.rb
@@ -45,6 +45,8 @@ class CustomWizard::Field
content: [:serializable, :permitted, :mapped],
prefill: [:permitted, :mapped],
condition: [:permitted, :mapped],
+ preview_template: [:serializable, :permitted, :mapped],
+ placeholder: [:serializable, :permitted, :mapped],
}
end
@@ -108,20 +110,26 @@ class CustomWizard::Field
max_length: nil,
prefill: nil,
char_counter: nil,
- validations: nil
+ validations: nil,
+ placeholder: nil
},
textarea: {
min_length: nil,
max_length: nil,
prefill: nil,
- char_counter: nil
+ char_counter: nil,
+ placeholder: nil
},
composer: {
min_length: nil,
max_length: nil,
- char_counter: nil
+ char_counter: nil,
+ placeholder: nil
},
text_only: {},
+ composer_preview: {
+ preview_template: nil,
+ },
date: {
format: "YYYY-MM-DD"
},
diff --git a/lib/custom_wizard/mapper.rb b/lib/custom_wizard/mapper.rb
index c1187b0f..0c3543cf 100644
--- a/lib/custom_wizard/mapper.rb
+++ b/lib/custom_wizard/mapper.rb
@@ -5,20 +5,27 @@ class CustomWizard::Mapper
USER_FIELDS = [
'name',
'username',
- 'email',
'date_of_birth',
'title',
'locale',
'trust_level',
+ 'email'
+ ]
+
+ USER_OPTION_FIELDS = [
'email_level',
'email_messages_level',
'email_digests'
]
- PROFILE_FIELDS = ['location', 'website', 'bio_raw']
+ PROFILE_FIELDS = [
+ 'location',
+ 'website',
+ 'bio_raw'
+ ]
def self.user_fields
- USER_FIELDS + PROFILE_FIELDS
+ USER_FIELDS + USER_OPTION_FIELDS + PROFILE_FIELDS
end
OPERATORS = {
@@ -197,11 +204,15 @@ class CustomWizard::Mapper
def map_user_field(value)
if value.include?(User::USER_FIELD_PREFIX)
- UserCustomField.where(user_id: user.id, name: value).pluck(:value).first
+ user.custom_fields[value]
elsif PROFILE_FIELDS.include?(value)
- UserProfile.find_by(user_id: user.id).send(value)
+ user.user_profile.send(value)
elsif USER_FIELDS.include?(value)
- User.find(user.id).send(value)
+ user.send(value)
+ elsif USER_OPTION_FIELDS.include?(value)
+ user.user_option.send(value)
+ else
+ nil
end
end
@@ -217,19 +228,11 @@ class CustomWizard::Mapper
return string if string.blank?
if opts[:user]
- string.gsub!(/u\{(.*?)\}/) do |match|
- result = ''
- result = user.send($1) if USER_FIELDS.include?($1)
- result = user.user_profile.send($1) if PROFILE_FIELDS.include?($1)
- result
- end
+ string.gsub!(/u\{(.*?)\}/) { |match| map_user_field($1) || '' }
end
if opts[:wizard]
- string.gsub!(/w\{(.*?)\}/) do |match|
- value = recurse(data, [*$1.split('.')])
- value.present? ? value : ''
- end
+ string.gsub!(/w\{(.*?)\}/) { |match| recurse(data, [*$1.split('.')]) || '' }
end
if opts[:value]
diff --git a/lib/custom_wizard/submission.rb b/lib/custom_wizard/submission.rb
new file mode 100644
index 00000000..e50cb259
--- /dev/null
+++ b/lib/custom_wizard/submission.rb
@@ -0,0 +1,118 @@
+# frozen_string_literal: true
+class CustomWizard::Submission
+ include ActiveModel::SerializerSupport
+
+ KEY ||= "submissions"
+ META ||= %w(submitted_at route_to redirect_on_complete redirect_to)
+
+ attr_reader :id,
+ :user,
+ :user_id,
+ :wizard
+
+ attr_accessor :fields,
+ :permitted_param_keys
+
+ META.each do |attr|
+ class_eval { attr_accessor attr }
+ end
+
+ def initialize(wizard, data = {}, user_id = nil)
+ @wizard = wizard
+ @user_id = user_id
+
+ if user_id
+ @user = User.find_by(id: user_id)
+ else
+ @user = wizard.user
+ end
+
+ data = (data || {}).with_indifferent_access
+ @id = data['id'] || SecureRandom.hex(12)
+ non_field_keys = META + ['id']
+ @fields = data.except(*non_field_keys) || {}
+
+ META.each do |attr|
+ send("#{attr}=", data[attr]) if data[attr]
+ end
+
+ @permitted_param_keys = data['permitted_param_keys'] || []
+ end
+
+ def save
+ return nil unless wizard.save_submissions
+ validate
+
+ submission_list = self.class.list(wizard, user_id: user.id)
+ submissions = submission_list.select { |submission| submission.id != self.id }
+ submissions.push(self)
+
+ submission_data = submissions.map { |submission| data_to_save(submission) }
+ PluginStore.set("#{wizard.id}_#{KEY}", user.id, submission_data)
+ end
+
+ def validate
+ self.fields = fields.select { |key, value| validate_field_key(key) }
+ end
+
+ def validate_field_key(key)
+ wizard.field_ids.include?(key) ||
+ wizard.action_ids.include?(key) ||
+ permitted_param_keys.include?(key)
+ end
+
+ def fields_and_meta
+ result = fields
+
+ META.each do |attr|
+ if value = self.send(attr)
+ result[attr] = value
+ end
+ end
+
+ result
+ end
+
+ def present?
+ fields_and_meta.present?
+ end
+
+ def data_to_save(submission)
+ data = {
+ id: submission.id
+ }
+
+ data.merge!(submission.fields_and_meta)
+
+ if submission.permitted_param_keys.present?
+ data[:permitted_param_keys] = submission.permitted_param_keys
+ end
+
+ data
+ end
+
+ def self.get(wizard, user_id)
+ data = PluginStore.get("#{wizard.id}_#{KEY}", user_id).first
+ new(wizard, data, user_id)
+ end
+
+ def self.list(wizard, user_id: nil, order_by: nil)
+ params = { plugin_name: "#{wizard.id}_#{KEY}" }
+ params[:key] = user_id if user_id.present?
+
+ query = PluginStoreRow.where(params)
+ query = query.order("#{order_by} DESC") if order_by.present?
+
+ result = []
+
+ query.each do |record|
+ if (submission_data = ::JSON.parse(record.value)).any?
+ submission_data.each do |data|
+ result.push(new(wizard, data, record.key))
+ end
+ end
+ end
+
+ result
+ end
+end
diff --git a/lib/custom_wizard/template.rb b/lib/custom_wizard/template.rb
index a1c0aad0..8e944dca 100644
--- a/lib/custom_wizard/template.rb
+++ b/lib/custom_wizard/template.rb
@@ -49,18 +49,15 @@ class CustomWizard::Template
def self.remove(wizard_id)
wizard = CustomWizard::Wizard.create(wizard_id)
-
return false if !wizard
ActiveRecord::Base.transaction do
PluginStore.remove(CustomWizard::PLUGIN_NAME, wizard.id)
-
- if wizard.after_time
- Jobs.cancel_scheduled_job(:set_after_time_wizard)
- Jobs.enqueue(:clear_after_time_wizard, wizard_id: wizard_id)
- end
+ clear_user_wizard_redirect(wizard_id)
end
+ Jobs.cancel_scheduled_job(:set_after_time_wizard) if wizard.after_time
+
true
end
@@ -88,6 +85,10 @@ class CustomWizard::Template
end
end
+ def self.clear_user_wizard_redirect(wizard_id)
+ UserCustomField.where(name: 'redirect_to_wizard', value: wizard_id).destroy_all
+ end
+
private
def normalize_data
@@ -132,7 +133,7 @@ class CustomWizard::Template
Jobs.enqueue_at(enqueue_wizard_at, :set_after_time_wizard, wizard_id: wizard_id)
elsif old_data && old_data[:after_time]
Jobs.cancel_scheduled_job(:set_after_time_wizard, wizard_id: wizard_id)
- Jobs.enqueue(:clear_after_time_wizard, wizard_id: wizard_id)
+ self.class.clear_user_wizard_redirect(wizard_id)
end
end
end
diff --git a/lib/custom_wizard/validators/update.rb b/lib/custom_wizard/validators/update.rb
index d84b448a..c722a763 100644
--- a/lib/custom_wizard/validators/update.rb
+++ b/lib/custom_wizard/validators/update.rb
@@ -32,11 +32,11 @@ class ::CustomWizard::UpdateValidator
@updater.errors.add(field_id, I18n.t('wizard.field.required', label: label))
end
- if min_length && value.is_a?(String) && value.strip.length < min_length.to_i
+ if min_length.present? && value.is_a?(String) && value.strip.length < min_length.to_i
@updater.errors.add(field_id, I18n.t('wizard.field.too_short', label: label, min: min_length.to_i))
end
- if max_length && value.is_a?(String) && value.strip.length > max_length.to_i
+ if max_length.present? && value.is_a?(String) && value.strip.length > max_length.to_i
@updater.errors.add(field_id, I18n.t('wizard.field.too_long', label: label, max: max_length.to_i))
end
@@ -52,7 +52,7 @@ class ::CustomWizard::UpdateValidator
@updater.errors.add(field_id, I18n.t('wizard.field.invalid_file', label: label, types: file_types))
end
- if ['date', 'date_time'].include?(type) && value.present? && !validate_date(value)
+ if ['date', 'date_time'].include?(type) && value.present? && !validate_date(value, format)
@updater.errors.add(field_id, I18n.t('wizard.field.invalid_date'))
end
@@ -88,13 +88,8 @@ class ::CustomWizard::UpdateValidator
.include?(File.extname(value['original_filename'])[1..-1])
end
- def validate_date(value)
- begin
- Date.parse(value)
- true
- rescue ArgumentError
- false
- end
+ def validate_date(value, format)
+ v8.eval("moment('#{value}', '#{format}', true).isValid()")
end
def validate_time(value)
@@ -126,4 +121,12 @@ class ::CustomWizard::UpdateValidator
def standardise_boolean(value)
ActiveRecord::Type::Boolean.new.cast(value)
end
+
+ def v8
+ return @ctx if @ctx
+
+ @ctx = PrettyText.v8
+ PrettyText.ctx_load(@ctx, "#{Rails.root}/vendor/assets/javascripts/moment.js")
+ @ctx
+ end
end
diff --git a/lib/custom_wizard/wizard.rb b/lib/custom_wizard/wizard.rb
index 5967e013..d5ba940e 100644
--- a/lib/custom_wizard/wizard.rb
+++ b/lib/custom_wizard/wizard.rb
@@ -34,10 +34,15 @@ class CustomWizard::Wizard
:needs_groups,
:steps,
:step_ids,
+ :field_ids,
:first_step,
:start,
:actions,
- :user
+ :action_ids,
+ :user,
+ :submissions
+
+ attr_reader :all_step_ids
def initialize(attrs = {}, user = nil)
@user = user
@@ -66,11 +71,22 @@ class CustomWizard::Wizard
@first_step = nil
@steps = []
+
if attrs['steps'].present?
- @step_ids = attrs['steps'].map { |s| s['id'] }
+ @step_ids = @all_step_ids = attrs['steps'].map { |s| s['id'] }
+
+ @field_ids = []
+ attrs['steps'].each do |step|
+ if step['fields'].present?
+ step['fields'].each do |field|
+ @field_ids << field['id']
+ end
+ end
+ end
end
- @actions = []
+ @actions = attrs['actions'] || []
+ @action_ids = @actions.map { |a| a['id'] }
end
def cast_bool(val)
@@ -91,7 +107,19 @@ class CustomWizard::Wizard
step.index = (steps.size == 1 ? 0 : steps.size) if step.index.nil?
end
- def update_step_order!
+ def update!
+ update_step_order
+ update_step_ids
+ update_field_ids
+ update_action_ids
+
+ @submissions = nil
+ @current_submission = nil
+
+ true
+ end
+
+ def update_step_order
steps.sort_by!(&:index)
steps.each_with_index do |step, index|
@@ -110,7 +138,7 @@ class CustomWizard::Wizard
step.conditional_final_step = true
end
- if index === (step_ids.length - 1)
+ if index === (all_step_ids.length - 1)
step.last_step = true
end
@@ -125,7 +153,7 @@ class CustomWizard::Wizard
acting_user_id: user.id,
action: ::UserHistory.actions[:custom_wizard_step],
context: id,
- subject: step_ids
+ subject: all_step_ids
).order("created_at").last
last_completed_step.subject
@@ -229,30 +257,41 @@ class CustomWizard::Wizard
@groups ||= ::Site.new(Guardian.new(user)).groups
end
- def submissions
- return nil unless user.present?
- @submissions ||= Array.wrap(PluginStore.get("#{id}_submissions", user.id))
+ def update_step_ids
+ @step_ids = steps.map(&:id)
end
- def current_submission
- if submissions.present? && submissions.last.present? && !submissions.last.key?("submitted_at")
- submissions.last.with_indifferent_access
- else
- nil
+ def update_field_ids
+ @field_ids = steps.map { |step| step.fields.map { |field| field.id } }.flatten
+ end
+
+ def update_action_ids
+ @action_ids = []
+
+ @actions.each do |action|
+ if action['run_after'].blank? ||
+ action['run_after'] === 'wizard_completion' ||
+ step_ids.include?(action['run_after'])
+
+ @action_ids << action['id']
+ end
end
end
- def set_submissions(submissions)
- PluginStore.set("#{id}_submissions", user.id, Array.wrap(submissions))
- @submissions = nil
+ def submissions
+ return nil unless user.present?
+ @submissions ||= CustomWizard::Submission.list(self, user_id: user.id)
end
- def save_submission(submission)
- return nil unless save_submissions
-
- submissions.pop(1) if unfinished?
- submissions.push(submission)
- set_submissions(submissions)
+ def current_submission
+ @current_submission ||= begin
+ if submissions.present?
+ unsubmitted = submissions.select { |submission| !submission.submitted_at }
+ unsubmitted.present? ? unsubmitted.first : CustomWizard::Submission.new(self)
+ else
+ CustomWizard::Submission.new(self)
+ end
+ end
end
def final_cleanup!
@@ -261,18 +300,12 @@ class CustomWizard::Wizard
user.save_custom_fields(true)
end
- if submission = current_submission
- submission['submitted_at'] = Time.now.iso8601
- save_submission(submission)
+ if current_submission.present?
+ current_submission.submitted_at = Time.now.iso8601
+ current_submission.save
end
- end
- def self.submissions(wizard_id, user)
- new({ id: wizard_id }, user).submissions
- end
-
- def self.set_submissions(wizard_id, user, submissions)
- new({ id: wizard_id }, user).set_submissions(submissions)
+ update!
end
def self.create(wizard_id, user = nil)
@@ -327,11 +360,7 @@ class CustomWizard::Wizard
end
end
- def self.set_submission_redirect(user, wizard_id, url)
- set_submissions(wizard_id, user, [{ redirect_to: url }])
- end
-
- def self.set_wizard_redirect(wizard_id, user)
+ def self.set_user_redirect(wizard_id, user)
wizard = self.create(wizard_id, user)
if wizard.permitted?
@@ -341,4 +370,16 @@ class CustomWizard::Wizard
false
end
end
+
+ def self.set_wizard_redirect(user, wizard_id, url)
+ wizard = self.create(wizard_id, user)
+
+ if wizard.permitted?
+ submission = wizard.current_submission
+ submission.redirect_to = url
+ submission.save
+ else
+ false
+ end
+ end
end
diff --git a/package.json b/package.json
index c6692218..f06823c8 100644
--- a/package.json
+++ b/package.json
@@ -2,9 +2,9 @@
"name": "discourse-custom-wizard",
"version": "1.0.0",
"repository": "git@github.com:paviliondev/discourse-custom-wizard.git",
- "author": "Discourse",
- "license": "MIT",
+ "author": "Pavilion",
+ "license": "GPL V2",
"devDependencies": {
"eslint-config-discourse": "^1.1.8"
}
-}
+}
\ No newline at end of file
diff --git a/plugin.rb b/plugin.rb
index 43fe69d2..32abc584 100644
--- a/plugin.rb
+++ b/plugin.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
# name: discourse-custom-wizard
# about: Create custom wizards
-# version: 0.7.0
+# version: 0.8.0
# authors: Angus McLeod
# url: https://github.com/paviliondev/discourse-custom-wizard
# contact emails: angus@thepavilion.io
@@ -42,6 +42,22 @@ if respond_to?(:register_svg_icon)
register_svg_icon "save"
end
+class ::Sprockets::DirectiveProcessor
+ def process_require_tree_discourse_directive(path = ".")
+ raise CustomWizard::SprocketsEmptyPath, "path cannot be empty" if path == "."
+
+ discourse_asset_path = "#{Rails.root}/app/assets/javascripts/"
+ path = File.expand_path(path, discourse_asset_path)
+ stat = @environment.stat(path)
+
+ if stat && stat.directory?
+ require_paths(*@environment.stat_sorted_tree_with_dependencies(path))
+ else
+ raise CustomWizard::SprocketsFileNotFound, "#{path} not found in discourse core"
+ end
+ end
+end
+
after_initialize do
%w[
../lib/custom_wizard/engine.rb
@@ -56,7 +72,6 @@ after_initialize do
../controllers/custom_wizard/wizard.rb
../controllers/custom_wizard/steps.rb
../controllers/custom_wizard/realtime_validations.rb
- ../jobs/clear_after_time_wizard.rb
../jobs/refresh_api_access_token.rb
../jobs/set_after_time_wizard.rb
../lib/custom_wizard/validators/template.rb
@@ -74,6 +89,7 @@ after_initialize do
../lib/custom_wizard/log.rb
../lib/custom_wizard/step_updater.rb
../lib/custom_wizard/step.rb
+ ../lib/custom_wizard/submission.rb
../lib/custom_wizard/template.rb
../lib/custom_wizard/wizard.rb
../lib/custom_wizard/api/api.rb
@@ -81,6 +97,7 @@ after_initialize do
../lib/custom_wizard/api/endpoint.rb
../lib/custom_wizard/api/log_entry.rb
../lib/custom_wizard/liquid_extensions/first_non_empty.rb
+ ../lib/custom_wizard/exceptions/exceptions.rb
../serializers/custom_wizard/api/authorization_serializer.rb
../serializers/custom_wizard/api/basic_endpoint_serializer.rb
../serializers/custom_wizard/api/endpoint_serializer.rb
@@ -93,12 +110,14 @@ after_initialize do
../serializers/custom_wizard/wizard_step_serializer.rb
../serializers/custom_wizard/wizard_serializer.rb
../serializers/custom_wizard/log_serializer.rb
+ ../serializers/custom_wizard/submission_serializer.rb
../serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb
../extensions/extra_locales_controller.rb
../extensions/invites_controller.rb
../extensions/users_controller.rb
../extensions/custom_field/preloader.rb
../extensions/custom_field/serializer.rb
+ ../extensions/custom_field/extension.rb
].each do |path|
load File.expand_path(path, __FILE__)
end
@@ -117,7 +136,7 @@ after_initialize do
if !wizard.completed?
custom_redirect = true
- CustomWizard::Wizard.set_wizard_redirect(wizard.id, user)
+ CustomWizard::Wizard.set_user_redirect(wizard.id, user)
end
end
@@ -138,7 +157,7 @@ after_initialize do
on(:user_approved) do |user|
if wizard = CustomWizard::Wizard.after_signup(user)
- CustomWizard::Wizard.set_wizard_redirect(wizard.id, user)
+ CustomWizard::Wizard.set_user_redirect(wizard.id, user)
end
end
@@ -149,7 +168,7 @@ after_initialize do
if request.format === 'text/html' && !@excluded_routes.any? { |str| /#{str}/ =~ url } && wizard_id
if request.referer !~ /\/w\// && request.referer !~ /\/invites\//
- CustomWizard::Wizard.set_submission_redirect(current_user, wizard_id, request.referer)
+ CustomWizard::Wizard.set_wizard_redirect(current_user, wizard_id, request.referer)
end
if CustomWizard::Template.exists?(wizard_id)
redirect_to "/w/#{wizard_id.dasherize}"
@@ -191,18 +210,18 @@ after_initialize do
end
CustomWizard::CustomField::CLASSES.keys.each do |klass|
+ class_constant = klass.to_s.classify.constantize
+
add_model_callback(klass, :after_initialize) do
if CustomWizard::CustomField.enabled?
CustomWizard::CustomField.list_by(:klass, klass.to_s).each do |field|
- klass.to_s
- .classify
- .constantize
- .register_custom_field_type(field[:name], field[:type].to_sym)
+ class_constant.register_custom_field_type(field[:name], field[:type].to_sym)
end
end
end
- klass.to_s.classify.constantize.singleton_class.prepend CustomWizardCustomFieldPreloader
+ class_constant.singleton_class.prepend CustomWizardCustomFieldPreloader
+ class_constant.singleton_class.prepend CustomWizardCustomFieldExtension
end
CustomWizard::CustomField.serializers.each do |serializer_klass|
diff --git a/serializers/custom_wizard/submission_serializer.rb b/serializers/custom_wizard/submission_serializer.rb
new file mode 100644
index 00000000..52f0cb32
--- /dev/null
+++ b/serializers/custom_wizard/submission_serializer.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+class CustomWizard::SubmissionSerializer < ApplicationSerializer
+ attributes :id,
+ :username,
+ :fields,
+ :submitted_at,
+ :route_to,
+ :redirect_on_complete,
+ :redirect_to
+
+ def username
+ object.user.present? ?
+ object.user.username :
+ I18n.t('admin.wizard.submission.no_user', user_id: object.user_id)
+ end
+end
diff --git a/serializers/custom_wizard/wizard_field_serializer.rb b/serializers/custom_wizard/wizard_field_serializer.rb
index ffad36c8..f37476f0 100644
--- a/serializers/custom_wizard/wizard_field_serializer.rb
+++ b/serializers/custom_wizard/wizard_field_serializer.rb
@@ -55,6 +55,7 @@ class CustomWizard::FieldSerializer < ::ApplicationSerializer
end
def placeholder
+ return object.placeholder if object.placeholder.present?
I18n.t("#{object.key || i18n_key}.placeholder", default: '')
end
@@ -100,4 +101,8 @@ class CustomWizard::FieldSerializer < ::ApplicationSerializer
def char_counter
object.char_counter
end
+
+ def preview_template
+ object.preview_template
+ end
end
diff --git a/serializers/custom_wizard/wizard_serializer.rb b/serializers/custom_wizard/wizard_serializer.rb
index f858c195..7a162ba5 100644
--- a/serializers/custom_wizard/wizard_serializer.rb
+++ b/serializers/custom_wizard/wizard_serializer.rb
@@ -8,11 +8,11 @@ class CustomWizard::WizardSerializer < CustomWizard::BasicWizardSerializer
:completed,
:required,
:permitted,
- :uncategorized_category_id
+ :uncategorized_category_id,
+ :categories
has_many :steps, serializer: ::CustomWizard::StepSerializer, embed: :objects
has_one :user, serializer: ::BasicUserSerializer, embed: :objects
- has_many :categories, serializer: ::BasicCategorySerializer, embed: :objects
has_many :groups, serializer: ::BasicGroupSerializer, embed: :objects
def completed
@@ -56,4 +56,8 @@ class CustomWizard::WizardSerializer < CustomWizard::BasicWizardSerializer
def include_uncategorized_category_id?
object.needs_categories
end
+
+ def categories
+ object.categories.map { |c| c.to_h }
+ end
end
diff --git a/spec/components/custom_wizard/action_spec.rb b/spec/components/custom_wizard/action_spec.rb
index 28f2cab8..8b617c39 100644
--- a/spec/components/custom_wizard/action_spec.rb
+++ b/spec/components/custom_wizard/action_spec.rb
@@ -72,6 +72,42 @@ describe CustomWizard::Action do
raw: "topic body"
).exists?).to eq(false)
end
+
+ it "adds custom fields" do
+ wizard = CustomWizard::Builder.new(@template[:id], user).build
+ wizard.create_updater(wizard.steps.first.id,
+ step_1_field_1: "Topic Title",
+ step_1_field_2: "topic body"
+ ).update
+ wizard.create_updater(wizard.steps.second.id, {}).update
+ wizard.create_updater(wizard.steps.last.id,
+ step_3_field_3: category.id
+ ).update
+
+ topic = Topic.where(
+ title: "Topic Title",
+ category_id: category.id
+ ).first
+ topic_custom_field = TopicCustomField.where(
+ name: "topic_field",
+ value: "Topic custom field value",
+ topic_id: topic.id
+ )
+ topic_json_custom_field = TopicCustomField.where("
+ name = 'topic_json_field' AND
+ (value::json->>'key_1') = 'Key 1 value' AND
+ (value::json->>'key_2') = 'Key 2 value' AND
+ topic_id = #{topic.id}"
+ )
+ post_custom_field = PostCustomField.where(
+ name: "post_field",
+ value: "Post custom field value",
+ post_id: topic.first_post.id
+ )
+ expect(topic_custom_field.exists?).to eq(true)
+ expect(topic_json_custom_field.exists?).to eq(true)
+ expect(post_custom_field.exists?).to eq(true)
+ end
end
context 'sending a message' do
@@ -146,7 +182,7 @@ describe CustomWizard::Action do
updater = wizard.create_updater(wizard.steps[1].id, {})
updater.update
- category = Category.find_by(id: wizard.current_submission['action_8'])
+ category = Category.find_by(id: wizard.current_submission.fields['action_8'])
expect(updater.result[:redirect_on_next]).to eq(
"/new-topic?title=Title%20of%20the%20composer%20topic&body=I%20am%20interpolating%20some%20user%20fields%20Angus%20angus%20angus%40email.com&category_id=#{category.id}&tags=tag1"
@@ -158,11 +194,10 @@ describe CustomWizard::Action do
open_composer['post_template'] = "Body & more body & more body".dup
wizard = CustomWizard::Wizard.new(@template, user)
-
action = CustomWizard::Action.new(
wizard: wizard,
action: open_composer,
- data: {}
+ submission: wizard.current_submission
)
action.perform
@@ -179,20 +214,20 @@ describe CustomWizard::Action do
wizard = CustomWizard::Builder.new(@template[:id], user).build
wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
wizard.create_updater(wizard.steps[1].id, {}).update
- expect(Category.where(id: wizard.current_submission['action_8']).exists?).to eq(true)
+ expect(Category.where(id: wizard.current_submission.fields['action_8']).exists?).to eq(true)
end
it 'creates a group' do
wizard = CustomWizard::Builder.new(@template[:id], user).build
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)
+ expect(Group.where(name: wizard.current_submission.fields['action_9']).exists?).to eq(true)
end
it 'adds a user to 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
- group = Group.find_by(name: wizard.current_submission['action_9'])
+ group = Group.find_by(name: wizard.current_submission.fields['action_9'])
expect(group.users.first.username).to eq('angus')
end
@@ -201,7 +236,7 @@ describe CustomWizard::Action do
wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
wizard.create_updater(wizard.steps[1].id, {}).update
expect(CategoryUser.where(
- category_id: wizard.current_submission['action_8'],
+ category_id: wizard.current_submission.fields['action_8'],
user_id: user.id
).first.notification_level).to eq(2)
expect(CategoryUser.where(
diff --git a/spec/components/custom_wizard/builder_spec.rb b/spec/components/custom_wizard/builder_spec.rb
index d9d3524e..8e80d806 100644
--- a/spec/components/custom_wizard/builder_spec.rb
+++ b/spec/components/custom_wizard/builder_spec.rb
@@ -189,7 +189,10 @@ describe CustomWizard::Builder do
context "user has partially completed" do
before do
wizard = CustomWizard::Wizard.new(@template, user)
- wizard.set_submissions(step_1_field_1: 'I am a user submission')
+ data = {
+ step_1_field_1: 'I am a user submission'
+ }
+ CustomWizard::Submission.new(wizard, data).save
end
it 'returns saved submissions' do
@@ -253,9 +256,9 @@ describe CustomWizard::Builder do
end
it 'is permitted if required data is present' do
- CustomWizard::Wizard.set_submissions('super_mega_fun_wizard', user,
- required_data: "required_value"
- )
+ wizard = CustomWizard::Wizard.create('super_mega_fun_wizard', user)
+ CustomWizard::Submission.new(wizard, step_1_field_1: "required").save
+
expect(
CustomWizard::Builder.new(@template[:id], user).build
.steps.first
@@ -274,7 +277,7 @@ describe CustomWizard::Builder do
wizard = CustomWizard::Builder.new(@template[:id], user).build({},
param: 'param_value'
)
- expect(wizard.current_submission['saved_param']).to eq('param_value')
+ expect(wizard.current_submission.fields['saved_param']).to eq('param_value')
end
end
@@ -336,31 +339,27 @@ describe CustomWizard::Builder do
context 'on update' do
def perform_update(step_id, submission)
- wizard = CustomWizard::Builder.new(@template[:id], user).build
- updater = wizard.create_updater(step_id, submission)
+ updater = @wizard.create_updater(step_id, submission)
updater.update
updater
end
it 'saves submissions' do
+ @wizard = CustomWizard::Builder.new(@template[:id], user).build
perform_update('step_1', step_1_field_1: 'Text input')
- expect(
- CustomWizard::Wizard.submissions(@template[:id], user)
- .first['step_1_field_1']
- ).to eq('Text input')
+ expect(@wizard.current_submission.fields['step_1_field_1']).to eq('Text input')
end
context 'save submissions disabled' do
before do
@template[:save_submissions] = false
CustomWizard::Template.save(@template.as_json)
+ @wizard = CustomWizard::Builder.new(@template[:id], user).build
end
it "does not save submissions" do
perform_update('step_1', step_1_field_1: 'Text input')
- expect(
- CustomWizard::Wizard.submissions(@template[:id], user).first
- ).to eq(nil)
+ expect(@wizard.current_submission.present?).to eq(false)
end
end
end
diff --git a/spec/components/custom_wizard/custom_field_spec.rb b/spec/components/custom_wizard/custom_field_spec.rb
index 3c9f1706..b17e26c6 100644
--- a/spec/components/custom_wizard/custom_field_spec.rb
+++ b/spec/components/custom_wizard/custom_field_spec.rb
@@ -49,6 +49,40 @@ describe CustomWizard::CustomField do
end
context "validation" do
+ it "does not save without required attributes" do
+ invalid_field_json = custom_field_json['custom_fields'].first
+ invalid_field_json['klass'] = nil
+
+ custom_field = CustomWizard::CustomField.new(nil, invalid_field_json)
+ expect(custom_field.save).to eq(false)
+ expect(custom_field.valid?).to eq(false)
+ expect(custom_field.errors.full_messages.first).to eq(
+ I18n.t("wizard.custom_field.error.required_attribute", attr: "klass")
+ )
+ expect(
+ PluginStoreRow.where(
+ plugin_name: CustomWizard::CustomField::NAMESPACE,
+ key: custom_field.name
+ ).exists?
+ ).to eq(false)
+ end
+
+ it "does save without optional attributes" do
+ field_json = custom_field_json['custom_fields'].first
+ field_json['serializers'] = nil
+
+ custom_field = CustomWizard::CustomField.new(nil, field_json)
+ expect(custom_field.save).to eq(true)
+ expect(custom_field.valid?).to eq(true)
+ expect(
+ PluginStoreRow.where("
+ plugin_name = '#{CustomWizard::CustomField::NAMESPACE}' AND
+ key = '#{custom_field.name}' AND
+ value::jsonb = '#{field_json.except('name').to_json}'::jsonb
+ ",).exists?
+ ).to eq(true)
+ end
+
it "does not save with an unsupported class" do
invalid_field_json = custom_field_json['custom_fields'].first
invalid_field_json['klass'] = 'user'
@@ -178,6 +212,22 @@ describe CustomWizard::CustomField do
it "lists saved custom field records by attribute value" do
expect(CustomWizard::CustomField.list_by(:klass, 'topic').length).to eq(1)
end
+
+ it "lists saved custom field records by optional values" do
+ field_json = custom_field_json['custom_fields'].first
+ field_json['serializers'] = nil
+
+ custom_field = CustomWizard::CustomField.new(nil, field_json)
+ expect(CustomWizard::CustomField.list_by(:serializers, ['post']).length).to eq(0)
+ end
+
+ it "lists custom field records added by other plugins " do
+ expect(CustomWizard::CustomField.external_list.length).to eq(11)
+ end
+
+ it "lists all custom field records" do
+ expect(CustomWizard::CustomField.full_list.length).to eq(15)
+ end
end
it "is enabled if there are custom fields" do
diff --git a/spec/components/custom_wizard/mapper_spec.rb b/spec/components/custom_wizard/mapper_spec.rb
index 434f0001..ed66d7c1 100644
--- a/spec/components/custom_wizard/mapper_spec.rb
+++ b/spec/components/custom_wizard/mapper_spec.rb
@@ -229,28 +229,40 @@ describe CustomWizard::Mapper do
).perform).to eq("value 2")
end
- it "interpolates user fields" do
- expect(CustomWizard::Mapper.new(
- inputs: inputs['interpolate_user_field'],
- data: data,
- user: user1
- ).perform).to eq("Name: Angus")
- end
+ context "interpolates" do
+ it "user fields" do
+ expect(CustomWizard::Mapper.new(
+ inputs: inputs['interpolate_user_field'],
+ data: data,
+ user: user1
+ ).perform).to eq("Name: Angus")
+ end
- it "interpolates wizard fields" do
- expect(CustomWizard::Mapper.new(
- inputs: inputs['interpolate_wizard_field'],
- data: data,
- user: user1
- ).perform).to eq("Input 1: value 1")
- end
+ it "user emails" do
+ expect(CustomWizard::Mapper.new(
+ inputs: inputs['interpolate_user_email'],
+ data: data,
+ user: user1
+ ).perform).to eq("Email: angus@email.com")
+ end
- it "interpolates date" do
- expect(CustomWizard::Mapper.new(
- inputs: inputs['interpolate_timestamp'],
- data: data,
- user: user1
- ).perform).to eq("Time: #{Time.now.strftime("%B %-d, %Y")}")
+ it "user options" do
+ user1.user_option.update_columns(email_level: UserOption.email_level_types[:never])
+
+ expect(CustomWizard::Mapper.new(
+ inputs: inputs['interpolate_user_option'],
+ data: data,
+ user: user1
+ ).perform).to eq("Email Level: #{UserOption.email_level_types[:never]}")
+ end
+
+ it "date" do
+ expect(CustomWizard::Mapper.new(
+ inputs: inputs['interpolate_timestamp'],
+ data: data,
+ user: user1
+ ).perform).to eq("Time: #{Time.now.strftime("%B %-d, %Y")}")
+ end
end
it "handles greater than pairs" do
diff --git a/spec/components/custom_wizard/submission_spec.rb b/spec/components/custom_wizard/submission_spec.rb
new file mode 100644
index 00000000..a8c33861
--- /dev/null
+++ b/spec/components/custom_wizard/submission_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+require_relative '../../plugin_helper'
+
+describe CustomWizard::Submission do
+ fab!(:user) { Fabricate(:user) }
+ fab!(:user2) { Fabricate(:user) }
+
+ let(:template_json) {
+ JSON.parse(File.open(
+ "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
+ ).read)
+ }
+
+ before do
+ CustomWizard::Template.save(template_json, skip_jobs: true)
+
+ template_json_2 = template_json.dup
+ template_json_2["id"] = "super_mega_fun_wizard_2"
+ CustomWizard::Template.save(template_json_2, skip_jobs: true)
+
+ @wizard = CustomWizard::Wizard.create(template_json["id"], user)
+ @wizard2 = CustomWizard::Wizard.create(template_json["id"], user2)
+ @wizard3 = CustomWizard::Wizard.create(template_json_2["id"], user)
+
+ described_class.new(@wizard, step_1_field_1: "I am a user submission").save
+ described_class.new(@wizard2, step_1_field_1: "I am another user's submission").save
+ described_class.new(@wizard3, step_1_field_1: "I am a user submission on another wizard").save
+ end
+
+ it "saves a user's submission" do
+ expect(
+ described_class.get(@wizard, user.id).fields["step_1_field_1"]
+ ).to eq("I am a user submission")
+ end
+
+ it "list submissions by wizard" do
+ expect(described_class.list(@wizard).size).to eq(2)
+ end
+
+ it "list submissions by wizard and user" do
+ expect(described_class.list(@wizard, user_id: user.id).size).to eq(1)
+ end
+end
diff --git a/spec/components/custom_wizard/template_spec.rb b/spec/components/custom_wizard/template_spec.rb
index fb76e0c4..0e3dbdbe 100644
--- a/spec/components/custom_wizard/template_spec.rb
+++ b/spec/components/custom_wizard/template_spec.rb
@@ -41,6 +41,14 @@ describe CustomWizard::Template do
).to eq(nil)
end
+ it "removes user wizard redirects if template is removed" do
+ user.custom_fields['redirect_to_wizard'] = 'super_mega_fun_wizard'
+ user.save_custom_fields(true)
+
+ CustomWizard::Template.remove('super_mega_fun_wizard')
+ expect(user.reload.custom_fields['redirect_to_wizard']).to eq(nil)
+ end
+
it "checks for wizard template existence" do
expect(
CustomWizard::Template.exists?('super_mega_fun_wizard')
diff --git a/spec/components/custom_wizard/update_validator_spec.rb b/spec/components/custom_wizard/update_validator_spec.rb
index 81212b4b..e7658d8c 100644
--- a/spec/components/custom_wizard/update_validator_spec.rb
+++ b/spec/components/custom_wizard/update_validator_spec.rb
@@ -132,4 +132,44 @@ describe CustomWizard::UpdateValidator do
updater.errors.messages[:step_2_field_6].first
).to eq(nil)
end
+
+ it 'validates date fields' do
+ @template[:steps][1][:fields][0][:format] = "DD-MM-YYYY"
+ CustomWizard::Template.save(@template)
+
+ updater = perform_validation('step_2', step_2_field_1: '13-11-2021')
+ expect(
+ updater.errors.messages[:step_2_field_1].first
+ ).to eq(nil)
+ end
+
+ it 'doesn\'t validate date field if the format is not respected' do
+ @template[:steps][1][:fields][0][:format] = "MM-DD-YYYY"
+ CustomWizard::Template.save(@template)
+
+ updater = perform_validation('step_2', step_2_field_1: '13-11-2021')
+ expect(
+ updater.errors.messages[:step_2_field_1].first
+ ).to eq(I18n.t('wizard.field.invalid_date'))
+ end
+
+ it 'validates date time fields' do
+ @template[:steps][1][:fields][2][:format] = "DD-MM-YYYY HH:mm:ss"
+ CustomWizard::Template.save(@template)
+
+ updater = perform_validation('step_2', step_2_field_3: '13-11-2021 09:15:00')
+ expect(
+ updater.errors.messages[:step_2_field_3].first
+ ).to eq(nil)
+ end
+
+ it 'doesn\'t validate date time field if the format is not respected' do
+ @template[:steps][1][:fields][2][:format] = "MM-DD-YYYY HH:mm:ss"
+ CustomWizard::Template.save(@template)
+
+ updater = perform_validation('step_2', step_2_field_3: '13-11-2021 09:15')
+ expect(
+ updater.errors.messages[:step_2_field_3].first
+ ).to eq(I18n.t('wizard.field.invalid_date'))
+ end
end
diff --git a/spec/components/custom_wizard/wizard_spec.rb b/spec/components/custom_wizard/wizard_spec.rb
index aed44fe6..67905f5a 100644
--- a/spec/components/custom_wizard/wizard_spec.rb
+++ b/spec/components/custom_wizard/wizard_spec.rb
@@ -34,7 +34,7 @@ describe CustomWizard::Wizard do
template_json['steps'].each do |step_template|
@wizard.append_step(step_template['id'])
end
- @wizard.update_step_order!
+ @wizard.update!
end
def progress_step(step_id, acting_user: user, wizard: @wizard)
@@ -44,7 +44,7 @@ describe CustomWizard::Wizard do
context: wizard.id,
subject: step_id
)
- @wizard.update_step_order!
+ @wizard.update!
end
it "appends steps" do
@@ -72,7 +72,7 @@ describe CustomWizard::Wizard do
expect(@wizard.steps.first.index).to eq(2)
expect(@wizard.steps.last.index).to eq(0)
- @wizard.update_step_order!
+ @wizard.update!
expect(@wizard.steps.first.id).to eq("step_3")
expect(@wizard.steps.last.id).to eq("step_1")
@@ -173,6 +173,8 @@ describe CustomWizard::Wizard do
progress_step("step_2", acting_user: trusted_user)
progress_step("step_3", acting_user: trusted_user)
+ @permitted_template["multiple_submissions"] = true
+
expect(
CustomWizard::Wizard.new(@permitted_template, trusted_user).can_access?
).to eq(true)
@@ -197,19 +199,13 @@ describe CustomWizard::Wizard do
end
it "lists the site categories" do
+ Site.clear_cache
expect(@wizard.categories.length).to eq(1)
end
context "submissions" do
before do
- @wizard.set_submissions(step_1_field_1: 'I am a user submission')
- end
-
- it "sets the user's submission" do
- expect(
- PluginStore.get("#{template_json['id']}_submissions", user.id)
- .first['step_1_field_1']
- ).to eq('I am a user submission')
+ CustomWizard::Submission.new(@wizard, step_1_field_1: "I am a user submission").save
end
it "lists the user's submissions" do
@@ -217,20 +213,10 @@ describe CustomWizard::Wizard do
end
it "returns the user's current submission" do
- expect(@wizard.current_submission['step_1_field_1']).to eq('I am a user submission')
+ expect(@wizard.current_submission.fields["step_1_field_1"]).to eq("I am a user submission")
end
end
- it "provides class methods to set and list submissions" do
- CustomWizard::Wizard.set_submissions(template_json['id'], user,
- step_1_field_1: 'I am a user submission'
- )
- expect(
- CustomWizard::Wizard.submissions(template_json['id'], user)
- .first['step_1_field_1']
- ).to eq('I am a user submission')
- end
-
context "class methods" do
before do
CustomWizard::Template.save(@permitted_template, skip_jobs: true)
@@ -271,7 +257,7 @@ describe CustomWizard::Wizard do
it "sets wizard redirects if user is permitted" do
CustomWizard::Template.save(@permitted_template, skip_jobs: true)
- CustomWizard::Wizard.set_wizard_redirect('super_mega_fun_wizard', trusted_user)
+ CustomWizard::Wizard.set_user_redirect('super_mega_fun_wizard', trusted_user)
expect(
trusted_user.custom_fields['redirect_to_wizard']
).to eq("super_mega_fun_wizard")
@@ -279,7 +265,7 @@ describe CustomWizard::Wizard do
it "does not set a wizard redirect if user is not permitted" do
CustomWizard::Template.save(@permitted_template, skip_jobs: true)
- CustomWizard::Wizard.set_wizard_redirect('super_mega_fun_wizard', user)
+ CustomWizard::Wizard.set_user_redirect('super_mega_fun_wizard', user)
expect(
trusted_user.custom_fields['redirect_to_wizard']
).to eq(nil)
diff --git a/spec/extensions/extra_locales_controller_spec.rb b/spec/extensions/extra_locales_controller_spec.rb
index 91a4e8c3..a71e39c4 100644
--- a/spec/extensions/extra_locales_controller_spec.rb
+++ b/spec/extensions/extra_locales_controller_spec.rb
@@ -37,6 +37,13 @@ describe ExtraLocalesControllerCustomWizard, type: :request do
expect(response.status).to eq(200)
end
+ it "returns wizard locales when requested by user in a wizard step" do
+ sign_in(new_user)
+
+ get @locale_url, headers: { 'REFERER' => "/w/super-mega-fun-wizard/steps/step_1" }
+ expect(response.status).to eq(200)
+ end
+
it "return wizard locales if user cant access wizard" do
template[:permitted] = permitted["permitted"]
CustomWizard::Template.save(template.as_json)
diff --git a/spec/extensions/sprockets_directive_spec.rb b/spec/extensions/sprockets_directive_spec.rb
new file mode 100644
index 00000000..5a074040
--- /dev/null
+++ b/spec/extensions/sprockets_directive_spec.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require_relative '../plugin_helper'
+
+describe "Sprockets: require_tree_discourse directive" do
+ let(:discourse_asset_path) {
+ "#{Rails.root}/app/assets/javascripts/"
+ }
+ let(:fixture_asset_path) {
+ "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/sprockets/"
+ }
+ let(:test_file_contents) {
+ "console.log('hello')"
+ }
+ let(:resolved_file_contents) {
+ File.read(
+ "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/sprockets/resolved_js_file_contents.txt"
+ )
+ }
+
+ before do
+ @env ||= Sprockets::Environment.new
+ discourse_asset_path = "#{Rails.root}/app/assets/javascripts/"
+ fixture_asset_path = "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/sprockets/"
+ @env.append_path(discourse_asset_path)
+ @env.append_path(fixture_asset_path)
+ @env.cache = {}
+ end
+
+ def create_tmp_folder_and_run(path, file_contents, &block)
+ dir = File.dirname(path)
+ unless File.directory?(dir)
+ FileUtils.mkdir_p(dir)
+ end
+
+ File.new(path, 'w')
+ File.write(path, file_contents)
+ yield block if block_given?
+ FileUtils.rm_r(dir)
+ end
+
+ it "includes assets from the discourse core" do
+ create_tmp_folder_and_run("#{discourse_asset_path}/sptest/test.js", test_file_contents) do
+ expect(@env.find_asset("require_tree_discourse_test.js").to_s).to eq(resolved_file_contents)
+ end
+ end
+
+ it "throws ArgumentError if path is empty" do
+ expect { @env.find_asset("require_tree_discourse_empty.js") }.to raise_error(CustomWizard::SprocketsEmptyPath).with_message("path cannot be empty")
+ end
+
+ it "throws ArgumentError if path is non non-existent" do
+ expect { @env.find_asset("require_tree_discourse_non_existant.js") }.to raise_error(CustomWizard::SprocketsFileNotFound)
+ end
+end
diff --git a/spec/fixtures/mapper/inputs.json b/spec/fixtures/mapper/inputs.json
index f7d98903..443f186b 100644
--- a/spec/fixtures/mapper/inputs.json
+++ b/spec/fixtures/mapper/inputs.json
@@ -57,6 +57,22 @@
"output": "Name: u{name}"
}
],
+ "interpolate_user_email": [
+ {
+ "type": "assignment",
+ "output_type": "text",
+ "output_connector": "set",
+ "output": "Email: u{email}"
+ }
+ ],
+ "interpolate_user_option": [
+ {
+ "type": "assignment",
+ "output_type": "text",
+ "output_connector": "set",
+ "output": "Email Level: u{email_level}"
+ }
+ ],
"interpolate_wizard_field": [
{
"type": "assignment",
diff --git a/spec/fixtures/sprockets/require_tree_discourse_empty.js b/spec/fixtures/sprockets/require_tree_discourse_empty.js
new file mode 100644
index 00000000..df264ec5
--- /dev/null
+++ b/spec/fixtures/sprockets/require_tree_discourse_empty.js
@@ -0,0 +1 @@
+//= require_tree_discourse
\ No newline at end of file
diff --git a/spec/fixtures/sprockets/require_tree_discourse_non_existant.js b/spec/fixtures/sprockets/require_tree_discourse_non_existant.js
new file mode 100644
index 00000000..d9b2be76
--- /dev/null
+++ b/spec/fixtures/sprockets/require_tree_discourse_non_existant.js
@@ -0,0 +1 @@
+//= require_tree_discourse dummy_path
\ No newline at end of file
diff --git a/spec/fixtures/sprockets/require_tree_discourse_test.js b/spec/fixtures/sprockets/require_tree_discourse_test.js
new file mode 100644
index 00000000..a86aa0d7
--- /dev/null
+++ b/spec/fixtures/sprockets/require_tree_discourse_test.js
@@ -0,0 +1 @@
+//= require_tree_discourse sptest
\ No newline at end of file
diff --git a/spec/fixtures/sprockets/resolved_js_file_contents.txt b/spec/fixtures/sprockets/resolved_js_file_contents.txt
new file mode 100644
index 00000000..53e2cfa2
--- /dev/null
+++ b/spec/fixtures/sprockets/resolved_js_file_contents.txt
@@ -0,0 +1,3 @@
+eval("define(\"sptest/test\", [], function () {\n \"use strict\";\n\n console.log('hello');\n});" + "\n//# sourceURL=sptest/test");
+;
+eval("" + "\n//# sourceURL=require_tree_discourse_test");
diff --git a/spec/fixtures/step/required_data.json b/spec/fixtures/step/required_data.json
index 9f65d516..7ff8bcaf 100644
--- a/spec/fixtures/step/required_data.json
+++ b/spec/fixtures/step/required_data.json
@@ -6,9 +6,9 @@
"pairs": [
{
"index": 0,
- "key": "required_data",
+ "key": "step_1_field_1",
"key_type": "text",
- "value": "required_value",
+ "value": "required",
"value_type": "text",
"connector": "equal"
}
diff --git a/spec/fixtures/wizard.json b/spec/fixtures/wizard.json
index c21d445c..a505c0d3 100644
--- a/spec/fixtures/wizard.json
+++ b/spec/fixtures/wizard.json
@@ -3,7 +3,6 @@
"name": "Super Mega Fun Wizard",
"background": "#333333",
"save_submissions": true,
- "multiple_submissions": true,
"after_signup": false,
"prompt_completion": false,
"theme_id": 2,
@@ -391,10 +390,34 @@
"pairs": [
{
"index": 0,
- "key": "custom_field_1",
+ "key": "post_field",
"key_type": "text",
- "value": "title",
- "value_type": "user_field",
+ "value": "Post custom field value",
+ "value_type": "text",
+ "connector": "association"
+ },
+ {
+ "index": 1,
+ "key": "topic.topic_field",
+ "key_type": "text",
+ "value": "Topic custom field value",
+ "value_type": "text",
+ "connector": "association"
+ },
+ {
+ "index": 2,
+ "key": "topic.topic_json_field{}.key_1",
+ "key_type": "text",
+ "value": "Key 1 value",
+ "value_type": "text",
+ "connector": "association"
+ },
+ {
+ "index": 3,
+ "key": "topic.topic_json_field{}.key_2",
+ "key_type": "text",
+ "value": "Key 2 value",
+ "value_type": "text",
"connector": "association"
}
]
diff --git a/spec/jobs/clear_after_time_wizard_spec.rb b/spec/jobs/clear_after_time_wizard_spec.rb
deleted file mode 100644
index 935036a3..00000000
--- a/spec/jobs/clear_after_time_wizard_spec.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-# frozen_string_literal: true
-
-require_relative '../plugin_helper'
-
-describe Jobs::ClearAfterTimeWizard do
- fab!(:user1) { Fabricate(:user) }
- fab!(:user2) { Fabricate(:user) }
- fab!(:user3) { Fabricate(:user) }
-
- let(:template) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read).with_indifferent_access
- }
-
- it "clears wizard redirect for all users " do
- after_time_template = template.dup
- after_time_template["after_time"] = true
- after_time_template["after_time_scheduled"] = (Time.now + 3.hours).iso8601
-
- CustomWizard::Template.save(after_time_template)
-
- Jobs::SetAfterTimeWizard.new.execute(wizard_id: 'super_mega_fun_wizard')
-
- expect(
- UserCustomField.where(
- name: 'redirect_to_wizard',
- value: 'super_mega_fun_wizard'
- ).length
- ).to eq(3)
-
- described_class.new.execute(wizard_id: 'super_mega_fun_wizard')
-
- expect(
- UserCustomField.where("
- name = 'redirect_to_wizard' AND
- value = 'super_mega_fun_wizard'
- ").exists?
- ).to eq(false)
- end
-end
diff --git a/spec/plugin_helper.rb b/spec/plugin_helper.rb
index 6680874f..93f33a81 100644
--- a/spec/plugin_helper.rb
+++ b/spec/plugin_helper.rb
@@ -6,7 +6,7 @@ if ENV['SIMPLECOV']
SimpleCov.start do
root "plugins/discourse-custom-wizard"
track_files "plugins/discourse-custom-wizard/**/*.rb"
- add_filter { |src| src.filename =~ /(\/spec\/|\/db\/|plugin\.rb|api)/ }
+ add_filter { |src| src.filename =~ /(\/spec\/|\/db\/|plugin\.rb|api|gems)/ }
SimpleCov.minimum_coverage 80
end
end
diff --git a/spec/requests/custom_wizard/admin/custom_fields_controller_spec.rb b/spec/requests/custom_wizard/admin/custom_fields_controller_spec.rb
index e006e65a..8c1a8550 100644
--- a/spec/requests/custom_wizard/admin/custom_fields_controller_spec.rb
+++ b/spec/requests/custom_wizard/admin/custom_fields_controller_spec.rb
@@ -17,9 +17,9 @@ describe CustomWizard::AdminCustomFieldsController do
sign_in(admin_user)
end
- it "returns the list of custom fields" do
+ it "returns the full list of custom fields" do
get "/admin/wizards/custom-fields.json"
- expect(response.parsed_body.length).to eq(4)
+ expect(response.parsed_body.length).to eq(15)
end
it "saves custom fields" do
diff --git a/spec/requests/custom_wizard/admin/submissions_controller_spec.rb b/spec/requests/custom_wizard/admin/submissions_controller_spec.rb
index f63eead5..36296e95 100644
--- a/spec/requests/custom_wizard/admin/submissions_controller_spec.rb
+++ b/spec/requests/custom_wizard/admin/submissions_controller_spec.rb
@@ -5,6 +5,7 @@ describe CustomWizard::AdminSubmissionsController do
fab!(:admin_user) { Fabricate(:user, admin: true) }
fab!(:user1) { Fabricate(:user) }
fab!(:user2) { Fabricate(:user) }
+ fab!(:user3) { Fabricate(:user) }
let(:template) {
JSON.parse(File.open(
@@ -12,35 +13,40 @@ describe CustomWizard::AdminSubmissionsController do
).read)
}
+ let(:template_2) {
+ temp = template.dup
+ temp["id"] = "super_mega_fun_wizard_2"
+ temp
+ }
+
before do
CustomWizard::Template.save(template, skip_jobs: true)
- CustomWizard::Wizard.set_submissions(template['id'], user1,
- step_1_field_1: "I am a user1's submission"
- )
- CustomWizard::Wizard.set_submissions(template['id'], user2,
- step_1_field_1: "I am a user2's submission"
- )
+ CustomWizard::Template.save(template_2, skip_jobs: true)
+
+ wizard1 = CustomWizard::Wizard.create(template["id"], user1)
+ wizard2 = CustomWizard::Wizard.create(template["id"], user2)
+ wizard3 = CustomWizard::Wizard.create(template_2["id"], user3)
+
+ CustomWizard::Submission.new(wizard1, step_1_field_1: "I am a user1's submission").save
+ CustomWizard::Submission.new(wizard2, step_1_field_1: "I am a user2's submission").save
+ CustomWizard::Submission.new(wizard3, step_1_field_1: "I am a user3's submission").save
+
sign_in(admin_user)
end
- it "returns a basic list of wizards" do
+ it "returns a list of wizards" do
get "/admin/wizards/submissions.json"
- expect(response.parsed_body.length).to eq(1)
+ expect(response.parsed_body.length).to eq(2)
expect(response.parsed_body.first['id']).to eq(template['id'])
end
- it "returns the all user's submissions for a wizard" do
+ it "returns users' submissions for a wizard" do
get "/admin/wizards/submissions/#{template['id']}.json"
expect(response.parsed_body['submissions'].length).to eq(2)
end
- it "returns the all user's submissions for a wizard" do
- get "/admin/wizards/submissions/#{template['id']}.json"
- expect(response.parsed_body['submissions'].length).to eq(2)
- end
-
- it "downloads all user submissions" do
- get "/admin/wizards/submissions/#{template['id']}/download"
- expect(response.parsed_body.length).to eq(2)
+ it "downloads submissions" do
+ get "/admin/wizards/submissions/#{template_2['id']}/download"
+ expect(response.parsed_body.length).to eq(1)
end
end
diff --git a/spec/requests/custom_wizard/application_controller_spec.rb b/spec/requests/custom_wizard/application_controller_spec.rb
index f79db877..0835f246 100644
--- a/spec/requests/custom_wizard/application_controller_spec.rb
+++ b/spec/requests/custom_wizard/application_controller_spec.rb
@@ -39,10 +39,16 @@ describe ApplicationController do
it "saves original destination of user" do
get '/', headers: { 'REFERER' => "/t/2" }
expect(
- CustomWizard::Wizard.submissions(@template['id'], user)
- .first['redirect_to']
+ CustomWizard::Wizard.create(@template['id'], user).submissions
+ .first.redirect_to
).to eq("/t/2")
end
+
+ it "does not redirect if wizard does not exist" do
+ CustomWizard::Template.remove('super_mega_fun_wizard')
+ get "/"
+ expect(response.status).to eq(200)
+ end
end
context "who is not required to complete wizard" do
diff --git a/spec/requests/custom_wizard/steps_controller_spec.rb b/spec/requests/custom_wizard/steps_controller_spec.rb
index c58f13a2..5da75d8d 100644
--- a/spec/requests/custom_wizard/steps_controller_spec.rb
+++ b/spec/requests/custom_wizard/steps_controller_spec.rb
@@ -68,7 +68,7 @@ describe CustomWizard::StepsController do
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")
+ expect(wizard.current_submission.fields['step_1_field_1']).to eq("Text input")
end
context "raises an error" do
@@ -175,8 +175,11 @@ describe CustomWizard::StepsController do
wizard_id = response.parsed_body['wizard']['id']
wizard = CustomWizard::Wizard.create(wizard_id, user)
- group_name = wizard.submissions.last['action_9']
+
+ group_name = wizard.submissions.first.fields['action_9']
group = Group.find_by(name: group_name)
+
+ expect(group.present?).to eq(true)
expect(group.full_name).to eq("My cool group")
end
@@ -249,4 +252,33 @@ describe CustomWizard::StepsController do
expect(response.status).to eq(200)
expect(response.parsed_body['final']).to eq(true)
end
+
+ it "excludes the non-included conditional fields from the submissions" do
+ new_template = wizard_template.dup
+ new_template['steps'][1]['fields'][0]['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"
+ }
+ }
+
+ put '/w/super-mega-fun-wizard/steps/step_2.json', params: {
+ fields: {
+ step_2_field_1: "1995-04-23"
+ }
+ }
+
+ put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
+ fields: {
+ step_1_field_1: "Condition will not pass"
+ }
+ }
+
+ wizard_id = response.parsed_body['wizard']['id']
+ wizard = CustomWizard::Wizard.create(wizard_id, user)
+ submission = wizard.current_submission
+ expect(submission.fields.keys).not_to include("step_2_field_1")
+ end
end
diff --git a/spec/requests/custom_wizard/wizard_controller_spec.rb b/spec/requests/custom_wizard/wizard_controller_spec.rb
index 3e7ddd3d..f2000bda 100644
--- a/spec/requests/custom_wizard/wizard_controller_spec.rb
+++ b/spec/requests/custom_wizard/wizard_controller_spec.rb
@@ -11,6 +11,14 @@ describe CustomWizard::WizardController do
)
}
+ let(:permitted_json) {
+ JSON.parse(
+ File.open(
+ "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
+ ).read
+ )
+ }
+
before do
CustomWizard::Template.save(
JSON.parse(File.open(
@@ -47,6 +55,14 @@ describe CustomWizard::WizardController do
expect(response.status).to eq(200)
end
+ it 'lets user skip if user cant access wizard' do
+ @template["permitted"] = permitted_json["permitted"]
+ CustomWizard::Template.save(@template, skip_jobs: true)
+
+ put '/w/super-mega-fun-wizard/skip.json'
+ expect(response.status).to eq(200)
+ end
+
it 'returns a no skip message if user is not allowed to skip' do
@template['required'] = 'true'
CustomWizard::Template.save(@template)
@@ -55,9 +71,8 @@ describe CustomWizard::WizardController do
end
it 'skip response contains a redirect_to if in users submissions' do
- CustomWizard::Wizard.set_submissions(@template['id'], user,
- redirect_to: '/t/2'
- )
+ @wizard = CustomWizard::Wizard.create(@template["id"], user)
+ CustomWizard::Submission.new(@wizard, redirect_to: "/t/2").save
put '/w/super-mega-fun-wizard/skip.json'
expect(response.parsed_body['redirect_to']).to eq('/t/2')
end
diff --git a/views/layouts/wizard.html.erb b/views/layouts/wizard.html.erb
index efa09734..16d119b7 100644
--- a/views/layouts/wizard.html.erb
+++ b/views/layouts/wizard.html.erb
@@ -4,8 +4,8 @@
<%= discourse_stylesheet_link_tag :wizard, theme_id: nil %>
<%= discourse_stylesheet_link_tag :wizard_custom %>
- <%- if wizard_theme_ids.present? %>
- <%= discourse_stylesheet_link_tag (mobile_view? ? :mobile_theme : :desktop_theme), theme_ids: wizard_theme_ids %>
+ <%- if wizard_theme_id.present? %>
+ <%= discourse_stylesheet_link_tag (mobile_view? ? :mobile_theme : :desktop_theme), theme_id: wizard_theme_id %>
<%- end %>
<%= preload_script "locales/#{I18n.locale}" %>
@@ -29,7 +29,7 @@
<%= tag.meta id: 'data-discourse-setup', data: client_side_setup_data %>
- ">
+
<%= render partial: "layouts/head" %>