diff --git a/.github/workflows/discourse-plugin.yml b/.github/workflows/discourse-plugin.yml
index f5cf62e5..13850e3e 100644
--- a/.github/workflows/discourse-plugin.yml
+++ b/.github/workflows/discourse-plugin.yml
@@ -5,6 +5,8 @@ on:
branches:
- main
pull_request:
+ schedule:
+ - cron: "0 0 * * *"
jobs:
ci:
diff --git a/app/serializers/custom_wizard/submission_serializer.rb b/app/serializers/custom_wizard/submission_serializer.rb
index 48892c21..ed9ad411 100644
--- a/app/serializers/custom_wizard/submission_serializer.rb
+++ b/app/serializers/custom_wizard/submission_serializer.rb
@@ -10,7 +10,7 @@ class CustomWizard::SubmissionSerializer < ApplicationSerializer
end
def user
- ::BasicUserSerializer.new(object.wizard.user).as_json
+ ::BasicUserSerializer.new(object.wizard.user, root: false).as_json
end
def fields
diff --git a/assets/javascripts/discourse/components/custom-wizard-composer-editor.js.es6 b/assets/javascripts/discourse/components/custom-wizard-composer-editor.js.es6
index 5e2ef424..5335da81 100644
--- a/assets/javascripts/discourse/components/custom-wizard-composer-editor.js.es6
+++ b/assets/javascripts/discourse/components/custom-wizard-composer-editor.js.es6
@@ -12,6 +12,7 @@ import { alias } from "@ember/object/computed";
import Site from "discourse/models/site";
import { uploadIcon } from "discourse/lib/uploads";
import { dasherize } from "@ember/string";
+import showModal from "discourse/lib/show-modal";
const IMAGE_MARKDOWN_REGEX = /!\[(.*?)\|(\d{1,4}x\d{1,4})(,\s*\d{1,3}%)?(.*?)\]\((upload:\/\/.*?)\)(?!(.*`))/g;
@@ -19,7 +20,6 @@ export default ComposerEditor.extend({
classNameBindings: ["fieldClass"],
allowUpload: true,
showLink: false,
- showHyperlinkBox: false,
topic: null,
showToolbar: true,
focusTarget: "reply",
@@ -29,6 +29,7 @@ export default ComposerEditor.extend({
draftStatus: "null",
replyPlaceholder: alias("field.translatedPlaceholder"),
wizardEventFieldId: null,
+ composerEventPrefix: "wizard-editor",
@on("didInsertElement")
_composerEditorInit() {
@@ -77,24 +78,13 @@ export default ComposerEditor.extend({
$input.on("scroll", this._throttledSyncEditorAndPreviewScroll);
this._bindUploadTarget();
- const wizardEventNames = ["insert-text", "replace-text"];
- const eventPrefix = this.eventPrefix;
- this.appEvents.reopen({
- trigger(name, ...args) {
- let eventParts = name.split(":");
- let currentEventPrefix = eventParts[0];
- let currentEventName = eventParts[1];
+ const field = this.field;
+ this.editorInputClass = `.${dasherize(field.type)}-${dasherize(
+ field.id
+ )} .d-editor-input`;
- if (
- currentEventPrefix !== "wizard-editor" &&
- wizardEventNames.some((wen) => wen === currentEventName)
- ) {
- let wizardEventName = name.replace(eventPrefix, "wizard-editor");
- return this._super(wizardEventName, ...args);
- } else {
- return this._super(name, ...args);
- }
- },
+ this._uppyInstance.on("file-added", () => {
+ this.session.set("wizardEventFieldId", field.id);
});
},
@@ -116,12 +106,6 @@ export default ComposerEditor.extend({
return uploadIcon(false, this.siteSettings);
},
- click(e) {
- if ($(e.target).hasClass("wizard-composer-hyperlink")) {
- this.set("showHyperlinkBox", false);
- }
- },
-
@bind
_handleImageDeleteButtonClick(event) {
if (!event.target.classList.contains("delete-image-button")) {
@@ -165,7 +149,7 @@ export default ComposerEditor.extend({
shortcut: "K",
trimLeading: true,
unshift: true,
- sendAction: () => component.set("showHyperlinkBox", true),
+ sendAction: (event) => component.send("showLinkModal", event),
});
if (this.siteSettings.mentionables_enabled) {
@@ -206,17 +190,18 @@ export default ComposerEditor.extend({
this._super(...arguments);
},
- addLink(linkName, linkUrl) {
- let link = `[${linkName}](${linkUrl})`;
- this.appEvents.trigger("wizard-editor:insert-text", {
- fieldId: this.field.id,
- text: link,
- });
- this.set("showHyperlinkBox", false);
- },
+ showLinkModal(toolbarEvent) {
+ let linkText = "";
+ this._lastSel = toolbarEvent.selected;
- hideBox() {
- this.set("showHyperlinkBox", false);
+ if (this._lastSel) {
+ linkText = this._lastSel.value;
+ }
+
+ showModal("insert-hyperlink").setProperties({
+ linkText,
+ toolbarEvent,
+ });
},
showUploadModal() {
diff --git a/assets/javascripts/discourse/components/custom-wizard-composer-hyperlink.js.es6 b/assets/javascripts/discourse/components/custom-wizard-composer-hyperlink.js.es6
deleted file mode 100644
index a56b7aff..00000000
--- a/assets/javascripts/discourse/components/custom-wizard-composer-hyperlink.js.es6
+++ /dev/null
@@ -1,15 +0,0 @@
-import Component from "@ember/component";
-
-export default Component.extend({
- classNames: ["wizard-composer-hyperlink"],
-
- actions: {
- addLink() {
- this.addLink(this.linkName, this.linkUrl);
- },
-
- hideBox() {
- this.hideBox();
- },
- },
-});
diff --git a/assets/javascripts/discourse/components/custom-wizard-date-input.js.es6 b/assets/javascripts/discourse/components/custom-wizard-date-input.js.es6
index 9c8e4bff..2805c370 100644
--- a/assets/javascripts/discourse/components/custom-wizard-date-input.js.es6
+++ b/assets/javascripts/discourse/components/custom-wizard-date-input.js.es6
@@ -3,6 +3,7 @@ import discourseComputed from "discourse-common/utils/decorators";
export default DateInput.extend({
useNativePicker: false,
+ classNameBindings: ["fieldClass"],
@discourseComputed()
placeholder() {
diff --git a/assets/javascripts/discourse/components/custom-wizard-date-time-input.js.es6 b/assets/javascripts/discourse/components/custom-wizard-date-time-input.js.es6
index 44b675b0..1fcb62f5 100644
--- a/assets/javascripts/discourse/components/custom-wizard-date-time-input.js.es6
+++ b/assets/javascripts/discourse/components/custom-wizard-date-time-input.js.es6
@@ -2,6 +2,8 @@ import DateTimeInput from "discourse/components/date-time-input";
import discourseComputed from "discourse-common/utils/decorators";
export default DateTimeInput.extend({
+ classNameBindings: ["fieldClass"],
+
@discourseComputed("timeFirst", "tabindex")
timeTabindex(timeFirst, tabindex) {
return timeFirst ? tabindex : tabindex + 1;
diff --git a/assets/javascripts/discourse/components/custom-wizard-field-time.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-time.js.es6
index 82f9c68b..1406d63b 100644
--- a/assets/javascripts/discourse/components/custom-wizard-field-time.js.es6
+++ b/assets/javascripts/discourse/components/custom-wizard-field-time.js.es6
@@ -2,6 +2,8 @@ import Component from "@ember/component";
import { observes } from "discourse-common/utils/decorators";
export default Component.extend({
+ classNameBindings: ["fieldClass"],
+
@observes("time")
setValue() {
this.set("field.value", this.time.format(this.field.format));
diff --git a/assets/javascripts/discourse/components/custom-wizard-field-upload.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-upload.js.es6
index eb5d318b..990d7daa 100644
--- a/assets/javascripts/discourse/components/custom-wizard-field-upload.js.es6
+++ b/assets/javascripts/discourse/components/custom-wizard-field-upload.js.es6
@@ -4,7 +4,7 @@ import { computed } from "@ember/object";
export default Component.extend(UppyUploadMixin, {
classNames: ["wizard-field-upload"],
- classNameBindings: ["isImage"],
+ classNameBindings: ["isImage", "fieldClass"],
uploading: false,
type: computed(function () {
return `wizard_${this.field.id}`;
diff --git a/assets/javascripts/discourse/components/custom-wizard-field-user-selector.js.es6 b/assets/javascripts/discourse/components/custom-wizard-field-user-selector.js.es6
index 87d5ddb0..64741c6b 100644
--- a/assets/javascripts/discourse/components/custom-wizard-field-user-selector.js.es6
+++ b/assets/javascripts/discourse/components/custom-wizard-field-user-selector.js.es6
@@ -1,3 +1,5 @@
import Component from "@ember/component";
-export default Component.extend({});
+export default Component.extend({
+ classNameBindings: ["fieldClass"],
+});
diff --git a/assets/javascripts/discourse/components/custom-wizard-step.js.es6 b/assets/javascripts/discourse/components/custom-wizard-step.js.es6
index b98db1ab..7c933771 100644
--- a/assets/javascripts/discourse/components/custom-wizard-step.js.es6
+++ b/assets/javascripts/discourse/components/custom-wizard-step.js.es6
@@ -9,6 +9,7 @@ import CustomWizard, {
updateCachedWizard,
} from "discourse/plugins/discourse-custom-wizard/discourse/models/custom-wizard";
import { alias, not } from "@ember/object/computed";
+import discourseLater from "discourse-common/lib/later";
const alreadyWarned = {};
@@ -110,29 +111,22 @@ export default Component.extend({
},
autoFocus() {
- schedule("afterRender", () => {
- const $invalid = $(
- ".wizard-field.invalid:nth-of-type(1) .wizard-focusable"
- );
-
- if ($invalid.length) {
- return $invalid.focus();
- }
-
- $(".wizard-focusable:first").focus();
+ discourseLater(() => {
+ schedule("afterRender", () => {
+ if ($(".invalid .wizard-focusable").length) {
+ this.animateInvalidFields();
+ }
+ });
});
},
animateInvalidFields() {
schedule("afterRender", () => {
- let $element = $(
- ".invalid input[type=text],.invalid textarea,.invalid input[type=checkbox],.invalid .select-kit"
- );
-
- if ($element.length) {
+ let $invalid = $(".invalid .wizard-focusable");
+ if ($invalid.length) {
$([document.documentElement, document.body]).animate(
{
- scrollTop: $element.offset().top - 200,
+ scrollTop: $invalid.offset().top - 200,
},
400
);
diff --git a/assets/javascripts/discourse/components/wizard-subscription-container.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-container.js.es6
index 5cc6b17c..3a1eac9c 100644
--- a/assets/javascripts/discourse/components/wizard-subscription-container.js.es6
+++ b/assets/javascripts/discourse/components/wizard-subscription-container.js.es6
@@ -7,7 +7,7 @@ export default Component.extend(Subscription, {
@discourseComputed("subscribed")
subscribedIcon(subscribed) {
- return subscribed ? "check" : "dash";
+ return subscribed ? "check" : "times";
},
@discourseComputed("subscribed")
diff --git a/assets/javascripts/discourse/helpers/char-counter.js.es6 b/assets/javascripts/discourse/helpers/char-counter.js.es6
deleted file mode 100644
index a700a432..00000000
--- a/assets/javascripts/discourse/helpers/char-counter.js.es6
+++ /dev/null
@@ -1,22 +0,0 @@
-import { registerUnbound } from "discourse-common/lib/helpers";
-import I18n from "I18n";
-import Handlebars from "handlebars";
-
-export default registerUnbound("char-counter", function (body, maxLength) {
- let bodyLength = body ? body.length : 0;
- let finalString;
-
- if (maxLength) {
- let isOverMax = bodyLength > maxLength ? "true" : "false";
- finalString = `
${bodyLength} / ${I18n.t(
- "wizard.x_characters",
- { count: parseInt(maxLength, 10) }
- )}
`;
- } else {
- finalString = `${I18n.t("wizard.x_characters", {
- count: parseInt(bodyLength, 10),
- })}
`;
- }
-
- return new Handlebars.SafeString(finalString);
-});
diff --git a/assets/javascripts/discourse/helpers/wizard-char-counter.js.es6 b/assets/javascripts/discourse/helpers/wizard-char-counter.js.es6
new file mode 100644
index 00000000..1e194314
--- /dev/null
+++ b/assets/javascripts/discourse/helpers/wizard-char-counter.js.es6
@@ -0,0 +1,25 @@
+import { registerUnbound } from "discourse-common/lib/helpers";
+import I18n from "I18n";
+import Handlebars from "handlebars";
+
+export default registerUnbound(
+ "wizard-char-counter",
+ function (body, maxLength) {
+ let bodyLength = body ? body.length : 0;
+ let finalString;
+
+ if (maxLength) {
+ let isOverMax = bodyLength > maxLength ? "true" : "false";
+ finalString = `${bodyLength} / ${I18n.t(
+ "wizard.x_characters",
+ { count: parseInt(maxLength, 10) }
+ )}
`;
+ } else {
+ finalString = `${I18n.t("wizard.x_characters", {
+ count: parseInt(bodyLength, 10),
+ })}
`;
+ }
+
+ return new Handlebars.SafeString(finalString);
+ }
+);
diff --git a/assets/javascripts/discourse/lib/wizard-mapper.js.es6 b/assets/javascripts/discourse/lib/wizard-mapper.js.es6
index c398eaf4..9037bec5 100644
--- a/assets/javascripts/discourse/lib/wizard-mapper.js.es6
+++ b/assets/javascripts/discourse/lib/wizard-mapper.js.es6
@@ -35,6 +35,7 @@ function inputTypesContent(options = {}) {
const connectors = {
pair: [
"equal",
+ "not_equal",
"greater",
"less",
"greater_or_equal",
diff --git a/assets/javascripts/discourse/mixins/undo-changes.js.es6 b/assets/javascripts/discourse/mixins/undo-changes.js.es6
index b2ab322d..e98cfb0e 100644
--- a/assets/javascripts/discourse/mixins/undo-changes.js.es6
+++ b/assets/javascripts/discourse/mixins/undo-changes.js.es6
@@ -4,6 +4,8 @@ import { get, set } from "@ember/object";
import Mixin from "@ember/object/mixin";
import { deepEqual } from "discourse-common/lib/object";
+const observedCache = [];
+
export default Mixin.create({
didInsertElement() {
this._super(...arguments);
@@ -32,7 +34,13 @@ export default Mixin.create({
};
listProperties(componentType, opts).forEach((property) => {
- obj.removeObserver(property, this, this.toggleUndo);
+ if (observedCache.includes(property)) {
+ obj.removeObserver(property, this, this.toggleUndo);
+ let index = observedCache.indexOf(property);
+ if (index !== -1) {
+ observedCache.splice(index, 1);
+ }
+ }
});
},
@@ -45,6 +53,9 @@ export default Mixin.create({
};
listProperties(componentType, opts).forEach((property) => {
+ if (observedCache.indexOf(property) === -1) {
+ observedCache.push(property);
+ }
obj.addObserver(property, this, this.toggleUndo);
});
},
diff --git a/assets/javascripts/discourse/models/custom-wizard-field.js.es6 b/assets/javascripts/discourse/models/custom-wizard-field.js.es6
index a03c7c9e..2afe79d9 100644
--- a/assets/javascripts/discourse/models/custom-wizard-field.js.es6
+++ b/assets/javascripts/discourse/models/custom-wizard-field.js.es6
@@ -72,7 +72,7 @@ export default EmberObject.extend(ValidState, {
valid = true;
}
- this.setValid(valid);
+ this.setValid(Boolean(valid));
return valid;
},
diff --git a/assets/javascripts/discourse/routes/custom-wizard-step.js.es6 b/assets/javascripts/discourse/routes/custom-wizard-step.js.es6
index dd7b8be8..a882340b 100644
--- a/assets/javascripts/discourse/routes/custom-wizard-step.js.es6
+++ b/assets/javascripts/discourse/routes/custom-wizard-step.js.es6
@@ -1,6 +1,8 @@
import I18n from "I18n";
import { getCachedWizard } from "../models/custom-wizard";
import Route from "@ember/routing/route";
+import { scrollTop } from "discourse/mixins/scroll-top";
+import { action } from "@ember/object";
export default Route.extend({
beforeModel() {
@@ -48,4 +50,10 @@ export default Route.extend({
controller.setProperties(props);
},
+
+ @action
+ didTransition() {
+ scrollTop();
+ return true;
+ },
});
diff --git a/assets/javascripts/discourse/templates/admin-wizards-api-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-api-show.hbs
index 303b3f6d..7fe0fd21 100644
--- a/assets/javascripts/discourse/templates/admin-wizards-api-show.hbs
+++ b/assets/javascripts/discourse/templates/admin-wizards-api-show.hbs
@@ -32,13 +32,19 @@
- {{input type="checkbox" checked=wizard.after_signup}}
+
{{i18n "admin.wizard.after_signup_label"}}
@@ -88,7 +99,10 @@
- {{input type="checkbox" checked=wizard.prompt_completion}}
+
{{i18n "admin.wizard.prompt_completion_label"}}
@@ -98,7 +112,10 @@
- {{input type="checkbox" checked=wizard.after_time}}
+
{{i18n "admin.wizard.after_time_label"}}
{{d-button
action=(action "setNextSessionScheduled")
@@ -114,7 +131,10 @@
- {{input type="checkbox" checked=wizard.required}}
+
{{i18n "admin.wizard.required_label"}}
@@ -124,7 +144,10 @@
- {{input type="checkbox" checked=wizard.restart_on_revisit}}
+
{{i18n "admin.wizard.restart_on_revisit_label"}}
diff --git a/assets/javascripts/discourse/templates/components/custom-field-input.hbs b/assets/javascripts/discourse/templates/components/custom-field-input.hbs
index c0bdaaff..8c532cc9 100644
--- a/assets/javascripts/discourse/templates/components/custom-field-input.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-field-input.hbs
@@ -20,9 +20,10 @@
)}}
- {{input
- value=field.name
- placeholder=(i18n "admin.wizard.custom_field.name.select")}}
+
|
{{multi-select
diff --git a/assets/javascripts/discourse/templates/components/custom-wizard-composer-hyperlink.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-composer-hyperlink.hbs
deleted file mode 100644
index f430fb59..00000000
--- a/assets/javascripts/discourse/templates/components/custom-wizard-composer-hyperlink.hbs
+++ /dev/null
@@ -1,21 +0,0 @@
-
- {{i18n "composer.link_dialog_title"}}
- {{input
- class="composer-link-name"
- placeholder=(i18n "composer.link_optional_text")
- type="text"
- value=linkName}}
- {{input
- class="composer-link-url"
- placeholder=(i18n "composer.link_url_placeholder")
- type="text"
- value=linkUrl}}
- {{d-button
- label="wizard_composer.modal_ok"
- class="add-link btn-primary"
- click=(action "addLink")}}
- {{d-button
- label="wizard_composer.modal_cancel"
- class="hide-hyperlink-box btn-danger"
- click=(action "hideBox")}}
-
diff --git a/assets/javascripts/discourse/templates/components/custom-wizard-date-input.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-date-input.hbs
index 3b776215..0f798714 100644
--- a/assets/javascripts/discourse/templates/components/custom-wizard-date-input.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-date-input.hbs
@@ -1,11 +1,11 @@
-{{input
- type=inputType
+
diff --git a/assets/javascripts/discourse/templates/components/custom-wizard-editor.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-editor.hbs
index 5c1cc45b..6350594c 100644
--- a/assets/javascripts/discourse/templates/components/custom-wizard-editor.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-editor.hbs
@@ -40,7 +40,12 @@
{{conditional-loading-spinner condition=loading}}
- {{textarea tabindex=tabindex value=value class="d-editor-input" placeholder=placeholder}}
+
{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/custom-wizard-field-category.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-category.hbs
index 9cce87bc..79180dc4 100644
--- a/assets/javascripts/discourse/templates/components/custom-wizard-field-category.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-field-category.hbs
@@ -1,5 +1,6 @@
{{custom-wizard-category-selector
categories=categories
+ class=fieldClass
whitelist=field.content
onChange=(action (mut categories))
tabindex=field.tabindex
diff --git a/assets/javascripts/discourse/templates/components/custom-wizard-field-checkbox.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-checkbox.hbs
index 053e0218..0ceeb11b 100644
--- a/assets/javascripts/discourse/templates/components/custom-wizard-field-checkbox.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-field-checkbox.hbs
@@ -1 +1,7 @@
-{{input type="checkbox" id=field.id checked=field.value tabindex=field.tabindex}}
+
diff --git a/assets/javascripts/discourse/templates/components/custom-wizard-field-composer.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-composer.hbs
index 51964a1b..5e54636b 100644
--- a/assets/javascripts/discourse/templates/components/custom-wizard-field-composer.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-field-composer.hbs
@@ -2,6 +2,7 @@
field=field
composer=composer
wizard=wizard
+ fieldClass=fieldClass
groupsMentioned=(action "groupsMentioned")
cannotSeeMention=(action "cannotSeeMention")
importQuote=(action "importQuote")
@@ -14,6 +15,6 @@
{{#if field.char_counter}}
- {{char-counter field.value field.max_length}}
+ {{wizard-char-counter field.value field.max_length}}
{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/custom-wizard-field-group.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-group.hbs
index 6873f9bd..75a5b2b1 100644
--- a/assets/javascripts/discourse/templates/components/custom-wizard-field-group.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-field-group.hbs
@@ -1,5 +1,6 @@
{{custom-wizard-group-selector
groups=site.groups
+ class=fieldClass
field=field
whitelist=field.content
value=field.value
diff --git a/assets/javascripts/discourse/templates/components/custom-wizard-field-number.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-number.hbs
index f5d6543c..3049ab08 100644
--- a/assets/javascripts/discourse/templates/components/custom-wizard-field-number.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-field-number.hbs
@@ -1 +1,9 @@
-{{input type="number" step="0.01" id=field.id value=field.value tabindex=field.tabindex}}
+
+
diff --git a/assets/javascripts/discourse/templates/components/custom-wizard-field-tag.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-tag.hbs
index 90679ae7..17f68b92 100644
--- a/assets/javascripts/discourse/templates/components/custom-wizard-field-tag.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-field-tag.hbs
@@ -1,5 +1,6 @@
{{custom-wizard-tag-chooser
tags=field.value
+ class=fieldClass
tabindex=field.tabindex
tagGroups=field.tag_groups
everyTag=true
diff --git a/assets/javascripts/discourse/templates/components/custom-wizard-field-text.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-text.hbs
index 08733d3f..ed3f4f37 100644
--- a/assets/javascripts/discourse/templates/components/custom-wizard-field-text.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-field-text.hbs
@@ -1 +1,8 @@
-{{input id=field.id value=field.value class=fieldClass placeholder=field.translatedPlaceholder tabindex=field.tabindex autocomplete=autocomplete}}
+
diff --git a/assets/javascripts/discourse/templates/components/custom-wizard-field-textarea.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-textarea.hbs
index dda299bc..1d43813e 100644
--- a/assets/javascripts/discourse/templates/components/custom-wizard-field-textarea.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-field-textarea.hbs
@@ -1 +1,7 @@
-{{textarea id=field.id value=field.value class=fieldClass placeholder=field.translatedPlaceholder tabindex=field.tabindex}}
+
diff --git a/assets/javascripts/discourse/templates/components/custom-wizard-field-url.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-url.hbs
index c7e1a508..5d1c4574 100644
--- a/assets/javascripts/discourse/templates/components/custom-wizard-field-url.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-field-url.hbs
@@ -1 +1,6 @@
-{{input type="text" id=field.id value=field.value tabindex=field.tabindex}}
+
diff --git a/assets/javascripts/discourse/templates/components/custom-wizard-field.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field.hbs
index efda8629..a95f9fef 100644
--- a/assets/javascripts/discourse/templates/components/custom-wizard-field.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-wizard-field.hbs
@@ -20,7 +20,7 @@
{{#if field.char_counter}}
{{#if textType}}
- {{char-counter field.value field.max_length}}
+ {{wizard-char-counter field.value field.max_length}}
{{/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 106c61ee..6777c9db 100644
--- a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs
@@ -79,7 +79,10 @@
)}}
- {{input type="checkbox" checked=action.post_builder}}
+
{{i18n "admin.wizard.action.post_builder.checkbox"}}
@@ -408,7 +411,10 @@
- {{input type="checkbox" checked=action.wizard_user}}
+
@@ -477,7 +483,10 @@
- {{input type="checkbox" checked=action.wizard_user}}
+
@@ -716,7 +725,7 @@
property="name"
onUpdate=(action "mappedFieldUpdated")
options=(hash
- textSelection="key,value"
+ textSelection="key,value,output"
wizardFieldSelection=true
userFieldSelection="key,value"
context="action"
@@ -877,7 +886,10 @@
- {{input type="checkbox" checked=action.skip_redirect}}
+
{{i18n "admin.wizard.action.skip_redirect.description" type="topic"}}
@@ -891,7 +903,10 @@
- {{input type="checkbox" checked=action.suppress_notifications}}
+
{{i18n "admin.wizard.action.suppress_notifications.description" type="topic"}}
@@ -907,7 +922,7 @@
- {{input value=action.code}}
+
{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs
index 6273f9a9..038d22fb 100644
--- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs
@@ -11,7 +11,10 @@
- {{input name="label" value=field.label}}
+
@@ -22,7 +25,10 @@
{{i18n "admin.wizard.field.required_label"}}
- {{input type="checkbox" checked=field.required}}
+
@@ -31,7 +37,10 @@
- {{textarea name="description" value=field.description}}
+
@@ -78,11 +87,12 @@
- {{input
- type="number"
+
@@ -92,11 +102,12 @@
- {{input
- type="number"
+
@@ -107,9 +118,10 @@
{{i18n "admin.wizard.field.char_counter_placeholder"}}
- {{input
- type="checkbox"
- checked=field.char_counter}}
+
@@ -119,10 +131,11 @@
- {{textarea
+
{{/if}}
@@ -134,7 +147,11 @@
- {{textarea name="preview-template" value=field.preview_template class="preview-template"}}
+
{{/if}}
@@ -146,7 +163,10 @@
- {{input value=field.file_types class="medium"}}
+
{{/if}}
@@ -158,7 +178,11 @@
- {{input type="number" value=field.limit class="small"}}
+
{{/if}}
@@ -170,7 +194,10 @@
- {{input value=field.format class="medium"}}
+
@@ -229,9 +256,10 @@
- {{input
- type="checkbox"
- checked=field.can_create_tag}}
+
{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs
index 40ac09e0..c1aadd4b 100644
--- a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs
@@ -3,9 +3,10 @@
- {{input
+
@@ -51,7 +52,10 @@
{{i18n "admin.wizard.step.force_final.label"}}
- {{input type="checkbox" checked=step.force_final}}
+
{{i18n "admin.wizard.step.force_final.description"}}
@@ -77,7 +81,9 @@
{{i18n "admin.wizard.step.required_data.not_permitted_message"}}
- {{input value=step.required_data_message}}
+
{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs b/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs
index cb94b7ae..8ab5ea37 100644
--- a/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs
@@ -21,11 +21,12 @@
{{#if showText}}
- {{input
- type="text"
- value=value
- placeholder=(i18n placeholderKey)
- change=(action "changeInputValue")}}
+
{{/if}}
{{#if showComboBox}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs b/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs
index 8269d6ca..c01b3095 100644
--- a/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs
@@ -7,7 +7,10 @@
{{i18n (concat "admin.wizard.field.validations." type)}}
- {{input type="checkbox" checked=props.status}}
+
{{i18n "admin.wizard.field.validations.enabled"}}
@@ -27,7 +30,11 @@
- {{input type="number" class="time-n-value" value=props.time_n_value}}
+
{{combo-box
value=(readonly props.time_unit)
content=timeUnits
diff --git a/assets/stylesheets/common/wizard/field.scss b/assets/stylesheets/common/wizard/field.scss
index f710632b..71f12d84 100644
--- a/assets/stylesheets/common/wizard/field.scss
+++ b/assets/stylesheets/common/wizard/field.scss
@@ -32,13 +32,8 @@ body.custom-wizard {
}
}
- &.invalid {
- textarea,
- input[type="text"],
- input[type="checkbox"],
- .select-kit {
- outline: 4px solid var(--danger);
- }
+ &.invalid .wizard-focusable {
+ outline: 4px solid var(--danger);
}
}
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 8a856636..98519335 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -324,6 +324,7 @@ en:
then: "then"
set: "set"
equal: '='
+ not_equal: '!='
greater: '>'
less: '<'
greater_or_equal: '>='
diff --git a/lib/custom_wizard/action.rb b/lib/custom_wizard/action.rb
index 34f81455..ac0799f3 100644
--- a/lib/custom_wizard/action.rb
+++ b/lib/custom_wizard/action.rb
@@ -456,11 +456,16 @@ class CustomWizard::Action
if new_group_params[:usernames].present?
user_ids = get_user_ids(new_group_params[:usernames])
+ if user_ids.count < new_group_params[:usernames].count
+ log_error("Warning, group creation: some users were not found!")
+ end
user_ids -= owner_ids if owner_ids
user_ids.each { |user_id| group.group_users.build(user_id: user_id) }
end
- log_success("Group created", group.name)
+ if group.save
+ log_success("Group created", group.name)
+ end
result.output = group.name
else
diff --git a/lib/custom_wizard/log.rb b/lib/custom_wizard/log.rb
index cb5b78c7..ed9624e1 100644
--- a/lib/custom_wizard/log.rb
+++ b/lib/custom_wizard/log.rb
@@ -15,13 +15,13 @@ class CustomWizard::Log
@username = attrs['username']
end
- def self.create(wizard_id, action, username, message)
+ def self.create(wizard_id, action, username, message, date = Time.now)
log_id = SecureRandom.hex(12)
PluginStore.set('custom_wizard_log',
log_id.to_s,
{
- date: Time.now,
+ date: date,
wizard_id: wizard_id,
action: action,
username: username,
diff --git a/lib/custom_wizard/mapper.rb b/lib/custom_wizard/mapper.rb
index 410a50e4..66a10736 100644
--- a/lib/custom_wizard/mapper.rb
+++ b/lib/custom_wizard/mapper.rb
@@ -30,6 +30,7 @@ class CustomWizard::Mapper
OPERATORS = {
equal: '==',
+ not_equal: "!=",
greater: '>',
less: '<',
greater_or_equal: '>=',
@@ -44,7 +45,7 @@ class CustomWizard::Mapper
def initialize(params)
@inputs = params[:inputs] || {}
- @data = params[:data] || {}
+ @data = params[:data] ? params[:data].with_indifferent_access : {}
@user = params[:user]
@opts = params[:opts] || {}
end
@@ -255,7 +256,7 @@ class CustomWizard::Mapper
end
end
- if opts[:template] && CustomWizard::Subscription.subscribed?
+ if opts[:template] #&& CustomWizard::Subscription.subscribed?
template = Liquid::Template.parse(string)
string = template.render(data)
end
@@ -267,7 +268,12 @@ class CustomWizard::Mapper
return nil if data.nil?
k = keys.shift
result = data[k]
- keys.empty? ? result : self.recurse(result, keys)
+
+ if keys.empty?
+ result.is_a?(Hash) ? "" : result
+ else
+ self.recurse(result, keys)
+ end
end
def bool(value)
diff --git a/lib/custom_wizard/submission.rb b/lib/custom_wizard/submission.rb
index fc10cf31..ae616b0b 100644
--- a/lib/custom_wizard/submission.rb
+++ b/lib/custom_wizard/submission.rb
@@ -84,6 +84,10 @@ class CustomWizard::Submission
data
end
+ def submitted?
+ !!submitted_at
+ end
+
def self.get(wizard)
data = PluginStore.get("#{wizard.id}_#{KEY}", wizard.actor_id).last
new(wizard, data)
@@ -120,16 +124,23 @@ class CustomWizard::Submission
end
def self.list(wizard, order_by: nil, page: nil)
+ list_actor_id = wizard.actor_id
+ list_user = wizard.user if wizard.user.present?
+
params = { plugin_name: "#{wizard.id}_#{KEY}" }
- params[:key] = wizard.actor_id if wizard.actor_id
+ params[:key] = list_actor_id if list_actor_id
query = PluginStoreRow.where(params)
result = OpenStruct.new(submissions: [], total: nil)
query.each do |record|
if (submission_data = ::JSON.parse(record.value)).any?
+ submission_user = list_user || User.find_by(id: record.key.to_i)
+
submission_data.each do |data|
- result.submissions.push(new(wizard, data))
+ _wizard = wizard.clone
+ _wizard.user = submission_user if submission_user.present?
+ result.submissions.push(new(_wizard, data))
end
end
end
diff --git a/lib/custom_wizard/validators/template.rb b/lib/custom_wizard/validators/template.rb
index 60652322..f2a4feb0 100644
--- a/lib/custom_wizard/validators/template.rb
+++ b/lib/custom_wizard/validators/template.rb
@@ -84,7 +84,7 @@ class CustomWizard::TemplateValidator
def validate_guests(object, type)
guests_permitted = @data[:permitted] && @data[:permitted].any? do |m|
- m["output"].include?(CustomWizard::Wizard::GUEST_GROUP_ID)
+ m["output"]&.include?(CustomWizard::Wizard::GUEST_GROUP_ID)
end
return unless guests_permitted
diff --git a/lib/custom_wizard/wizard.rb b/lib/custom_wizard/wizard.rb
index c815c764..3d62cf25 100644
--- a/lib/custom_wizard/wizard.rb
+++ b/lib/custom_wizard/wizard.rb
@@ -176,6 +176,7 @@ class CustomWizard::Wizard
def unfinished?
return nil unless actor_id
+ return false if last_submission&.submitted?
most_recent = CustomWizard::UserHistory.where(
actor_id: actor_id,
@@ -194,6 +195,7 @@ class CustomWizard::Wizard
def completed?
return nil unless actor_id
+ return true if last_submission&.submitted?
history = CustomWizard::UserHistory.where(
actor_id: actor_id,
@@ -230,6 +232,7 @@ class CustomWizard::Wizard
m[:type] === 'assignment' && [*m[:result]].include?(GUEST_GROUP_ID)
else
if m[:type] === 'assignment'
+ [*m[:result]].include?(GUEST_GROUP_ID) ||
[*m[:result]].include?(Group::AUTO_GROUPS[:everyone]) ||
GroupUser.exists?(group_id: m[:result], user_id: user.id)
elsif m[:type] === 'validation'
@@ -281,6 +284,10 @@ class CustomWizard::Wizard
@submissions ||= CustomWizard::Submission.list(self).submissions
end
+ def last_submission
+ @last_submission ||= submissions&.last
+ end
+
def current_submission
@current_submission ||= begin
if submissions.present?
diff --git a/plugin.rb b/plugin.rb
index 3a65d315..4556ce99 100644
--- a/plugin.rb
+++ b/plugin.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
# name: discourse-custom-wizard
# about: Forms for Discourse. Better onboarding, structured posting, data enrichment, automated actions and much more.
-# version: 2.3.0
+# version: 2.3.4
# authors: Angus McLeod, Faizaan Gagan, Robert Barrow, Keegan George, Kaitlin Maddever
# url: https://github.com/paviliondev/discourse-custom-wizard
# contact_emails: development@pavilion.tech
diff --git a/spec/components/custom_wizard/action_spec.rb b/spec/components/custom_wizard/action_spec.rb
index 79c64520..c450582d 100644
--- a/spec/components/custom_wizard/action_spec.rb
+++ b/spec/components/custom_wizard/action_spec.rb
@@ -2,6 +2,7 @@
describe CustomWizard::Action do
fab!(:user) { Fabricate(:user, name: "Angus", username: 'angus', email: "angus@email.com", trust_level: TrustLevel[2]) }
+ fab!(:user1) { Fabricate(:user, name: "Angus One", username: 'angus1', email: "angus_one@email.com", trust_level: TrustLevel[2]) }
fab!(:category) { Fabricate(:category, name: 'cat1', slug: 'cat-slug') }
fab!(:tag) { Fabricate(:tag, name: 'tag1') }
fab!(:group) { Fabricate(:group) }
@@ -12,6 +13,7 @@ describe CustomWizard::Action do
let(:watch_categories) { get_wizard_fixture("actions/watch_categories") }
let(:watch_tags) { get_wizard_fixture("actions/watch_tags") }
let(:create_group) { get_wizard_fixture("actions/create_group") }
+ let(:create_group_with_nonexistent_user) { get_wizard_fixture("actions/create_group_bad_user") }
let(:add_to_group) { get_wizard_fixture("actions/add_to_group") }
let(:send_message) { get_wizard_fixture("actions/send_message") }
let(:send_message_multi) { get_wizard_fixture("actions/send_message_multi") }
@@ -350,7 +352,25 @@ 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
+ group_id = Group.where(name: wizard.current_submission.fields['action_9']).first.id
+ user_id = User.find_by(username: wizard_template['actions'][4]['usernames'][0]["output"][0]).id
+
expect(Group.where(name: wizard.current_submission.fields['action_9']).exists?).to eq(true)
+ expect(GroupUser.where(group_id: group_id, user_id: user_id).exists?).to eq(true)
+ end
+
+ it '#create_group completes successfully when user included in usernames does not exist but excludes users who do not exist and includes warning in log' do
+ wizard_template['actions'] << create_group_with_nonexistent_user
+ update_template(wizard_template)
+
+ wizard = CustomWizard::Builder.new(@template[:id], user).build
+ wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
+
+ group_id = Group.where(name: wizard.current_submission.fields['action_9']).first.id
+
+ expect(CustomWizard::Log.list_query.all.last.value.include? "some users were not found").to eq(true)
+ expect(Group.where(name: wizard.current_submission.fields['action_9']).exists?).to eq(true)
+ expect(GroupUser.where(group_id: group_id).count).to eq(1)
end
it '#add_to_group' do
diff --git a/spec/components/custom_wizard/mapper_spec.rb b/spec/components/custom_wizard/mapper_spec.rb
index 56778a07..3e0e6ce6 100644
--- a/spec/components/custom_wizard/mapper_spec.rb
+++ b/spec/components/custom_wizard/mapper_spec.rb
@@ -58,6 +58,11 @@ describe CustomWizard::Mapper do
"step_1_field_3" => "Value"
}
}
+ let(:template_params_object) {
+ {
+ "step_1_field_1": get_wizard_fixture("field/upload")
+ }
+ }
def create_template_mapper(data, user)
CustomWizard::Mapper.new(
@@ -286,6 +291,19 @@ describe CustomWizard::Mapper do
end
end
+ it "handles not equal pairs" do
+ expect(CustomWizard::Mapper.new(
+ inputs: inputs['not_equals_pair'],
+ data: data,
+ user: user1
+ ).perform).to eq(true)
+ expect(CustomWizard::Mapper.new(
+ inputs: inputs['not_equals_pair'],
+ data: data,
+ user: user2
+ ).perform).to eq(false)
+ end
+
it "handles greater than pairs" do
expect(CustomWizard::Mapper.new(
inputs: inputs['greater_than_pair'],
@@ -373,7 +391,7 @@ describe CustomWizard::Mapper do
expect(result).to eq(template_params["step_1_field_1"])
end
- it "requires a subscription" do
+ it "does not require a subscription" do
template = '{{ "w{step_1_field_1}" | size }}'
mapper = create_template_mapper(template_params, user1)
result = mapper.interpolate(
@@ -383,7 +401,7 @@ describe CustomWizard::Mapper do
wizard: true,
value: true
)
- expect(result).to eq("{{ \"#{template_params["step_1_field_1"]}\" | size }}")
+ expect(result).to eq("5")
end
context "with a subscription" do
@@ -448,6 +466,40 @@ describe CustomWizard::Mapper do
expect(result).to eq(template)
end
+ it "handles correct object variable references" do
+ template = <<-LIQUID.strip
+ {%- if "w{step_1_field_1.id}" == "step_2_field_7" -%}
+ Correct
+ {%- else -%}
+ Incorrect
+ {%-endif-%}
+ LIQUID
+ mapper = create_template_mapper(template_params_object, user1)
+ result = mapper.interpolate(
+ template.dup,
+ template: true,
+ wizard: true
+ )
+ expect(result).to eq("Correct")
+ end
+
+ it "handles incorrect object variable references" do
+ template = <<-LIQUID.strip
+ {%- if "w{step_1_field_1}" == "step_2_field_7" -%}
+ Correct
+ {%- else -%}
+ Incorrect
+ {%-endif-%}
+ LIQUID
+ mapper = create_template_mapper(template_params_object, user1)
+ result = mapper.interpolate(
+ template.dup,
+ template: true,
+ wizard: true
+ )
+ expect(result).to eq("Incorrect")
+ end
+
context "custom filter: 'first_non_empty'" do
it "gives first non empty element from list" do
template = <<-LIQUID.strip
diff --git a/spec/components/custom_wizard/template_validator_spec.rb b/spec/components/custom_wizard/template_validator_spec.rb
index b9b257e3..fe61be91 100644
--- a/spec/components/custom_wizard/template_validator_spec.rb
+++ b/spec/components/custom_wizard/template_validator_spec.rb
@@ -9,6 +9,7 @@ describe CustomWizard::TemplateValidator do
let(:composer_preview) { get_wizard_fixture("field/composer_preview") }
let(:guests_permitted) { get_wizard_fixture("wizard/guests_permitted") }
let(:upload_field) { get_wizard_fixture("field/upload") }
+ let(:validation_condition) { get_wizard_fixture("condition/validation_condition") }
let(:valid_liquid_template) {
<<-LIQUID.strip
@@ -182,6 +183,13 @@ describe CustomWizard::TemplateValidator do
CustomWizard::TemplateValidator.new(template).perform
).to eq(true)
end
+
+ it "validates settings with validation conditions" do
+ template[:permitted] = validation_condition["condition"]
+ expect(
+ CustomWizard::TemplateValidator.new(template).perform
+ ).to eq(true)
+ end
end
context "steps" do
diff --git a/spec/components/custom_wizard/wizard_spec.rb b/spec/components/custom_wizard/wizard_spec.rb
index ed6ebbea..17151077 100644
--- a/spec/components/custom_wizard/wizard_spec.rb
+++ b/spec/components/custom_wizard/wizard_spec.rb
@@ -6,11 +6,15 @@ describe CustomWizard::Wizard do
fab!(:admin_user) { Fabricate(:user, admin: true) }
let(:template_json) { get_wizard_fixture("wizard") }
let(:permitted_json) { get_wizard_fixture("wizard/permitted") }
+ let(:guests_permitted_json) { get_wizard_fixture("wizard/guests_permitted") }
+ let(:step_json) { get_wizard_fixture("step/step") }
before do
Group.refresh_automatic_group!(:trust_level_3)
@permitted_template = template_json.dup
@permitted_template["permitted"] = permitted_json["permitted"]
+ @guests_permitted_template = template_json.dup
+ @guests_permitted_template["permitted"] = guests_permitted_json["permitted"]
@wizard = CustomWizard::Wizard.new(template_json, user)
end
@@ -72,6 +76,28 @@ describe CustomWizard::Wizard do
expect(@wizard.start).to eq('step_2')
end
+ it "determines the user's current step if steps are added" do
+ append_steps
+ progress_step('step_1')
+ progress_step('step_2')
+ progress_step("step_3")
+
+ fourth_step = step_json.dup
+ fourth_step['id'] = "step_4"
+ template = template_json.dup
+ template['steps'] << fourth_step
+
+ CustomWizard::Template.save(template, skip_jobs: true)
+
+ wizard = CustomWizard::Wizard.new(template, user)
+ template['steps'].each do |step_template|
+ wizard.append_step(step_template['id'])
+ end
+
+ expect(wizard.steps.size).to eq(4)
+ expect(wizard.start).to eq(nil)
+ end
+
it "creates a step updater" do
expect(
@wizard.create_updater('step_1', step_1_field_1: "Text input")
@@ -200,6 +226,30 @@ describe CustomWizard::Wizard do
end
end
+ context "with subscription and guest wizard" do
+ before do
+ enable_subscription("standard")
+ end
+
+ it "permits admins" do
+ expect(
+ CustomWizard::Wizard.new(@guests_permitted_template, admin_user).permitted?
+ ).to eq(true)
+ end
+
+ it "permits regular users" do
+ expect(
+ CustomWizard::Wizard.new(@guests_permitted_template, user).permitted?
+ ).to eq(true)
+ end
+
+ it "permits guests" do
+ expect(
+ CustomWizard::Wizard.new(@guests_permitted_template, nil, "guest123").permitted?
+ ).to eq(true)
+ end
+ end
+
context "submissions" do
before do
CustomWizard::Submission.new(@wizard, step_1_field_1: "I am a user submission").save
diff --git a/spec/fixtures/actions/create_group_bad_user.json b/spec/fixtures/actions/create_group_bad_user.json
new file mode 100644
index 00000000..96d6796d
--- /dev/null
+++ b/spec/fixtures/actions/create_group_bad_user.json
@@ -0,0 +1,104 @@
+{
+ "id": "action_9",
+ "run_after": "step_1",
+ "type": "create_group",
+ "title": [
+ {
+ "type": "assignment",
+ "output": "New Group Member",
+ "output_type": "text",
+ "output_connector": "set"
+ }
+ ],
+ "custom_fields": [
+ {
+ "type": "association",
+ "pairs": [
+ {
+ "index": 0,
+ "key": "group_custom_field",
+ "key_type": "text",
+ "value": "step_3_field_1",
+ "value_type": "wizard_field",
+ "connector": "association"
+ }
+ ]
+ }
+ ],
+ "name": [
+ {
+ "type": "assignment",
+ "output": "step_1_field_1",
+ "output_type": "wizard_field",
+ "output_connector": "set"
+ }
+ ],
+ "full_name": [
+ {
+ "type": "assignment",
+ "output": "step_1_field_1",
+ "output_type": "wizard_field",
+ "output_connector": "set"
+ }
+ ],
+ "usernames": [
+ {
+ "type": "assignment",
+ "output_type": "user",
+ "output_connector": "set",
+ "output": [
+ "angus3"
+ ]
+ }
+ ],
+ "owner_usernames": [
+ {
+ "type": "assignment",
+ "output_type": "user",
+ "output_connector": "set",
+ "output": [
+ "angus"
+ ]
+ }
+ ],
+ "grant_trust_level": [
+ {
+ "type": "assignment",
+ "output": "3",
+ "output_type": "text",
+ "output_connector": "set"
+ }
+ ],
+ "mentionable_level": [
+ {
+ "type": "assignment",
+ "output": "1",
+ "output_type": "text",
+ "output_connector": "set"
+ }
+ ],
+ "messageable_level": [
+ {
+ "type": "assignment",
+ "output": "2",
+ "output_type": "text",
+ "output_connector": "set"
+ }
+ ],
+ "visibility_level": [
+ {
+ "type": "assignment",
+ "output": "3",
+ "output_type": "text",
+ "output_connector": "set"
+ }
+ ],
+ "members_visibility_level": [
+ {
+ "type": "assignment",
+ "output": "99",
+ "output_type": "text",
+ "output_connector": "set"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/spec/fixtures/condition/validation_condition.json b/spec/fixtures/condition/validation_condition.json
new file mode 100644
index 00000000..695c25c9
--- /dev/null
+++ b/spec/fixtures/condition/validation_condition.json
@@ -0,0 +1,17 @@
+{
+ "condition": [
+ {
+ "type": "validation",
+ "pairs": [
+ {
+ "index": 0,
+ "key": "trust_level",
+ "key_type": "user_field",
+ "value": "2",
+ "value_type": "text",
+ "connector": "greater_or_equal"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/spec/fixtures/mapper/inputs.json b/spec/fixtures/mapper/inputs.json
index 3fd406a4..2ef81a28 100644
--- a/spec/fixtures/mapper/inputs.json
+++ b/spec/fixtures/mapper/inputs.json
@@ -195,6 +195,21 @@
]
}
],
+ "not_equals_pair": [
+ {
+ "type": "validation",
+ "pairs": [
+ {
+ "index": 0,
+ "key": "trust_level",
+ "key_type": "user_field",
+ "value": "1",
+ "value_type": "text",
+ "connector": "not_equal"
+ }
+ ]
+ }
+ ],
"greater_than_pair": [
{
"type": "validation",
diff --git a/spec/fixtures/sprockets/require_tree_discourse_empty.js b/spec/fixtures/sprockets/require_tree_discourse_empty.js
index 953c5ec4..4f034d13 100644
--- a/spec/fixtures/sprockets/require_tree_discourse_empty.js
+++ b/spec/fixtures/sprockets/require_tree_discourse_empty.js
@@ -1 +1 @@
-//= require_tree_discourse
\ No newline at end of file
+//= require_tree_discourse
diff --git a/spec/fixtures/sprockets/require_tree_discourse_non_existant.js b/spec/fixtures/sprockets/require_tree_discourse_non_existant.js
index d9b2be76..fc4752e5 100644
--- a/spec/fixtures/sprockets/require_tree_discourse_non_existant.js
+++ b/spec/fixtures/sprockets/require_tree_discourse_non_existant.js
@@ -1 +1 @@
-//= require_tree_discourse dummy_path
\ No newline at end of file
+//= require_tree_discourse dummy_path
diff --git a/spec/fixtures/sprockets/require_tree_discourse_test.js b/spec/fixtures/sprockets/require_tree_discourse_test.js
index a86aa0d7..56451213 100644
--- a/spec/fixtures/sprockets/require_tree_discourse_test.js
+++ b/spec/fixtures/sprockets/require_tree_discourse_test.js
@@ -1 +1 @@
-//= require_tree_discourse sptest
\ No newline at end of file
+//= require_tree_discourse sptest
diff --git a/spec/serializers/custom_wizard/log_serializer_spec.rb b/spec/serializers/custom_wizard/log_serializer_spec.rb
index 7dd1db0f..cad4a85b 100644
--- a/spec/serializers/custom_wizard/log_serializer_spec.rb
+++ b/spec/serializers/custom_wizard/log_serializer_spec.rb
@@ -4,7 +4,7 @@ describe CustomWizard::LogSerializer do
fab!(:user) { Fabricate(:user) }
it 'should return log attributes' do
- CustomWizard::Log.create('first-test-wizard', 'perform_first_action', 'first_test_user', 'First log message')
+ CustomWizard::Log.create('first-test-wizard', 'perform_first_action', 'first_test_user', 'First log message', 1.day.ago)
CustomWizard::Log.create('second-test-wizard', 'perform_second_action', 'second_test_user', 'Second log message')
json_array = ActiveModel::ArraySerializer.new(
diff --git a/spec/serializers/custom_wizard/submission_serializer_spec.rb b/spec/serializers/custom_wizard/submission_serializer_spec.rb
index 02d8be8a..2fa000e8 100644
--- a/spec/serializers/custom_wizard/submission_serializer_spec.rb
+++ b/spec/serializers/custom_wizard/submission_serializer_spec.rb
@@ -3,7 +3,8 @@
require_relative '../../plugin_helper'
describe CustomWizard::SubmissionSerializer do
- fab!(:user) { Fabricate(:user) }
+ fab!(:user1) { Fabricate(:user) }
+ fab!(:user2) { Fabricate(:user) }
let(:template_json) {
JSON.parse(File.open(
@@ -13,36 +14,43 @@ describe CustomWizard::SubmissionSerializer do
before do
CustomWizard::Template.save(template_json, skip_jobs: true)
- wizard = CustomWizard::Wizard.create(template_json["id"], user)
- CustomWizard::Submission.new(wizard,
- step_1_field_1: "I am user submission",
- submitted_at: Time.now.iso8601
- ).save
- @list = CustomWizard::Submission.list(wizard, page: 0)
+
+ wizard = CustomWizard::Wizard.create(template_json["id"], user1)
+ CustomWizard::Submission.new(wizard, step_1_field_1: "I am user1 submission", submitted_at: Time.now.iso8601).save
+
+ wizard = CustomWizard::Wizard.create(template_json["id"], user2)
+ CustomWizard::Submission.new(wizard, step_1_field_1: "I am user2 submission", submitted_at: Time.now.iso8601).save
end
it 'should return submission attributes' do
+ wizard = CustomWizard::Wizard.create(template_json["id"])
+ list = CustomWizard::Submission.list(wizard, page: 0)
+
json_array = ActiveModel::ArraySerializer.new(
- @list.submissions,
+ list.submissions,
each_serializer: described_class
).as_json
- expect(json_array.length).to eq(1)
+ expect(json_array.length).to eq(2)
expect(json_array[0][:id].present?).to eq(true)
- expect(json_array[0][:user].present?).to eq(true)
expect(json_array[0][:submitted_at].present?).to eq(true)
+ expect(json_array[0][:user]).to eq(BasicUserSerializer.new(user2, root: false).as_json)
+ expect(json_array[1][:user]).to eq(BasicUserSerializer.new(user1, root: false).as_json)
end
it "should return field values, types and labels" do
+ wizard = CustomWizard::Wizard.create(template_json["id"])
+ list = CustomWizard::Submission.list(wizard, page: 0)
+
json_array = ActiveModel::ArraySerializer.new(
- @list.submissions,
+ list.submissions,
each_serializer: described_class
).as_json
- expect(json_array.length).to eq(1)
+ expect(json_array.length).to eq(2)
expect(json_array[0][:fields].as_json).to eq({
"step_1_field_1": {
- "value": "I am user submission",
+ "value": "I am user2 submission",
"type": "text",
"label": "Text"
}
diff --git a/test/javascripts/acceptance/field-test.js b/test/javascripts/acceptance/field-test.js
index 5eecce4f..f1d97130 100644
--- a/test/javascripts/acceptance/field-test.js
+++ b/test/javascripts/acceptance/field-test.js
@@ -54,6 +54,70 @@ acceptance("Field | Fields", function (needs) {
"Input in composer"
);
});
+ test("Composer - Hyperlink", async function (assert) {
+ await visit("/w/wizard");
+ assert.ok(
+ visible(".wizard-field.composer-field .wizard-field-composer textarea")
+ );
+ assert.ok(
+ exists(".wizard-field.composer-field .d-editor-button-bar button")
+ );
+ assert.ok(visible(".wizard-btn.toggle-preview"));
+ await fillIn(
+ ".wizard-field.composer-field .wizard-field-composer textarea",
+ "This is a link to "
+ );
+ assert.ok(
+ !exists(".insert-link.modal-body"),
+ "no hyperlink modal by default"
+ );
+ await click(
+ ".wizard-field.composer-field .wizard-field-composer .d-editor button.link"
+ );
+ assert.ok(exists(".insert-link.modal-body"), "hyperlink modal visible");
+
+ await fillIn(".modal-body .link-url", "google.com");
+ await fillIn(".modal-body .link-text", "Google");
+ await click(".modal-footer button.btn-primary");
+
+ assert.strictEqual(
+ query(".wizard-field.composer-field .wizard-field-composer textarea")
+ .value,
+ "This is a link to [Google](https://google.com)",
+ "adds link with url and text, prepends 'https://'"
+ );
+
+ assert.ok(
+ !exists(
+ ".wizard-field.composer-field .wizard-field-composer .insert-link.modal-body"
+ ),
+ "modal dismissed after submitting link"
+ );
+
+ await fillIn(
+ ".wizard-field.composer-field .wizard-field-composer textarea",
+ "Reset textarea contents."
+ );
+
+ await click(
+ ".wizard-field.composer-field .wizard-field-composer .d-editor button.link"
+ );
+ await fillIn(".modal-body .link-url", "google.com");
+ await fillIn(".modal-body .link-text", "Google");
+ await click(".modal-footer button.btn-danger");
+
+ assert.strictEqual(
+ query(".wizard-field.composer-field .wizard-field-composer textarea")
+ .value,
+ "Reset textarea contents.",
+ "does not insert anything after cancelling"
+ );
+
+ assert.ok(
+ !exists(".insert-link.modal-body"),
+ "modal dismissed after cancelling"
+ );
+ });
test("Text Only", async function (assert) {
await visit("/w/wizard");
|