DEV: Drop jQuery file uploader and old upload components (#15376)

This commit removes jQuery file uploader from Discourse,
completing the transition to Uppy. The image-uploader
and UploadMixin components are also removed in this commit
as they have already been replaced and are the only things
using jQuery file upload.

            .-'~~~`-.
          .'         `.
          |  R  I  P  |
          |   jquery  |
          |    file   |
          |   upload  |
          |           |
        \\| 2013-2021 |//
        -----------------
This commit is contained in:
Martin Brennan 2021-12-22 08:59:44 +10:00 committed by GitHub
parent 72ad5bf8bd
commit 667a8a63b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 0 additions and 2194 deletions

View File

@ -1,135 +0,0 @@
import Component from "@ember/component";
import deprecated from "discourse-common/lib/deprecated";
import UploadMixin from "discourse/mixins/upload";
import { ajax } from "discourse/lib/ajax";
import discourseComputed from "discourse-common/utils/decorators";
import { getURLWithCDN } from "discourse-common/lib/get-url";
import { isEmpty } from "@ember/utils";
import lightbox from "discourse/lib/lightbox";
import { next } from "@ember/runloop";
import { popupAjaxError } from "discourse/lib/ajax-error";
export default Component.extend(UploadMixin, {
classNames: ["image-uploader"],
loadingLightbox: false,
init() {
this._super(...arguments);
// TODO (martin) (2022-01-22) Remove this component.
deprecated(
"image-uploader will be removed in a future version, use uppy-image-uploader instead (the API is the same)"
);
this._applyLightbox();
},
willDestroyElement() {
this._super(...arguments);
const elem = $("a.lightbox");
if (elem && typeof elem.magnificPopup === "function") {
$("a.lightbox").magnificPopup("close");
}
},
@discourseComputed("imageUrl", "placeholderUrl")
showingPlaceholder(imageUrl, placeholderUrl) {
return !imageUrl && placeholderUrl;
},
@discourseComputed("placeholderUrl")
placeholderStyle(url) {
if (isEmpty(url)) {
return "".htmlSafe();
}
return `background-image: url(${url})`.htmlSafe();
},
@discourseComputed("imageUrl")
imageCDNURL(url) {
if (isEmpty(url)) {
return "".htmlSafe();
}
return getURLWithCDN(url);
},
@discourseComputed("imageCDNURL")
backgroundStyle(url) {
return `background-image: url(${url})`.htmlSafe();
},
@discourseComputed("imageUrl")
imageBaseName(imageUrl) {
if (isEmpty(imageUrl)) {
return;
}
return imageUrl.split("/").slice(-1)[0];
},
validateUploadedFilesOptions() {
return { imagesOnly: true };
},
uploadDone(upload) {
this.setProperties({
imageUrl: upload.url,
imageId: upload.id,
imageFilesize: upload.human_filesize,
imageFilename: upload.original_filename,
imageWidth: upload.width,
imageHeight: upload.height,
});
this._applyLightbox();
if (this.onUploadDone) {
this.onUploadDone(upload);
}
},
_openLightbox() {
next(() =>
$(this.element.querySelector("a.lightbox")).magnificPopup("open")
);
},
_applyLightbox() {
if (this.imageUrl) {
next(() => lightbox(this.element, this.siteSettings));
}
},
actions: {
toggleLightbox() {
if (this.imageFilename) {
this._openLightbox();
} else {
this.set("loadingLightbox", true);
ajax(`/uploads/lookup-metadata`, {
type: "POST",
data: { url: this.imageUrl },
})
.then((json) => {
this.setProperties({
imageFilename: json.original_filename,
imageFilesize: json.human_filesize,
imageWidth: json.width,
imageHeight: json.height,
});
this._openLightbox();
this.set("loadingLightbox", false);
})
.catch(popupAjaxError);
}
},
trash() {
this.setProperties({ imageUrl: null, imageId: null });
if (this.onUploadDeleted) {
this.onUploadDeleted();
}
},
},
});

View File

@ -1,134 +0,0 @@
import {
displayErrorForUpload,
validateUploadedFiles,
} from "discourse/lib/uploads";
import I18n from "I18n";
import Mixin from "@ember/object/mixin";
import bootbox from "bootbox";
import { deepMerge } from "discourse-common/lib/object";
import getUrl from "discourse-common/lib/get-url";
import { on } from "@ember/object/evented";
import { warn } from "@ember/debug";
export default Mixin.create({
uploading: false,
uploadProgress: 0,
uploadDone() {
warn("You should implement `uploadDone`", {
id: "discourse.upload.missing-upload-done",
});
},
validateUploadedFilesOptions() {
return {};
},
calculateUploadUrl() {
return (
getUrl(this.getWithDefault("uploadUrl", "/uploads")) +
".json?client_id=" +
(this.messageBus && this.messageBus.clientId) +
this.uploadUrlParams
);
},
uploadUrlParams: "",
uploadOptions() {
return {};
},
_initialize: on("didInsertElement", function () {
const $upload = $(this.element);
const reset = () => {
this.setProperties({ uploading: false, uploadProgress: 0 });
document.getElementsByClassName("hidden-upload-field")[0].value = "";
};
const maxFiles = this.getWithDefault(
"maxFiles",
this.siteSettings.simultaneous_uploads
);
$upload.on("fileuploaddone", (e, data) => {
let upload = data.result;
this.uploadDone(upload);
reset();
});
$upload.fileupload(
deepMerge(
{
url: this.calculateUploadUrl(),
dataType: "json",
replaceFileInput: false,
dropZone: $upload,
pasteZone: $upload,
},
this.uploadOptions()
)
);
$upload.on("fileuploaddrop", (e, data) => {
if (maxFiles > 0 && data.files.length > maxFiles) {
bootbox.alert(
I18n.t("post.errors.too_many_dragged_and_dropped_files", {
count: maxFiles,
})
);
return false;
} else {
return true;
}
});
$upload.on("fileuploadsubmit", (e, data) => {
const opts = deepMerge(
{
bypassNewUserRestriction: true,
user: this.currentUser,
siteSettings: this.siteSettings,
},
this.validateUploadedFilesOptions()
);
const isValid = validateUploadedFiles(data.files, opts);
const type = this.type;
let form = type ? { type } : {};
if (this.data) {
form = Object.assign(form, this.data);
}
data.formData = form;
this.setProperties({ uploadProgress: 0, uploading: isValid });
return isValid;
});
$upload.on("fileuploadprogressall", (e, data) => {
if (this.isDestroying || this.isDestroyed) {
return;
}
const progress = parseInt((data.loaded / data.total) * 100, 10);
this.set("uploadProgress", progress);
});
$upload.on("fileuploadfail", (e, data) => {
displayErrorForUpload(data, this.siteSettings, data.files[0].name);
reset();
});
}),
_destroy: on("willDestroyElement", function () {
this.messageBus && this.messageBus.unsubscribe("/uploads/" + this.type);
const $upload = $(this.element);
try {
$upload.fileupload("destroy");
} catch (e) {
/* wasn't initialized yet */
}
$upload.off();
}),
});

View File

@ -1,45 +0,0 @@
<div class="uploaded-image-preview input-xxlarge" style={{backgroundStyle}}>
{{#if showingPlaceholder}}
<div class="placeholder-overlay" style={{placeholderStyle}}></div>
{{/if}}
<div class="image-upload-controls">
<label class="btn btn-default pad-left no-text {{if uploading "disabled"}}">
{{d-icon "far-image"}}
<input class="hidden-upload-field" disabled={{uploading}} type="file" accept="image/*">
</label>
{{#if imageUrl}}
{{d-button
action=(action "trash")
class="btn-danger pad-left no-text"
icon="far-trash-alt"
type="button"
}}
{{d-button
icon="discourse-expand"
title="expand"
type="button"
class="image-uploader-lightbox-btn no-text"
action=(action "toggleLightbox")
disabled=loadingLightbox
}}
{{/if}}
<span class="btn {{unless uploading "hidden"}}">{{i18n "upload_selector.uploading"}} {{uploadProgress}}%</span>
</div>
{{#if imageUrl}}
<a class="lightbox"
href={{imageCDNURL}}
title={{imageFilename}}
rel="nofollow ugc noopener">
<div class="meta">
<span class="informations">
{{imageWidth}}x{{imageHeight}} {{imageFilesize}}
</span>
</div>
</a>
{{/if}}
</div>

View File

@ -42,8 +42,6 @@ module.exports = function (defaults) {
app.import(vendorJs + "bootbox.js");
app.import(vendorJs + "bootstrap-modal.js");
app.import(vendorJs + "jquery.ui.widget.js");
app.import(vendorJs + "jquery.fileupload.js");
app.import(vendorJs + "jquery.fileupload-process.js");
app.import(vendorJs + "caret_position.js");
app.import("node_modules/ember-source/dist/ember-template-compiler.js", {
type: "test",

View File

@ -1,97 +0,0 @@
import componentTest, {
setupRenderingTest,
} from "discourse/tests/helpers/component-test";
import {
count,
discourseModule,
exists,
} from "discourse/tests/helpers/qunit-helpers";
import { click } from "@ember/test-helpers";
import hbs from "htmlbars-inline-precompile";
discourseModule("Integration | Component | image-uploader", function (hooks) {
setupRenderingTest(hooks);
componentTest("with image", {
template: hbs`
{{image-uploader imageUrl='/images/avatar.png' placeholderUrl='/not/used.png'}}
`,
async test(assert) {
assert.strictEqual(
count(".d-icon-far-image"),
1,
"it displays the upload icon"
);
assert.strictEqual(
count(".d-icon-far-trash-alt"),
1,
"it displays the trash icon"
);
assert.ok(
!exists(".placeholder-overlay"),
"it does not display the placeholder image"
);
await click(".image-uploader-lightbox-btn");
assert.strictEqual(
document.querySelectorAll(".mfp-container").length,
1,
"it displays the image lightbox"
);
},
});
componentTest("without image", {
template: hbs`{{image-uploader}}`,
test(assert) {
assert.strictEqual(
count(".d-icon-far-image"),
1,
"it displays the upload icon"
);
assert.ok(
!exists(".d-icon-far-trash-alt"),
"it does not display trash icon"
);
assert.ok(
!exists(".image-uploader-lightbox-btn"),
"it does not display the button to open image lightbox"
);
},
});
componentTest("with placeholder", {
template: hbs`{{image-uploader placeholderUrl='/images/avatar.png'}}`,
test(assert) {
assert.strictEqual(
count(".d-icon-far-image"),
1,
"it displays the upload icon"
);
assert.ok(
!exists(".d-icon-far-trash-alt"),
"it does not display trash icon"
);
assert.ok(
!exists(".image-uploader-lightbox-btn"),
"it does not display the button to open image lightbox"
);
assert.strictEqual(
count(".placeholder-overlay"),
1,
"it displays the placeholder image"
);
},
});
});

View File

@ -18,9 +18,6 @@
//= require popper.js
//= require bootstrap-modal.js
//= require caret_position
//= require jquery.fileupload.js
//= require jquery.iframe-transport.js
//= require jquery.fileupload-process.js
//= require jquery.sortable.js
//= require lodash.js
//= require itsatrap.js

View File

@ -11,9 +11,6 @@
//= require popper.js
//= require bootstrap-modal.js
//= require caret_position
//= require jquery.fileupload.js
//= require jquery.iframe-transport.js
//= require jquery.fileupload-process.js
//= require jquery.sortable.js
//= require lodash.js
//= require itsatrap.js

View File

@ -1,7 +1,6 @@
//= require ember_jquery
//= require template_include.js
//= require jquery.ui.widget.js
//= require jquery.fileupload.js
//= require uppy.js
//= require bootstrap-modal.js
//= require bootbox.js

View File

@ -103,14 +103,6 @@ def dependencies
}, {
source: '@highlightjs/cdn-assets/.',
destination: 'highlightjs'
}, {
source: 'blueimp-file-upload/js/jquery.fileupload.js',
}, {
source: 'blueimp-file-upload/js/jquery.iframe-transport.js',
}, {
source: 'blueimp-file-upload/js/jquery.fileupload-process.js',
}, {
source: 'blueimp-file-upload/js/vendor/jquery.ui.widget.js',
}, {
source: 'jquery/dist/jquery.js'
}, {

View File

@ -1,169 +0,0 @@
/*
* jQuery File Upload Processing Plugin
* https://github.com/blueimp/jQuery-File-Upload
*
* Copyright 2012, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* https://opensource.org/licenses/MIT
*/
/* global define, require */
(function (factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
// Register as an anonymous AMD module:
define(['jquery', './jquery.fileupload'], factory);
} else if (typeof exports === 'object') {
// Node/CommonJS:
factory(require('jquery'), require('./jquery.fileupload'));
} else {
// Browser globals:
factory(window.jQuery);
}
})(function ($) {
'use strict';
var originalAdd = $.blueimp.fileupload.prototype.options.add;
// The File Upload Processing plugin extends the fileupload widget
// with file processing functionality:
$.widget('blueimp.fileupload', $.blueimp.fileupload, {
options: {
// The list of processing actions:
processQueue: [
/*
{
action: 'log',
type: 'debug'
}
*/
],
add: function (e, data) {
var $this = $(this);
data.process(function () {
return $this.fileupload('process', data);
});
originalAdd.call(this, e, data);
}
},
processActions: {
/*
log: function (data, options) {
console[options.type](
'Processing "' + data.files[data.index].name + '"'
);
}
*/
},
_processFile: function (data, originalData) {
var that = this,
// eslint-disable-next-line new-cap
dfd = $.Deferred().resolveWith(that, [data]),
chain = dfd.promise();
this._trigger('process', null, data);
$.each(data.processQueue, function (i, settings) {
var func = function (data) {
if (originalData.errorThrown) {
// eslint-disable-next-line new-cap
return $.Deferred().rejectWith(that, [originalData]).promise();
}
return that.processActions[settings.action].call(
that,
data,
settings
);
};
chain = chain.then(func, settings.always && func);
});
chain
.done(function () {
that._trigger('processdone', null, data);
that._trigger('processalways', null, data);
})
.fail(function () {
that._trigger('processfail', null, data);
that._trigger('processalways', null, data);
});
return chain;
},
// Replaces the settings of each processQueue item that
// are strings starting with an "@", using the remaining
// substring as key for the option map,
// e.g. "@autoUpload" is replaced with options.autoUpload:
_transformProcessQueue: function (options) {
var processQueue = [];
$.each(options.processQueue, function () {
var settings = {},
action = this.action,
prefix = this.prefix === true ? action : this.prefix;
$.each(this, function (key, value) {
if ($.type(value) === 'string' && value.charAt(0) === '@') {
settings[key] =
options[
value.slice(1) ||
(prefix
? prefix + key.charAt(0).toUpperCase() + key.slice(1)
: key)
];
} else {
settings[key] = value;
}
});
processQueue.push(settings);
});
options.processQueue = processQueue;
},
// Returns the number of files currently in the processsing queue:
processing: function () {
return this._processing;
},
// Processes the files given as files property of the data parameter,
// returns a Promise object that allows to bind callbacks:
process: function (data) {
var that = this,
options = $.extend({}, this.options, data);
if (options.processQueue && options.processQueue.length) {
this._transformProcessQueue(options);
if (this._processing === 0) {
this._trigger('processstart');
}
$.each(data.files, function (index) {
var opts = index ? $.extend({}, options) : options,
func = function () {
if (data.errorThrown) {
// eslint-disable-next-line new-cap
return $.Deferred().rejectWith(that, [data]).promise();
}
return that._processFile(opts, data);
};
opts.index = index;
that._processing += 1;
that._processingQueue = that._processingQueue
.then(func, func)
.always(function () {
that._processing -= 1;
if (that._processing === 0) {
that._trigger('processstop');
}
});
});
}
return this._processingQueue;
},
_create: function () {
this._super();
this._processing = 0;
// eslint-disable-next-line new-cap
this._processingQueue = $.Deferred().resolveWith(this).promise();
}
});
});

File diff suppressed because it is too large Load Diff