From 1ad9691e49fcb657abab42bc94fd76d37cd68db3 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Wed, 1 Dec 2021 15:19:14 +0800 Subject: [PATCH] COMPATIBILITY: support new uploader --- assets/javascripts/wizard-custom.js | 1 - .../components/wizard-composer-editor.js.es6 | 250 ++++-------------- .../components/wizard-field-composer.js.es6 | 2 +- .../wizard/initializers/custom-wizard.js.es6 | 12 +- .../components/wizard-composer-editor.hbs | 26 +- plugin.rb | 2 +- 6 files changed, 68 insertions(+), 225 deletions(-) diff --git a/assets/javascripts/wizard-custom.js b/assets/javascripts/wizard-custom.js index 9214df5a..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 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-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 c7c2b8d3..f2095827 100644 --- a/assets/javascripts/wizard/initializers/custom-wizard.js.es6 +++ b/assets/javascripts/wizard/initializers/custom-wizard.js.es6 @@ -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/plugin.rb b/plugin.rb index 752b6b92..6f68ae3b 100644 --- a/plugin.rb +++ b/plugin.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # name: discourse-custom-wizard # about: Create custom wizards -# version: 1.15.4 +# version: 1.15.5 # authors: Angus McLeod # url: https://github.com/paviliondev/discourse-custom-wizard # contact emails: angus@thepavilion.io