diff --git a/.discourse-compatibility b/.discourse-compatibility
index b2f02cc7..b53ff04d 100644
--- a/.discourse-compatibility
+++ b/.discourse-compatibility
@@ -1,2 +1,2 @@
-2.7.8: e07a57e398b6b1676ab42a7e34467556fca5416b
-2.5.1: bb85b3a0d2c0ab6b59bcb405731c39089ec6731c
\ No newline at end of file
+2.7.99: e07a57e398b6b1676ab42a7e34467556fca5416b
+2.5.1: bb85b3a0d2c0ab6b59bcb405731c39089ec6731c
diff --git a/assets/javascripts/discourse/models/custom-wizard-manager.js.es6 b/assets/javascripts/discourse/models/custom-wizard-manager.js.es6
index ea7cfd92..f054d0f8 100644
--- a/assets/javascripts/discourse/models/custom-wizard-manager.js.es6
+++ b/assets/javascripts/discourse/models/custom-wizard-manager.js.es6
@@ -1,6 +1,7 @@
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import EmberObject from "@ember/object";
+import getURL from "discourse-common/lib/get-url";
const CustomWizardManager = EmberObject.extend();
@@ -17,7 +18,7 @@ CustomWizardManager.reopenClass({
},
export(wizardIds) {
- let url = `${Discourse.BaseUrl}/${basePath}/export?`;
+ let url = `${getURL()}/${basePath}/export?`;
wizardIds.forEach((wizardId, index) => {
let step = "wizard_ids[]=" + wizardId;
diff --git a/assets/javascripts/wizard-custom.js b/assets/javascripts/wizard-custom.js
index cd0b06ae..61507fd9 100644
--- a/assets/javascripts/wizard-custom.js
+++ b/assets/javascripts/wizard-custom.js
@@ -4,7 +4,6 @@
//= require discourse/app/mixins/singleton
//= require discourse/app/mixins/upload
-//= require discourse/app/mixins/composer-upload
//= require discourse/app/mixins/textarea-text-manipulation
//= require discourse/app/adapters/rest
@@ -25,6 +24,7 @@
//= require discourse/app/services/app-events
//= require discourse/app/services/emoji-store
+//= require discourse/app/services/store
//= require discourse/app/components/user-selector
//= require discourse/app/components/conditional-loading-spinner
diff --git a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6
index b60f7551..1356f6ac 100644
--- a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6
+++ b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6
@@ -4,24 +4,14 @@ import {
on,
} from "discourse-common/utils/decorators";
import { findRawTemplate } from "discourse-common/lib/raw-templates";
-import { next, scheduleOnce, throttle } from "@ember/runloop";
+import { scheduleOnce, throttle } from "@ember/runloop";
import { caretPosition, inCodeBlock } from "discourse/lib/utilities";
import highlightSyntax from "discourse/lib/highlight-syntax";
-import { getToken } from "wizard/lib/ajax";
-import {
- displayErrorForUpload,
- getUploadMarkdown,
- uploadIcon,
- validateUploadedFiles,
-} from "discourse/lib/uploads";
-import { cacheShortUploadUrl } from "pretty-text/upload-short-url";
import { alias } from "@ember/object/computed";
-import WizardI18n from "../lib/wizard-i18n";
import Site from "../models/site";
+import { uploadIcon } from "discourse/lib/uploads";
+import { dasherize } from "@ember/string";
-const uploadMarkdownResolvers = [];
-
-const uploadHandlers = [];
export default ComposerEditor.extend({
classNameBindings: ["fieldClass"],
allowUpload: true,
@@ -35,6 +25,7 @@ export default ComposerEditor.extend({
popupMenuOptions: [],
draftStatus: "null",
replyPlaceholder: alias("field.placeholder"),
+ uploadingFieldId: null,
@on("didInsertElement")
_composerEditorInit() {
@@ -90,6 +81,50 @@ export default ComposerEditor.extend({
}
this._bindUploadTarget();
+
+ const wizardEventNames = ["insert-text", "replace-text"];
+ const eventPrefix = this.eventPrefix;
+ const session = this.get("session");
+ this.appEvents.reopen({
+ trigger(name, ...args) {
+ let eventParts = name.split(":");
+ let currentEventPrefix = eventParts[0];
+ let currentEventName = eventParts[1];
+
+ if (
+ currentEventPrefix !== "wizard-editor" &&
+ wizardEventNames.some((wen) => wen === currentEventName)
+ ) {
+ let wizardName = name.replace(eventPrefix, "wizard-editor");
+ if (currentEventName === "insert-text") {
+ args = {
+ text: args[0],
+ };
+ }
+ if (currentEventName === "replace-text") {
+ args = {
+ oldVal: args[0],
+ newVal: args[1],
+ };
+ }
+ let wizardArgs = Object.assign(
+ {},
+ {
+ fieldId: session.get("uploadingFieldId"),
+ },
+ args
+ );
+ return this._super(wizardName, wizardArgs);
+ } else {
+ return this._super(name, ...args);
+ }
+ },
+ });
+ },
+
+ @discourseComputed("field.id")
+ fileUploadElementId(fieldId) {
+ return `file-uploader-${dasherize(fieldId)}`;
},
@discourseComputed
@@ -105,192 +140,6 @@ export default ComposerEditor.extend({
return uploadIcon(false, this.siteSettings);
},
- _setUploadPlaceholderSend() {
- if (!this.composer.get("reply")) {
- this.composer.set("reply", "");
- }
- this._super(...arguments);
- },
-
- _bindUploadTarget() {
- this._super(...arguments);
- const $element = $(this.element);
- // adding dropZone property post initialization
- $element.fileupload("option", "dropZone", $element);
-
- $element.off("fileuploadsubmit");
-
- $element.on("fileuploadsubmit", (e, data) => {
- const max = this.siteSettings.simultaneous_uploads;
-
- // Limit the number of simultaneous uploads
- if (max > 0 && data.files.length > max) {
- bootbox.alert(
- WizardI18n("post.errors.too_many_dragged_and_dropped_files", { max })
- );
- return false;
- }
-
- // Look for a matching file upload handler contributed from a plugin
- const matcher = (handler) => {
- const ext = handler.extensions.join("|");
- const regex = new RegExp(`\\.(${ext})$`, "i");
- return regex.test(data.files[0].name);
- };
-
- const matchingHandler = uploadHandlers.find(matcher);
- if (data.files.length === 1 && matchingHandler) {
- if (!matchingHandler.method(data.files[0], this)) {
- return false;
- }
- }
-
- // If no plugin, continue as normal
- const isPrivateMessage = this.get("composer.privateMessage");
-
- data.formData = { type: "composer" };
- data.formData.authenticity_token = getToken();
- if (isPrivateMessage) {
- data.formData.for_private_message = true;
- }
- if (this._pasted) {
- data.formData.pasted = true;
- }
-
- const opts = {
- user: this.currentUser,
- siteSettings: this.siteSettings,
- isPrivateMessage,
- allowStaffToUploadAnyFileInPm: this.siteSettings
- .allow_staff_to_upload_any_file_in_pm,
- };
-
- const isUploading = validateUploadedFiles(data.files, opts);
-
- this.setProperties({ uploadProgress: 0, isUploading });
-
- return isUploading;
- });
-
- $element.on("fileuploadprogressall", (e, data) => {
- this.set(
- "uploadProgress",
- parseInt((data.loaded / data.total) * 100, 10)
- );
- });
-
- $element.on("fileuploadfail", (e, data) => {
- this._setUploadPlaceholderDone(data);
- this._resetUpload(true);
-
- const userCancelled = this._xhr && this._xhr._userCancelled;
- this._xhr = null;
-
- if (!userCancelled) {
- displayErrorForUpload(data, this.siteSettings);
- }
- });
-
- $element.on("fileuploadsend", (e, data) => {
- this._pasted = false;
- this._validUploads++;
-
- this._setUploadPlaceholderSend(data);
-
- this.appEvents.trigger("wizard-editor:insert-text", {
- fieldId: this.field.id,
- text: this.uploadPlaceholder,
- });
-
- if (data.xhr && data.originalFiles.length === 1) {
- this.set("isCancellable", true);
- this._xhr = data.xhr();
- }
- });
-
- $element.on("fileuploaddone", (e, data) => {
- let upload = data.result;
-
- this._setUploadPlaceholderDone(data);
-
- if (!this._xhr || !this._xhr._userCancelled) {
- const markdown = uploadMarkdownResolvers.reduce(
- (md, resolver) => resolver(upload) || md,
- getUploadMarkdown(upload)
- );
-
- cacheShortUploadUrl(upload.short_url, upload);
- this.appEvents.trigger("wizard-editor:replace-text", {
- fieldId: this.field.id,
- oldVal: this.uploadPlaceholder.trim(),
- newVal: markdown,
- });
- this._resetUpload(false);
- } else {
- this._resetUpload(true);
- }
- });
- },
-
- _resetUpload(removePlaceholder) {
- next(() => {
- if (this._validUploads > 0) {
- this._validUploads--;
- }
- if (this._validUploads === 0) {
- this.setProperties({
- uploadProgress: 0,
- isUploading: false,
- isCancellable: false,
- });
- }
- if (removePlaceholder) {
- this.appEvents.trigger("wizard-editor:replace-text", {
- fieldId: this.field.id,
- oldVal: this.uploadPlaceholder,
- newVal: "",
- });
- }
- this._resetUploadFilenamePlaceholder();
- });
- },
-
- _registerImageScaleButtonClick($preview) {
- const imageScaleRegex = /!\[(.*?)\|(\d{1,4}x\d{1,4})(,\s*\d{1,3}%)?(.*?)\]\((upload:\/\/.*?)\)(?!(.*`))/g;
- $preview.off("click", ".scale-btn").on("click", ".scale-btn", (e) => {
- const index = parseInt($(e.target).parent().attr("data-image-index"), 10);
-
- const scale = e.target.attributes["data-scale"].value;
- const matchingPlaceholder = this.get("composer.reply").match(
- imageScaleRegex
- );
-
- if (matchingPlaceholder) {
- const match = matchingPlaceholder[index];
-
- if (match) {
- const replacement = match.replace(
- imageScaleRegex,
- `![$1|$2, ${scale}%$4]($5)`
- );
-
- this.appEvents.trigger("wizard-editor:replace-text", {
- fieldId: this.field.id,
- oldVal: matchingPlaceholder[index],
- newVal: replacement,
- options: {
- regex: imageScaleRegex,
- index,
- },
- });
- }
- }
-
- e.preventDefault();
- return;
- });
- },
-
click(e) {
if ($(e.target).hasClass("wizard-composer-hyperlink")) {
this.set("showHyperlinkBox", false);
@@ -372,7 +221,8 @@ export default ComposerEditor.extend({
},
showUploadModal() {
- $(this.element.querySelector(".wizard-composer-upload")).trigger("click");
+ this.session.set("uploadingFieldId", this.field.id);
+ document.getElementById(this.fileUploadElementId).click();
},
},
});
diff --git a/assets/javascripts/wizard/components/wizard-date-input.js.es6 b/assets/javascripts/wizard/components/wizard-date-input.js.es6
index bb11b655..9c8e4bff 100644
--- a/assets/javascripts/wizard/components/wizard-date-input.js.es6
+++ b/assets/javascripts/wizard/components/wizard-date-input.js.es6
@@ -1,8 +1,5 @@
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({
useNativePicker: false,
@@ -11,32 +8,9 @@ export default DateInput.extend({
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()));
- });
+ _opts() {
+ return {
+ format: this.format || "LL",
+ };
},
});
diff --git a/assets/javascripts/wizard/components/wizard-field-composer.js.es6 b/assets/javascripts/wizard/components/wizard-field-composer.js.es6
index 3a4a706c..8b9ecb82 100644
--- a/assets/javascripts/wizard/components/wizard-field-composer.js.es6
+++ b/assets/javascripts/wizard/components/wizard-field-composer.js.es6
@@ -16,7 +16,7 @@ export default Ember.Component.extend({
"composer",
EmberObject.create({
loading: false,
- reply: this.get("field.value"),
+ reply: this.get("field.value") || "",
})
);
},
diff --git a/assets/javascripts/wizard/initializers/custom-wizard.js.es6 b/assets/javascripts/wizard/initializers/custom-wizard.js.es6
index ca396081..f2095827 100644
--- a/assets/javascripts/wizard/initializers/custom-wizard.js.es6
+++ b/assets/javascripts/wizard/initializers/custom-wizard.js.es6
@@ -9,7 +9,7 @@ export default {
const Router = requirejs("wizard/router").default;
const ApplicationRoute = requirejs("wizard/routes/application").default;
const getUrl = requirejs("discourse-common/lib/get-url").default;
- const Store = requirejs("discourse/models/store").default;
+ const Store = requirejs("discourse/services/store").default;
const registerRawHelpers = requirejs(
"discourse-common/lib/raw-handlebars-helpers"
).registerRawHelpers;
@@ -111,8 +111,16 @@ export default {
model() {},
});
- $.ajaxPrefilter(function (_, __, jqXHR) {
- jqXHR.setRequestHeader("X-CSRF-Token", getToken());
+ // Add a CSRF token to all AJAX requests
+ let token = getToken();
+ session.set("csrfToken", token);
+ let callbacks = $.Callbacks();
+ $.ajaxPrefilter(callbacks.fire);
+
+ callbacks.add(function (options, originalOptions, xhr) {
+ if (!options.crossDomain) {
+ xhr.setRequestHeader("X-CSRF-Token", session.get("csrfToken"));
+ }
});
},
};
diff --git a/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs b/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs
index c4bf1c74..be98db8e 100644
--- a/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs
+++ b/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs
@@ -16,24 +16,10 @@
disabled=disableTextarea
outletArgs=(hash composer=composer editorType="composer")}}
-{{input
- class="wizard-composer-upload hidden-upload-field"
- disabled=isUploading
+
- {{loading-spinner size="small"}}{{wizard-i18n "upload_selector.uploading"}} {{uploadProgress}}%
- {{#if isCancellable}}
- {{d-icon "times"}}
- {{/if}}
-
-{{/if}}
+ id={{fileUploadElementId}}
+ class="wizard-composer-upload"
+ accept={{allowedFileTypes}}
+ multiple
+ >
diff --git a/lib/custom_wizard/validators/template.rb b/lib/custom_wizard/validators/template.rb
index c90944e9..d4a8367f 100644
--- a/lib/custom_wizard/validators/template.rb
+++ b/lib/custom_wizard/validators/template.rb
@@ -18,8 +18,8 @@ class CustomWizard::TemplateValidator
data[:steps].each do |step|
check_required(step, :step)
- if data[:fields].present?
- data[:fields].each do |field|
+ if step[:fields].present?
+ step[:fields].each do |field|
check_required(field, :field)
end
end
diff --git a/spec/components/custom_wizard/template_validator_spec.rb b/spec/components/custom_wizard/template_validator_spec.rb
index c8ce915a..015228a3 100644
--- a/spec/components/custom_wizard/template_validator_spec.rb
+++ b/spec/components/custom_wizard/template_validator_spec.rb
@@ -45,4 +45,37 @@ describe CustomWizard::TemplateValidator do
CustomWizard::TemplateValidator.new(template).perform
).to eq(false)
end
+
+ context "steps" do
+ CustomWizard::TemplateValidator.required[:step].each do |attribute|
+ it "invalidates if \"#{attribute.to_s}\" is not present" do
+ template[:steps][0][attribute] = nil
+ expect(
+ CustomWizard::TemplateValidator.new(template).perform
+ ).to eq(false)
+ end
+ end
+ end
+
+ context "fields" do
+ CustomWizard::TemplateValidator.required[:field].each do |attribute|
+ it "invalidates if \"#{attribute.to_s}\" is not present" do
+ template[:steps][0][:fields][0][attribute] = nil
+ expect(
+ CustomWizard::TemplateValidator.new(template).perform
+ ).to eq(false)
+ end
+ end
+ end
+
+ context "actions" do
+ CustomWizard::TemplateValidator.required[:action].each do |attribute|
+ it "invalidates if \"#{attribute.to_s}\" is not present" do
+ template[:actions][0][attribute] = nil
+ expect(
+ CustomWizard::TemplateValidator.new(template).perform
+ ).to eq(false)
+ end
+ end
+ end
end