FIXES: Improve wizard initialization && more upload improvements
Dieser Commit ist enthalten in:
Ursprung
20ddba2afd
Commit
039262ce2c
8 geänderte Dateien mit 574 neuen und 352 gelöschten Zeilen
|
@ -2,7 +2,7 @@ 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 { scheduleOnce, next } from "@ember/runloop";
|
||||
import {
|
||||
safariHacksDisabled,
|
||||
caretPosition,
|
||||
|
@ -10,7 +10,15 @@ import {
|
|||
} from "discourse/lib/utilities";
|
||||
import highlightSyntax from "discourse/lib/highlight-syntax";
|
||||
import { getToken } from "wizard/lib/ajax";
|
||||
import { validateUploadedFiles } from "discourse/lib/uploads";
|
||||
import {
|
||||
validateUploadedFiles,
|
||||
getUploadMarkdown
|
||||
} from "discourse/lib/uploads";
|
||||
import {
|
||||
cacheShortUploadUrl,
|
||||
} from "pretty-text/upload-short-url";
|
||||
|
||||
const uploadMarkdownResolvers = [];
|
||||
|
||||
const uploadHandlers = [];
|
||||
export default ComposerEditor.extend({
|
||||
|
@ -57,10 +65,6 @@ export default ComposerEditor.extend({
|
|||
|
||||
this._bindUploadTarget();
|
||||
},
|
||||
|
||||
showUploadModal() {
|
||||
$(".wizard-composer-upload").trigger("click");
|
||||
},
|
||||
|
||||
_setUploadPlaceholderSend() {
|
||||
if (!this.composer.get("reply")) {
|
||||
|
@ -140,11 +144,117 @@ export default ComposerEditor.extend({
|
|||
|
||||
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.fieldId,
|
||||
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.fieldId,
|
||||
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.fieldId,
|
||||
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.fieldId,
|
||||
oldVal: matchingPlaceholder[index],
|
||||
newVal: replacement,
|
||||
options: {
|
||||
regex: imageScaleRegex,
|
||||
index
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
return;
|
||||
});
|
||||
},
|
||||
|
||||
click(e) {
|
||||
|
@ -155,17 +265,18 @@ export default ComposerEditor.extend({
|
|||
|
||||
actions: {
|
||||
extraButtons(toolbar) {
|
||||
const component = this;
|
||||
|
||||
if (this.allowUpload && this.uploadIcon && !this.site.mobileView) {
|
||||
toolbar.addButton({
|
||||
id: "upload",
|
||||
group: "insertions",
|
||||
icon: this.uploadIcon,
|
||||
title: "upload",
|
||||
sendAction: this.showUploadModal,
|
||||
sendAction: (event) => component.send("showUploadModal", event),
|
||||
});
|
||||
}
|
||||
|
||||
const component = this;
|
||||
toolbar.addButton({
|
||||
id: "link",
|
||||
group: "insertions",
|
||||
|
@ -183,12 +294,19 @@ export default ComposerEditor.extend({
|
|||
|
||||
addLink(linkName, linkUrl) {
|
||||
let link = `[${linkName}](${linkUrl})`;
|
||||
this.appEvents.trigger("composer:insert-text", link);
|
||||
this.appEvents.trigger("wizard-editor:insert-text", {
|
||||
fieldId: this.fieldId,
|
||||
text: link
|
||||
});
|
||||
this.set("showHyperlinkBox", false);
|
||||
},
|
||||
|
||||
hideBox() {
|
||||
this.set("showHyperlinkBox", false);
|
||||
},
|
||||
|
||||
showUploadModal() {
|
||||
$(this.element.querySelector(".wizard-composer-upload")).trigger("click");
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -5,16 +5,6 @@ export default Ember.Application.extend({
|
|||
Resolver: buildResolver("wizard"),
|
||||
|
||||
start() {
|
||||
Object.keys(requirejs._eak_seen).forEach(key => {
|
||||
if (/\/initializers\//.test(key)) {
|
||||
const module = requirejs(key, null, null, true);
|
||||
if (!module) {
|
||||
throw new Error(key + " must export an initializer.");
|
||||
}
|
||||
this.initializer(module.default);
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(requirejs._eak_seen).forEach((key) => {
|
||||
if (/\/pre\-initializers\//.test(key)) {
|
||||
const module = requirejs(key, null, null, true);
|
||||
|
@ -31,5 +21,15 @@ export default Ember.Application.extend({
|
|||
this.initializer(init);
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(requirejs._eak_seen).forEach(key => {
|
||||
if (/\/initializers\//.test(key)) {
|
||||
const module = requirejs(key, null, null, true);
|
||||
if (!module) {
|
||||
throw new Error(key + " must export an initializer.");
|
||||
}
|
||||
this.initializer(module.default);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
178
assets/javascripts/wizard/initializers/custom-wizard-field.js.es6
Normale Datei
178
assets/javascripts/wizard/initializers/custom-wizard-field.js.es6
Normale Datei
|
@ -0,0 +1,178 @@
|
|||
import { dasherize } from "@ember/string";
|
||||
|
||||
export default {
|
||||
name: "custom-wizard-field",
|
||||
initialize(app) {
|
||||
if (window.location.pathname.indexOf("/w/") < 0) return;
|
||||
|
||||
const FieldComponent = requirejs("wizard/components/wizard-field").default;
|
||||
const FieldModel = requirejs("wizard/models/wizard-field").default;
|
||||
const { cook } = requirejs("discourse/plugins/discourse-custom-wizard/wizard/lib/text-lite");
|
||||
const DEditor = requirejs("discourse/components/d-editor").default;
|
||||
const { clipboardHelpers } = requirejs("discourse/lib/utilities");
|
||||
const { toMarkdown } = requirejs("discourse/lib/to-markdown");
|
||||
|
||||
FieldComponent.reopen({
|
||||
classNameBindings: ["field.id"],
|
||||
|
||||
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"),
|
||||
});
|
||||
|
||||
const StandardFieldValidation = [
|
||||
"text",
|
||||
"number",
|
||||
"textarea",
|
||||
"dropdown",
|
||||
"tag",
|
||||
"image",
|
||||
"user_selector",
|
||||
"text_only",
|
||||
"composer",
|
||||
"category",
|
||||
"group",
|
||||
"date",
|
||||
"time",
|
||||
"date_time",
|
||||
];
|
||||
|
||||
FieldModel.reopen({
|
||||
check() {
|
||||
if (this.customCheck) {
|
||||
return this.customCheck();
|
||||
}
|
||||
|
||||
let valid = this.valid;
|
||||
|
||||
if (!this.required) {
|
||||
this.setValid(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
const val = this.get("value");
|
||||
const type = this.get("type");
|
||||
|
||||
if (type === "checkbox") {
|
||||
valid = val;
|
||||
} 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") {
|
||||
valid = true;
|
||||
}
|
||||
|
||||
this.setValid(valid);
|
||||
|
||||
return valid;
|
||||
},
|
||||
});
|
||||
|
||||
const isInside = (text, regex) => {
|
||||
const matches = text.match(regex);
|
||||
return matches && matches.length % 2;
|
||||
};
|
||||
|
||||
DEditor.reopen({
|
||||
isComposer: true,
|
||||
|
||||
didInsertElement() {
|
||||
this._super();
|
||||
if (this.wizardComposerEvents) {
|
||||
this.appEvents.on("wizard-editor:insert-text", this, "_wizardInsertText");
|
||||
this.appEvents.on("wizard-editor:replace-text", this, "_wizardReplaceText");
|
||||
}
|
||||
},
|
||||
|
||||
_wizardInsertText(args = {}) {
|
||||
if (args.fieldId === this.fieldId) {
|
||||
this._insertText(args.text, args.options);
|
||||
}
|
||||
},
|
||||
|
||||
_wizardReplaceText(args = {}) {
|
||||
if (args.fieldId === this.fieldId) {
|
||||
this._replaceText(args.oldVal, args.newVal, args.opts = {});
|
||||
}
|
||||
},
|
||||
|
||||
paste(e) {
|
||||
if (!$(".d-editor-input").is(":focus")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isComposer = this.isComposer;
|
||||
let { clipboard, canPasteHtml, canUpload } = clipboardHelpers(e, {
|
||||
siteSettings: this.siteSettings,
|
||||
canUpload: isComposer,
|
||||
});
|
||||
|
||||
let plainText = clipboard.getData("text/plain");
|
||||
let html = clipboard.getData("text/html");
|
||||
let handled = false;
|
||||
|
||||
const { pre, lineVal } = this._getSelected(null, { lineVal: true });
|
||||
const isInlinePasting = pre.match(/[^\n]$/);
|
||||
const isCodeBlock = isInside(pre, /(^|\n)```/g);
|
||||
|
||||
if (
|
||||
plainText &&
|
||||
this.siteSettings.enable_rich_text_paste &&
|
||||
!isInlinePasting &&
|
||||
!isCodeBlock
|
||||
) {
|
||||
plainText = plainText.trim().replace(/\r/g, "");
|
||||
const table = this._extractTable(plainText);
|
||||
if (table) {
|
||||
this.appEvents.trigger("wizard-editor:insert-text", {
|
||||
fieldId: this.fieldId,
|
||||
text: table
|
||||
});
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (canPasteHtml && plainText) {
|
||||
if (isInlinePasting) {
|
||||
canPasteHtml = !(
|
||||
lineVal.match(/^```/) ||
|
||||
isInside(pre, /`/g) ||
|
||||
lineVal.match(/^ /)
|
||||
);
|
||||
} else {
|
||||
canPasteHtml = !isCodeBlock;
|
||||
}
|
||||
}
|
||||
|
||||
if (canPasteHtml && !handled) {
|
||||
let markdown = toMarkdown(html);
|
||||
|
||||
if (!plainText || plainText.length < markdown.length) {
|
||||
if (isInlinePasting) {
|
||||
markdown = markdown.replace(/^#+/, "").trim();
|
||||
markdown = pre.match(/\S$/) ? ` ${markdown}` : markdown;
|
||||
}
|
||||
|
||||
this.appEvents.trigger("composer:insert-text", {
|
||||
fieldId: this.fieldId,
|
||||
text: markdown
|
||||
});
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (handled || (canUpload && !plainText)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
153
assets/javascripts/wizard/initializers/custom-wizard-step.js.es6
Normale Datei
153
assets/javascripts/wizard/initializers/custom-wizard-step.js.es6
Normale Datei
|
@ -0,0 +1,153 @@
|
|||
|
||||
export default {
|
||||
name: "custom-wizard-step",
|
||||
initialize(app) {
|
||||
if (window.location.pathname.indexOf("/w/") < 0) return;
|
||||
|
||||
const StepModel = requirejs("wizard/models/step").default;
|
||||
const StepComponent = requirejs("wizard/components/wizard-step").default;
|
||||
const ajax = requirejs("wizard/lib/ajax").ajax;
|
||||
const cook = requirejs("discourse/plugins/discourse-custom-wizard/wizard/lib/text-lite").cook;
|
||||
|
||||
StepModel.reopen({
|
||||
save() {
|
||||
const wizardId = this.get("wizardId");
|
||||
const fields = {};
|
||||
|
||||
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
|
||||
) {
|
||||
let wizardErrors = [];
|
||||
response.responseJSON.errors.forEach((err) => {
|
||||
if (err.field === wizardId) {
|
||||
wizardErrors.push(err.description);
|
||||
} else if (err.field) {
|
||||
this.fieldError(err.field, err.description);
|
||||
} else if (err) {
|
||||
wizardErrors.push(err);
|
||||
}
|
||||
});
|
||||
if (wizardErrors.length) {
|
||||
this.handleWizardError(wizardErrors.join("\n"));
|
||||
}
|
||||
this.animateInvalidFields();
|
||||
throw response;
|
||||
}
|
||||
|
||||
if (response && response.responseText) {
|
||||
const responseText = response.responseText;
|
||||
const start = responseText.indexOf(">") + 1;
|
||||
const end = responseText.indexOf("plugins");
|
||||
const message = responseText.substring(start, end);
|
||||
this.handleWizardError(message);
|
||||
throw message;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
handleWizardError(message) {
|
||||
this.set("message", {
|
||||
state: "error",
|
||||
text: message,
|
||||
});
|
||||
Ember.run.later(() => this.set("message", null), 6000);
|
||||
},
|
||||
});
|
||||
|
||||
StepComponent.reopen({
|
||||
classNameBindings: ["step.id"],
|
||||
|
||||
animateInvalidFields() {
|
||||
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);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
ensureStartsAtTop: function () {
|
||||
window.scrollTo(0, 0);
|
||||
}.observes("step.id"),
|
||||
|
||||
showQuitButton: function () {
|
||||
const index = this.get("step.index");
|
||||
const required = this.get("wizard.required");
|
||||
return index === 0 && !required;
|
||||
}.property("step.index", "wizard.required"),
|
||||
|
||||
cookedTitle: function () {
|
||||
return cook(this.get("step.title"));
|
||||
}.property("step.title"),
|
||||
|
||||
cookedDescription: function () {
|
||||
return cook(this.get("step.description"));
|
||||
}.property("step.description"),
|
||||
|
||||
bannerImage: function () {
|
||||
const src = this.get("step.banner");
|
||||
if (!src) return;
|
||||
return getUrl(src);
|
||||
}.property("step.banner"),
|
||||
|
||||
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")) {
|
||||
CustomWizard.finished(response);
|
||||
} else {
|
||||
this.sendAction("goNext", response);
|
||||
}
|
||||
})
|
||||
.catch(() => this.animateInvalidFields())
|
||||
.finally(() => this.set("saving", false));
|
||||
},
|
||||
|
||||
keyPress(key) {},
|
||||
|
||||
actions: {
|
||||
quit() {
|
||||
this.get("wizard").skip();
|
||||
},
|
||||
|
||||
done() {
|
||||
this.set("finalStep", true);
|
||||
this.send("nextStep");
|
||||
},
|
||||
|
||||
showMessage(message) {
|
||||
this.sendAction("showMessage", message);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
100
assets/javascripts/wizard/initializers/custom-wizard.js.es6
Normale Datei
100
assets/javascripts/wizard/initializers/custom-wizard.js.es6
Normale Datei
|
@ -0,0 +1,100 @@
|
|||
export default {
|
||||
name: "custom-routes",
|
||||
initialize(app) {
|
||||
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 CustomWizard = requirejs("discourse/plugins/discourse-custom-wizard/wizard/models/custom").default;
|
||||
const getUrl = requirejs("discourse-common/lib/get-url").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 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 container = app.__container__;
|
||||
Discourse.Model = EmberObject.extend();
|
||||
Discourse.__container__ = container;
|
||||
setDefaultOwner(container);
|
||||
registerRawHelpers(RawHandlebars, Handlebars);
|
||||
|
||||
// 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]]];
|
||||
|
||||
return resArray;
|
||||
};
|
||||
}
|
||||
|
||||
Object.keys(Ember.TEMPLATES).forEach((k) => {
|
||||
if (k.indexOf("select-kit") === 0) {
|
||||
let template = Ember.TEMPLATES[k];
|
||||
define(k, () => template);
|
||||
}
|
||||
});
|
||||
|
||||
const targets = ["controller", "component", "route", "model", "adapter"];
|
||||
|
||||
const siteSettings = Wizard.SiteSettings;
|
||||
app.register("site-settings:main", siteSettings, { instantiate: false });
|
||||
createHelperContext({ siteSettings });
|
||||
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"));
|
||||
|
||||
app.register("service:store", Store);
|
||||
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);
|
||||
app.register("session:main", Session.current(), { instantiate: false });
|
||||
targets.forEach((t) => app.inject(t, "session", "session:main"));
|
||||
|
||||
createHelperContext({
|
||||
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"),
|
||||
});
|
||||
|
||||
const session = container.lookup("session:main");
|
||||
const setupData = document.getElementById("data-discourse-setup").dataset;
|
||||
session.set("highlightJsPath", setupData.highlightJsPath);
|
||||
|
||||
Router.reopen({
|
||||
rootURL: getUrl("/w/")
|
||||
});
|
||||
|
||||
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");
|
||||
},
|
||||
model() {},
|
||||
});
|
||||
},
|
||||
};
|
|
@ -1,329 +0,0 @@
|
|||
import { default as computed } from "discourse-common/utils/decorators";
|
||||
import { dasherize } from "@ember/string";
|
||||
|
||||
export default {
|
||||
name: "custom-routes",
|
||||
after: "sniff-capabilities",
|
||||
initialize(app) {
|
||||
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 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 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 container = app.__container__;
|
||||
Discourse.Model = EmberObject.extend();
|
||||
Discourse.__container__ = container;
|
||||
setDefaultOwner(container);
|
||||
registerRawHelpers(RawHandlebars, Handlebars);
|
||||
|
||||
// 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]]];
|
||||
|
||||
return resArray;
|
||||
};
|
||||
|
||||
$.fn.autocomplete = autocomplete;
|
||||
|
||||
Object.keys(Ember.TEMPLATES).forEach((k) => {
|
||||
if (k.indexOf("select-kit") === 0) {
|
||||
let template = Ember.TEMPLATES[k];
|
||||
define(k, () => template);
|
||||
}
|
||||
});
|
||||
|
||||
const targets = ["controller", "component", "route", "model", "adapter"];
|
||||
|
||||
const siteSettings = Wizard.SiteSettings;
|
||||
app.register("site-settings:main", siteSettings, { instantiate: false });
|
||||
createHelperContext({ siteSettings });
|
||||
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"));
|
||||
|
||||
app.register("service:store", Store);
|
||||
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);
|
||||
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/"),
|
||||
});
|
||||
|
||||
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");
|
||||
},
|
||||
|
||||
model() {},
|
||||
});
|
||||
|
||||
WizardStep.reopen({
|
||||
classNameBindings: ["step.id"],
|
||||
|
||||
animateInvalidFields() {
|
||||
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);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
ensureStartsAtTop: function () {
|
||||
window.scrollTo(0, 0);
|
||||
}.observes("step.id"),
|
||||
|
||||
showQuitButton: function () {
|
||||
const index = this.get("step.index");
|
||||
const required = this.get("wizard.required");
|
||||
return index === 0 && !required;
|
||||
}.property("step.index", "wizard.required"),
|
||||
|
||||
cookedTitle: function () {
|
||||
return cook(this.get("step.title"));
|
||||
}.property("step.title"),
|
||||
|
||||
cookedDescription: function () {
|
||||
return cook(this.get("step.description"));
|
||||
}.property("step.description"),
|
||||
|
||||
bannerImage: function () {
|
||||
const src = this.get("step.banner");
|
||||
if (!src) return;
|
||||
return getUrl(src);
|
||||
}.property("step.banner"),
|
||||
|
||||
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")) {
|
||||
CustomWizard.finished(response);
|
||||
} else {
|
||||
this.sendAction("goNext", response);
|
||||
}
|
||||
})
|
||||
.catch(() => this.animateInvalidFields())
|
||||
.finally(() => this.set("saving", false));
|
||||
},
|
||||
|
||||
keyPress(key) {},
|
||||
|
||||
actions: {
|
||||
quit() {
|
||||
this.get("wizard").skip();
|
||||
},
|
||||
|
||||
done() {
|
||||
this.set("finalStep", true);
|
||||
this.send("nextStep");
|
||||
},
|
||||
|
||||
showMessage(message) {
|
||||
this.sendAction("showMessage", message);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
StepModel.reopen({
|
||||
save() {
|
||||
const wizardId = this.get("wizardId");
|
||||
const fields = {};
|
||||
|
||||
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
|
||||
) {
|
||||
let wizardErrors = [];
|
||||
response.responseJSON.errors.forEach((err) => {
|
||||
if (err.field === wizardId) {
|
||||
wizardErrors.push(err.description);
|
||||
} else if (err.field) {
|
||||
this.fieldError(err.field, err.description);
|
||||
} else if (err) {
|
||||
wizardErrors.push(err);
|
||||
}
|
||||
});
|
||||
if (wizardErrors.length) {
|
||||
this.handleWizardError(wizardErrors.join("\n"));
|
||||
}
|
||||
this.animateInvalidFields();
|
||||
throw response;
|
||||
}
|
||||
|
||||
if (response && response.responseText) {
|
||||
const responseText = response.responseText;
|
||||
const start = responseText.indexOf(">") + 1;
|
||||
const end = responseText.indexOf("plugins");
|
||||
const message = responseText.substring(start, end);
|
||||
this.handleWizardError(message);
|
||||
throw message;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
handleWizardError(message) {
|
||||
this.set("message", {
|
||||
state: "error",
|
||||
text: message,
|
||||
});
|
||||
Ember.run.later(() => this.set("message", null), 6000);
|
||||
},
|
||||
});
|
||||
|
||||
WizardField.reopen({
|
||||
classNameBindings: ["field.id"],
|
||||
|
||||
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"),
|
||||
});
|
||||
|
||||
const StandardFieldValidation = [
|
||||
"text",
|
||||
"number",
|
||||
"textarea",
|
||||
"dropdown",
|
||||
"tag",
|
||||
"image",
|
||||
"user_selector",
|
||||
"text_only",
|
||||
"composer",
|
||||
"category",
|
||||
"group",
|
||||
"date",
|
||||
"time",
|
||||
"date_time",
|
||||
];
|
||||
|
||||
FieldModel.reopen({
|
||||
check() {
|
||||
if (this.customCheck) {
|
||||
return this.customCheck();
|
||||
}
|
||||
|
||||
let valid = this.valid;
|
||||
|
||||
if (!this.required) {
|
||||
this.setValid(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
const val = this.get("value");
|
||||
const type = this.get("type");
|
||||
|
||||
if (type === "checkbox") {
|
||||
valid = val;
|
||||
} 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") {
|
||||
valid = true;
|
||||
}
|
||||
|
||||
this.setValid(valid);
|
||||
|
||||
return valid;
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
|
@ -6,18 +6,19 @@
|
|||
markdownOptions=markdownOptions
|
||||
extraButtons=(action "extraButtons")
|
||||
importQuote=(action "importQuote")
|
||||
showUploadModal=showUploadModal
|
||||
showUploadModal=(action "showUploadModal")
|
||||
togglePreview=(action "togglePreview")
|
||||
validation=validation
|
||||
loading=composer.loading
|
||||
showLink=showLink
|
||||
composerEvents=true
|
||||
wizardComposerEvents=true
|
||||
fieldId=fieldId
|
||||
disabled=disableTextarea
|
||||
outletArgs=(hash composer=composer editorType="composer")}}
|
||||
|
||||
{{input
|
||||
class="wizard-composer-upload hidden-upload-field"
|
||||
disabled=uploading
|
||||
disabled=isUploading
|
||||
type="file"
|
||||
multiple=true}}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{{wizard-composer-editor
|
||||
replyPlaceholder=field.placeholder
|
||||
fieldId=field.id
|
||||
composer=composer
|
||||
groupsMentioned=(action "groupsMentioned")
|
||||
cannotSeeMention=(action "cannotSeeMention")
|
||||
|
|
Laden …
In neuem Issue referenzieren