diff --git a/assets/javascripts/wizard-custom.js b/assets/javascripts/wizard-custom.js
index 0ff181d0..03ab9103 100644
--- a/assets/javascripts/wizard-custom.js
+++ b/assets/javascripts/wizard-custom.js
@@ -38,6 +38,7 @@
//= 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 discourse/app/mixins/singleton
//= require discourse/app/mixins/upload
@@ -128,6 +129,7 @@
//= require template_include.js
//= require caret_position.js
//= require popper.js
+//= require bootstrap-modal.js
//= require bootbox.js
//= require discourse-shims
diff --git a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6
index 50061883..7e2dce31 100644
--- a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6
+++ b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6
@@ -1,37 +1,41 @@
-import ComposerEditor from 'discourse/components/composer-editor';
-import { default as computed, on } from 'discourse-common/utils/decorators';
+import ComposerEditor from "discourse/components/composer-editor";
+import { default as computed, on } from "discourse-common/utils/decorators";
import { findRawTemplate } from "discourse-common/lib/raw-templates";
import { throttle } from "@ember/runloop";
import { scheduleOnce } from "@ember/runloop";
import { safariHacksDisabled } from "discourse/lib/utilities";
+import highlightSyntax from "discourse/lib/highlight-syntax";
+import { getToken } from "wizard/lib/ajax";
+import { validateUploadedFiles } from "discourse/lib/uploads";
+const uploadHandlers = [];
export default ComposerEditor.extend({
- classNameBindings: ['fieldClass'],
- allowUpload: false,
+ classNameBindings: ["fieldClass"],
+ allowUpload: true,
showLink: false,
topic: null,
showToolbar: true,
focusTarget: "reply",
canWhisper: false,
- lastValidatedAt: 'lastValidatedAt',
+ lastValidatedAt: "lastValidatedAt",
uploadIcon: "upload",
popupMenuOptions: [],
- draftStatus: 'null',
-
+ draftStatus: "null",
+
@on("didInsertElement")
_composerEditorInit() {
const $input = $(this.element.querySelector(".d-editor-input"));
const $preview = $(this.element.querySelector(".d-editor-preview-wrapper"));
-
+
if (this.siteSettings.enable_mentions) {
$input.autocomplete({
template: findRawTemplate("user-selector-autocomplete"),
- dataSource: term => this.userSearchTerm.call(this, term),
+ dataSource: (term) => this.userSearchTerm.call(this, term),
key: "@",
- transformComplete: v => v.username || v.name,
+ transformComplete: (v) => v.username || v.name,
afterComplete() {
scheduleOnce("afterRender", () => $input.blur().focus());
- }
+ },
});
}
@@ -45,13 +49,73 @@ export default ComposerEditor.extend({
this._bindUploadTarget();
},
-
+
+ showUploadModal() {
+ $(".wizard-composer-upload").trigger("click");
+ },
+ _setUploadPlaceholderSend() {
+ if (!this.composer.get("reply")) {
+ this.composer.set("reply", "");
+ }
+ this._super(...arguments);
+ },
+
_bindUploadTarget() {
+ this._super(...arguments);
+ const $element = $(this.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(
+ I18n.t("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;
+ });
},
-
- _unbindUploadTarget() {
- },
-
actions: {
extraButtons(toolbar) {
if (this.allowUpload && this.uploadIcon && !this.site.mobileView) {
@@ -60,9 +124,13 @@ export default ComposerEditor.extend({
group: "insertions",
icon: this.uploadIcon,
title: "upload",
- sendAction: this.showUploadModal
+ sendAction: this.showUploadModal,
});
}
- }
- }
-})
\ No newline at end of file
+ },
+ previewUpdated($preview) {
+ highlightSyntax($preview[0], this.siteSettings, this.session);
+ this._super(...arguments);
+ },
+ },
+});
diff --git a/assets/javascripts/wizard/initializers/custom.js.es6 b/assets/javascripts/wizard/initializers/custom.js.es6
index dc1b400e..60f9a0e2 100644
--- a/assets/javascripts/wizard/initializers/custom.js.es6
+++ b/assets/javascripts/wizard/initializers/custom.js.es6
@@ -1,34 +1,45 @@
-import { default as computed } from 'discourse-common/utils/decorators';
+import { default as computed } from "discourse-common/utils/decorators";
import { dasherize } from "@ember/string";
export default {
- name: 'custom-routes',
-
+ name: "custom-routes",
+ after: "sniff-capabilities",
initialize(app) {
- if (window.location.pathname.indexOf('/w/') < 0) return;
+ if (window.location.pathname.indexOf("/w/") < 0) return;
- const EmberObject = requirejs('@ember/object').default;
- const Router = requirejs('wizard/router').default;
- const ApplicationRoute = requirejs('wizard/routes/application').default;
- const ajax = requirejs('wizard/lib/ajax').ajax;
- const StepModel = requirejs('wizard/models/step').default;
- const CustomWizard = requirejs('discourse/plugins/discourse-custom-wizard/wizard/models/custom').default;
- const WizardStep = requirejs('wizard/components/wizard-step').default;
- const WizardField = requirejs('wizard/components/wizard-field').default;
- const getUrl = requirejs('discourse-common/lib/get-url').default;
- const FieldModel = requirejs('wizard/models/wizard-field').default;
- const autocomplete = requirejs('discourse/lib/autocomplete').default;
- const cook = requirejs('discourse/plugins/discourse-custom-wizard/wizard/lib/text-lite').cook;
+ const EmberObject = requirejs("@ember/object").default;
+ const Router = requirejs("wizard/router").default;
+ const ApplicationRoute = requirejs("wizard/routes/application").default;
+ const ajax = requirejs("wizard/lib/ajax").ajax;
+ const StepModel = requirejs("wizard/models/step").default;
+ const CustomWizard = requirejs(
+ "discourse/plugins/discourse-custom-wizard/wizard/models/custom"
+ ).default;
+ const WizardStep = requirejs("wizard/components/wizard-step").default;
+ const WizardField = requirejs("wizard/components/wizard-field").default;
+ const getUrl = requirejs("discourse-common/lib/get-url").default;
+ const FieldModel = requirejs("wizard/models/wizard-field").default;
+ const autocomplete = requirejs("discourse/lib/autocomplete").default;
+ const cook = requirejs(
+ "discourse/plugins/discourse-custom-wizard/wizard/lib/text-lite"
+ ).cook;
const Singleton = requirejs("discourse/mixins/singleton").default;
const Store = requirejs("discourse/models/store").default;
- const registerRawHelpers = requirejs("discourse-common/lib/raw-handlebars-helpers").registerRawHelpers;
- const createHelperContext = requirejs("discourse-common/lib/helpers").createHelperContext;
- const RawHandlebars = requirejs("discourse-common/lib/raw-handlebars").default;
- const Site = requirejs("discourse/plugins/discourse-custom-wizard/wizard/models/site").default;
+ const registerRawHelpers = requirejs(
+ "discourse-common/lib/raw-handlebars-helpers"
+ ).registerRawHelpers;
+ const createHelperContext = requirejs("discourse-common/lib/helpers")
+ .createHelperContext;
+ const RawHandlebars = requirejs("discourse-common/lib/raw-handlebars")
+ .default;
+ const Site = requirejs(
+ "discourse/plugins/discourse-custom-wizard/wizard/models/site"
+ ).default;
const RestAdapter = requirejs("discourse/adapters/rest").default;
const Session = requirejs("discourse/models/session").default;
- const setDefaultOwner = requirejs("discourse-common/lib/get-owner").setDefaultOwner;
- const messageBus = requirejs('message-bus-client').default;
+ const setDefaultOwner = requirejs("discourse-common/lib/get-owner")
+ .setDefaultOwner;
+ const messageBus = requirejs("message-bus-client").default;
const container = app.__container__;
Discourse.Model = EmberObject.extend();
Discourse.__container__ = container;
@@ -37,20 +48,19 @@ export default {
// IE11 Polyfill - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill
if (!Object.entries)
- Object.entries = function( obj ){
- var ownProps = Object.keys( obj ),
- i = ownProps.length,
- resArray = new Array(i); // preallocate the Array
- while (i--)
- resArray[i] = [ownProps[i], obj[ownProps[i]]];
+ Object.entries = function (obj) {
+ var ownProps = Object.keys(obj),
+ i = ownProps.length,
+ resArray = new Array(i); // preallocate the Array
+ while (i--) resArray[i] = [ownProps[i], obj[ownProps[i]]];
return resArray;
};
$.fn.autocomplete = autocomplete;
-
- Object.keys(Ember.TEMPLATES).forEach(k => {
- if (k.indexOf("select-kit") === 0) {
+
+ Object.keys(Ember.TEMPLATES).forEach((k) => {
+ if (k.indexOf("select-kit") === 0) {
let template = Ember.TEMPLATES[k];
define(k, () => template);
}
@@ -61,148 +71,162 @@ export default {
const siteSettings = Wizard.SiteSettings;
app.register("site-settings:main", siteSettings, { instantiate: false });
createHelperContext({ siteSettings });
- targets.forEach(t => app.inject(t, "siteSettings", "site-settings:main"));
+ targets.forEach((t) => app.inject(t, "siteSettings", "site-settings:main"));
app.register("message-bus:main", messageBus, { instantiate: false });
- targets.forEach(t => app.inject(t, "messageBus", "message-bus:main"));
+ targets.forEach((t) => app.inject(t, "messageBus", "message-bus:main"));
app.register("service:store", Store);
- targets.forEach(t => app.inject(t, "store", "service:store"));
- targets.forEach(t => app.inject(t, "appEvents", "service:app-events"));
-
+ targets.forEach((t) => app.inject(t, "store", "service:store"));
+ targets.forEach((t) => app.inject(t, "appEvents", "service:app-events"));
+
app.register("adapter:rest", RestAdapter);
-
+
const site = Site.current();
app.register("site:main", site, { instantiate: false });
- targets.forEach(t => app.inject(t, "site", "site:main"));
-
- site.set('can_create_tag', false);
-
+ targets.forEach((t) => app.inject(t, "site", "site:main"));
+
+ site.set("can_create_tag", false);
+ app.register("session:main", Session.current(), { instantiate: false });
+ targets.forEach((t) => app.inject(t, "session", "session:main"));
let context = {
siteSettings: container.lookup("site-settings:main"),
currentUser: container.lookup("current-user:main"),
site: container.lookup("site:main"),
session: container.lookup("session:main"),
+ capabilities: container.lookup("capabilities:main"),
};
createHelperContext(context);
-
+ const session = container.lookup("session:main");
+ const setupData = document.getElementById("data-discourse-setup").dataset;
+ session.set("highlightJsPath", setupData.highlightJsPath);
Router.reopen({
- rootURL: getUrl('/w/')
+ rootURL: getUrl("/w/"),
});
- Router.map(function() {
- this.route('custom', { path: '/:wizard_id' }, function() {
- this.route('steps');
- this.route('step', { path: '/steps/:step_id' });
+ Router.map(function () {
+ this.route("custom", { path: "/:wizard_id" }, function () {
+ this.route("steps");
+ this.route("step", { path: "/steps/:step_id" });
});
});
ApplicationRoute.reopen({
redirect() {
- this.transitionTo('custom');
+ this.transitionTo("custom");
},
- model() {}
+ model() {},
});
WizardStep.reopen({
- classNameBindings: ['step.id'],
+ classNameBindings: ["step.id"],
animateInvalidFields() {
- Ember.run.scheduleOnce('afterRender', () => {
- let $element = $('.invalid input[type=text], .invalid textarea, .invalid input[type=checkbox], .invalid .select-kit');
-
+ Ember.run.scheduleOnce("afterRender", () => {
+ let $element = $(
+ ".invalid input[type=text], .invalid textarea, .invalid input[type=checkbox], .invalid .select-kit"
+ );
+
if ($element.length) {
- $([document.documentElement, document.body]).animate({
- scrollTop: $element.offset().top - 200
- }, 400, function() {
- $element.wiggle(2, 100);
- });
+ $([document.documentElement, document.body]).animate(
+ {
+ scrollTop: $element.offset().top - 200,
+ },
+ 400,
+ function () {
+ $element.wiggle(2, 100);
+ }
+ );
}
});
},
- ensureStartsAtTop: function() {
- window.scrollTo(0,0);
- }.observes('step.id'),
+ ensureStartsAtTop: function () {
+ window.scrollTo(0, 0);
+ }.observes("step.id"),
- showQuitButton: function() {
- const index = this.get('step.index');
- const required = this.get('wizard.required');
+ showQuitButton: function () {
+ const index = this.get("step.index");
+ const required = this.get("wizard.required");
return index === 0 && !required;
- }.property('step.index', 'wizard.required'),
+ }.property("step.index", "wizard.required"),
- cookedTitle: function() {
- return cook(this.get('step.title'));
- }.property('step.title'),
+ cookedTitle: function () {
+ return cook(this.get("step.title"));
+ }.property("step.title"),
- cookedDescription: function() {
- return cook(this.get('step.description'));
- }.property('step.description'),
+ cookedDescription: function () {
+ return cook(this.get("step.description"));
+ }.property("step.description"),
- bannerImage: function() {
- const src = this.get('step.banner');
+ bannerImage: function () {
+ const src = this.get("step.banner");
if (!src) return;
return getUrl(src);
- }.property('step.banner'),
+ }.property("step.banner"),
- handleMessage: function() {
- const message = this.get('step.message');
- this.sendAction('showMessage', message);
- }.observes('step.message'),
+ handleMessage: function () {
+ const message = this.get("step.message");
+ this.sendAction("showMessage", message);
+ }.observes("step.message"),
advance() {
- this.set('saving', true);
- this.get('step').save()
- .then(response => {
- if (this.get('finalStep')) {
+ this.set("saving", true);
+ this.get("step")
+ .save()
+ .then((response) => {
+ if (this.get("finalStep")) {
CustomWizard.finished(response);
} else {
- this.sendAction('goNext', response);
+ this.sendAction("goNext", response);
}
})
.catch(() => this.animateInvalidFields())
- .finally(() => this.set('saving', false));
- },
-
- keyPress(key) {
+ .finally(() => this.set("saving", false));
},
+ keyPress(key) {},
+
actions: {
quit() {
- this.get('wizard').skip();
+ this.get("wizard").skip();
},
done() {
- this.set('finalStep', true);
- this.send('nextStep');
+ this.set("finalStep", true);
+ this.send("nextStep");
},
showMessage(message) {
- this.sendAction('showMessage', message);
- }
- }
+ this.sendAction("showMessage", message);
+ },
+ },
});
StepModel.reopen({
save() {
- const wizardId = this.get('wizardId');
+ const wizardId = this.get("wizardId");
const fields = {};
- this.get('fields').forEach(f => {
- if (f.type !== 'text_only') {
+ this.get("fields").forEach((f) => {
+ if (f.type !== "text_only") {
fields[f.id] = f.value;
}
});
-
+
return ajax({
- url: `/w/${wizardId}/steps/${this.get('id')}`,
- type: 'PUT',
- data: { fields }
- }).catch(response => {
- if (response && response.responseJSON && response.responseJSON.errors) {
+ url: `/w/${wizardId}/steps/${this.get("id")}`,
+ type: "PUT",
+ data: { fields },
+ }).catch((response) => {
+ if (
+ response &&
+ response.responseJSON &&
+ response.responseJSON.errors
+ ) {
let wizardErrors = [];
- response.responseJSON.errors.forEach(err => {
+ response.responseJSON.errors.forEach((err) => {
if (err.field === wizardId) {
wizardErrors.push(err.description);
} else if (err.field) {
@@ -212,7 +236,7 @@ export default {
}
});
if (wizardErrors.length) {
- this.handleWizardError(wizardErrors.join('\n'));
+ this.handleWizardError(wizardErrors.join("\n"));
}
this.animateInvalidFields();
throw response;
@@ -220,8 +244,8 @@ export default {
if (response && response.responseText) {
const responseText = response.responseText;
- const start = responseText.indexOf('>') + 1;
- const end = responseText.indexOf('plugins');
+ const start = responseText.indexOf(">") + 1;
+ const end = responseText.indexOf("plugins");
const message = responseText.substring(start, end);
this.handleWizardError(message);
throw message;
@@ -230,44 +254,44 @@ export default {
},
handleWizardError(message) {
- this.set('message', {
- state: 'error',
- text: message
+ this.set("message", {
+ state: "error",
+ text: message,
});
- Ember.run.later(() => this.set('message', null), 6000);
- }
+ Ember.run.later(() => this.set("message", null), 6000);
+ },
});
WizardField.reopen({
- classNameBindings: ['field.id'],
+ classNameBindings: ["field.id"],
- cookedDescription: function() {
- return cook(this.get('field.description'));
- }.property('field.description'),
+ cookedDescription: function () {
+ return cook(this.get("field.description"));
+ }.property("field.description"),
- inputComponentName: function() {
- const type = this.get('field.type');
- const id = this.get('field.id');
- if (['text_only'].includes(type)) return false;
- return dasherize((type === 'component') ? id : `wizard-field-${type}`);
- }.property('field.type', 'field.id')
+ inputComponentName: function () {
+ const type = this.get("field.type");
+ const id = this.get("field.id");
+ if (["text_only"].includes(type)) return false;
+ return dasherize(type === "component" ? id : `wizard-field-${type}`);
+ }.property("field.type", "field.id"),
});
const StandardFieldValidation = [
- 'text',
- 'number',
- 'textarea',
- 'dropdown',
- 'tag',
- 'image',
- 'user_selector',
- 'text_only',
- 'composer',
- 'category',
- 'group',
- 'date',
- 'time',
- 'date_time'
+ "text",
+ "number",
+ "textarea",
+ "dropdown",
+ "tag",
+ "image",
+ "user_selector",
+ "text_only",
+ "composer",
+ "category",
+ "group",
+ "date",
+ "time",
+ "date_time",
];
FieldModel.reopen({
@@ -275,7 +299,7 @@ export default {
if (this.customCheck) {
return this.customCheck();
}
-
+
let valid = this.valid;
if (!this.required) {
@@ -283,23 +307,23 @@ export default {
return true;
}
- const val = this.get('value');
- const type = this.get('type');
-
- if (type === 'checkbox') {
+ const val = this.get("value");
+ const type = this.get("type");
+
+ if (type === "checkbox") {
valid = val;
- } else if (type === 'upload') {
+ } else if (type === "upload") {
valid = val && val.id > 0;
} else if (StandardFieldValidation.indexOf(type) > -1) {
valid = val && val.toString().length > 0;
- } else if (type === 'url') {
+ } else if (type === "url") {
valid = true;
}
-
+
this.setValid(valid);
return valid;
- }
+ },
});
- }
+ },
};
diff --git a/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs b/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs
index 87af2ccb..5e6e3c8c 100644
--- a/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs
+++ b/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs
@@ -13,4 +13,6 @@
showLink=showLink
composerEvents=true
disabled=disableTextarea
- outletArgs=(hash composer=composer editorType="composer")}}
\ No newline at end of file
+ outletArgs=(hash composer=composer editorType="composer")}}
+
+ {{input class="wizard-composer-upload hidden-upload-field" disabled=uploading type="file" multiple=true }}
diff --git a/assets/stylesheets/wizard/custom/composer.scss b/assets/stylesheets/wizard/custom/composer.scss
index 08521893..ddbdc4f7 100644
--- a/assets/stylesheets/wizard/custom/composer.scss
+++ b/assets/stylesheets/wizard/custom/composer.scss
@@ -183,3 +183,7 @@
.d-editor-button-bar .btn {
border: none;
}
+
+.wizard-composer-upload {
+ display: none;
+}
diff --git a/assets/stylesheets/wizard/wizard_custom.scss b/assets/stylesheets/wizard/wizard_custom.scss
index 366981bc..9e2221fc 100644
--- a/assets/stylesheets/wizard/wizard_custom.scss
+++ b/assets/stylesheets/wizard/wizard_custom.scss
@@ -1,5 +1,8 @@
@import "common/foundation/colors";
@import "common/foundation/variables";
+@import "common/base/code_highlighting";
+@import "common/base/modal";
+@import "desktop/modal";
@import "custom/base";
@import "custom/wizard";
@import "custom/step";
@@ -9,5 +12,3 @@
@import "custom/composer";
@import "custom/events";
@import "custom/locations";
-
-
diff --git a/views/layouts/wizard.html.erb b/views/layouts/wizard.html.erb
index 15a902b9..5979a464 100644
--- a/views/layouts/wizard.html.erb
+++ b/views/layouts/wizard.html.erb
@@ -23,6 +23,8 @@
<%= server_plugin_outlet "custom_wizard" %>
+ <%= tag.meta id: 'data-discourse-setup', data: client_side_setup_data %>
+
">