FEATURE: image uploads now have short urls
Shorten all image uploads to use short urls, this is the client side implementation.
This commit is contained in:
parent
605653a369
commit
d7a2584c6e
|
@ -13,6 +13,9 @@ import { tinyAvatar,
|
|||
displayErrorForUpload,
|
||||
getUploadMarkdown,
|
||||
validateUploadedFiles } from 'discourse/lib/utilities';
|
||||
import { lookupCachedUploadUrl,
|
||||
lookupUncachedUploadUrls,
|
||||
cacheShortUploadUrl } from 'pretty-text/image-short-url';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['wmd-controls'],
|
||||
|
@ -191,6 +194,24 @@ export default Ember.Component.extend({
|
|||
$oneboxes.each((_, o) => load(o, refresh, ajax, this.currentUser.id));
|
||||
},
|
||||
|
||||
_loadShortUrls($images) {
|
||||
const urls = _.map($images, img => $(img).data('orig-src'));
|
||||
lookupUncachedUploadUrls(urls, ajax).then(() => this._loadCachedShortUrls($images));
|
||||
},
|
||||
|
||||
_loadCachedShortUrls($images) {
|
||||
$images.each((idx, image) => {
|
||||
let $image = $(image);
|
||||
let url = lookupCachedUploadUrl($image.data('orig-src'));
|
||||
if (url) {
|
||||
$image.removeAttr('data-orig-src');
|
||||
if (url !== "missing") {
|
||||
$image.attr('src', url);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_warnMentionedGroups($preview) {
|
||||
Ember.run.scheduleOnce('afterRender', () => {
|
||||
var found = this.get('warnedGroupMentions') || [];
|
||||
|
@ -312,6 +333,7 @@ export default Ember.Component.extend({
|
|||
if (upload && upload.url) {
|
||||
if (!this._xhr || !this._xhr._userCancelled) {
|
||||
const markdown = getUploadMarkdown(upload);
|
||||
cacheShortUploadUrl(upload.short_url, upload.url);
|
||||
this.appEvents.trigger('composer:replace-text', uploadPlaceholder, markdown);
|
||||
this._resetUpload(false);
|
||||
} else {
|
||||
|
@ -579,6 +601,19 @@ export default Ember.Component.extend({
|
|||
Ember.run.debounce(this, this._loadOneboxes, $oneboxes, 450);
|
||||
}
|
||||
|
||||
// Short upload urls
|
||||
let $shortUploadUrls = $('img[data-orig-src]');
|
||||
|
||||
if ($shortUploadUrls.length > 0) {
|
||||
this._loadCachedShortUrls($shortUploadUrls);
|
||||
|
||||
$shortUploadUrls = $('img[data-orig-src]');
|
||||
if ($shortUploadUrls.length > 0) {
|
||||
// this is carefully batched so we can do an leading debounce (trigger right away)
|
||||
Ember.run.debounce(this, this._loadShortUrls, $shortUploadUrls, 450, true);
|
||||
}
|
||||
}
|
||||
|
||||
let inline = {};
|
||||
$('a.inline-onebox-loading', $preview).each(function(index, link) {
|
||||
let $link = $(link);
|
||||
|
|
|
@ -298,7 +298,7 @@ export function getUploadMarkdown(upload) {
|
|||
if (isAnImage(upload.original_filename)) {
|
||||
const split = upload.original_filename.split('.');
|
||||
const name = split[split.length-2];
|
||||
return `![${name}|${upload.width}x${upload.height}](${upload.url})`;
|
||||
return `![${name}|${upload.width}x${upload.height}](${upload.short_url || upload.url})`;
|
||||
} else if (!Discourse.SiteSettings.prevent_anons_from_downloading_files && (/\.(mov|mp4|webm|ogv|mp3|ogg|wav|m4a)$/i).test(upload.original_filename)) {
|
||||
return uploadLocation(upload.url);
|
||||
} else {
|
||||
|
|
|
@ -10,3 +10,4 @@
|
|||
//= require ./pretty-text/sanitizer
|
||||
//= require ./pretty-text/oneboxer
|
||||
//= require ./pretty-text/inline-oneboxer
|
||||
//= require ./pretty-text/image-short-url
|
||||
|
|
|
@ -35,7 +35,8 @@ function rule(state) {
|
|||
|
||||
if (images.length > 0) {
|
||||
let srcList = images.map(([token, srcIndex]) => token.attrs[srcIndex][1]);
|
||||
let longUrls = state.md.options.discourse.lookupImageUrls(srcList);
|
||||
let lookup = state.md.options.discourse.lookupImageUrls;
|
||||
let longUrls = (lookup && lookup(srcList)) || {};
|
||||
|
||||
images.forEach(([token, srcIndex]) => {
|
||||
let origSrc = token.attrs[srcIndex][1];
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
let _cache = {};
|
||||
|
||||
export function lookupCachedUploadUrl(shortUrl) {
|
||||
return _cache[shortUrl];
|
||||
}
|
||||
|
||||
export function lookupUncachedUploadUrls(urls, ajax) {
|
||||
return ajax('/uploads/lookup-urls', { method: 'POST', data: { short_urls: urls } })
|
||||
.then(uploads => {
|
||||
uploads.forEach(upload => _cache[upload.short_url] = upload.url);
|
||||
urls.forEach(url => _cache[url] = _cache[url] || "missing");
|
||||
return uploads;
|
||||
});
|
||||
}
|
||||
|
||||
export function cacheShortUploadUrl(shortUrl, url) {
|
||||
_cache[shortUrl] = url;
|
||||
}
|
|
@ -20,19 +20,32 @@ class UploadsController < ApplicationController
|
|||
|
||||
if params[:synchronous] && (current_user.staff? || is_api?)
|
||||
data = create_upload(file, url, type, for_private_message, pasted)
|
||||
render json: data.as_json
|
||||
render json: serialize_upload(data)
|
||||
else
|
||||
Scheduler::Defer.later("Create Upload") do
|
||||
begin
|
||||
data = create_upload(file, url, type, for_private_message, pasted)
|
||||
ensure
|
||||
MessageBus.publish("/uploads/#{type}", (data || {}).as_json, client_ids: [params[:client_id]])
|
||||
MessageBus.publish("/uploads/#{type}", serialize_upload(data), client_ids: [params[:client_id]])
|
||||
end
|
||||
end
|
||||
render json: success_json
|
||||
end
|
||||
end
|
||||
|
||||
def lookup_urls
|
||||
params.permit(short_urls: [])
|
||||
uploads = []
|
||||
|
||||
if (params[:short_urls] && params[:short_urls].length > 0)
|
||||
PrettyText::Helpers.lookup_image_urls(params[:short_urls]).each do |short_url, url|
|
||||
uploads << { short_url: short_url, url: url }
|
||||
end
|
||||
end
|
||||
|
||||
render json: uploads.to_json
|
||||
end
|
||||
|
||||
def show
|
||||
return render_404 if !RailsMultisite::ConnectionManagement.has_db?(params[:site])
|
||||
|
||||
|
@ -57,6 +70,13 @@ class UploadsController < ApplicationController
|
|||
|
||||
protected
|
||||
|
||||
def serialize_upload(data)
|
||||
# as_json.as_json is not a typo... as_json in AM serializer returns keys as symbols, we need them
|
||||
# as strings here
|
||||
serialized = UploadSerializer.new(data, root: nil).as_json.as_json if Upload === data
|
||||
serialized ||= (data || {}).as_json
|
||||
end
|
||||
|
||||
def render_404
|
||||
raise Discourse::NotFound
|
||||
end
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
class UploadSerializer < ApplicationSerializer
|
||||
|
||||
attributes :id, :url, :original_filename, :filesize, :width, :height
|
||||
|
||||
attributes :id,
|
||||
:url,
|
||||
:original_filename,
|
||||
:filesize,
|
||||
:width,
|
||||
:height,
|
||||
:extension,
|
||||
:short_url,
|
||||
:retain_hours
|
||||
end
|
||||
|
|
|
@ -414,6 +414,7 @@ Discourse::Application.routes.draw do
|
|||
get "stylesheets/:name.css" => "stylesheets#show", constraints: { name: /[-a-z0-9_]+/ }
|
||||
|
||||
post "uploads" => "uploads#create"
|
||||
post "uploads/lookup-urls" => "uploads#lookup_urls"
|
||||
|
||||
# used to download original images
|
||||
get "uploads/:site/:sha(.:extension)" => "uploads#show", constraints: { site: /\w+/, sha: /\h{40}/, extension: /[a-z0-9\.]+/i }
|
||||
|
|
|
@ -36,6 +36,13 @@ describe UploadsController do
|
|||
xhr :post, :create, file: logo, type: "super \# long \//\\ type with \\. $%^&*( chars" * 5
|
||||
end
|
||||
|
||||
it 'can look up long urls' do
|
||||
upload = Fabricate(:upload)
|
||||
xhr :post, :lookup_urls, short_urls: [upload.short_url]
|
||||
result = JSON.parse(response.body)
|
||||
expect(result[0]["url"]).to eq(upload.url)
|
||||
end
|
||||
|
||||
it 'is successful with an image' do
|
||||
Jobs.expects(:enqueue).with(:create_avatar_thumbnails, anything)
|
||||
|
||||
|
@ -78,6 +85,7 @@ describe UploadsController do
|
|||
|
||||
expect(response.status).to eq 200
|
||||
expect(json["id"]).to be
|
||||
expect(json["short_url"]).to eq("upload://qUm0DGR49PAZshIi7HxMd3cAlzn.png")
|
||||
end
|
||||
|
||||
it 'correctly sets retain_hours for admins' do
|
||||
|
|
Loading…
Reference in New Issue