add a site setting for allowing animated avatars
This commit is contained in:
parent
663adde90e
commit
43a8bff652
|
@ -291,6 +291,54 @@ Discourse.Utilities = {
|
|||
}
|
||||
// otherwise, display a generic error message
|
||||
bootbox.alert(I18n.t('post.errors.upload'));
|
||||
},
|
||||
|
||||
/**
|
||||
Crop an image to be used as avatar.
|
||||
Simulate the "centered square thumbnail" generation done server-side.
|
||||
Uses only the first frame of animated gifs when they are disabled.
|
||||
|
||||
@method cropAvatar
|
||||
@param {String} url The url of the avatar
|
||||
@param {String} fileType The file type of the uploaded file
|
||||
@returns {Ember.Deferred} a promise that will eventually be the cropped avatar.
|
||||
**/
|
||||
cropAvatar: function(url, fileType) {
|
||||
if (Discourse.SiteSettings.allow_animated_avatars && fileType === "image/gif") {
|
||||
// can't crop animated gifs... let the browser stretch the gif
|
||||
return Ember.RSVP.resolve(url);
|
||||
} else {
|
||||
return Ember.Deferred.promise(function(promise) {
|
||||
var image = document.createElement("img");
|
||||
// this event will be fired as soon as the image is loaded
|
||||
image.onload = function(e) {
|
||||
var img = e.target;
|
||||
// computes the dimension & position (x, y) of the largest square we can fit in the image
|
||||
var width = img.width, height = img.height, dimension, center, x, y;
|
||||
if (width <= height) {
|
||||
dimension = width;
|
||||
center = height / 2;
|
||||
x = 0;
|
||||
y = center - (dimension / 2);
|
||||
} else {
|
||||
dimension = height;
|
||||
center = width / 2;
|
||||
x = center - (dimension / 2);
|
||||
y = 0;
|
||||
}
|
||||
// set the size of the canvas to the maximum available size for avatars (browser will take care of downsizing the image)
|
||||
var canvas = document.createElement("canvas");
|
||||
var size = Discourse.Utilities.getRawSize(Discourse.Utilities.translateSize("huge"));
|
||||
canvas.height = canvas.width = size;
|
||||
// draw the image into the canvas
|
||||
canvas.getContext("2d").drawImage(img, x, y, dimension, dimension, 0, 0, size, size);
|
||||
// retrieve the image from the canvas
|
||||
promise.resolve(canvas.toDataURL(fileType));
|
||||
};
|
||||
// launch the onload event
|
||||
image.src = url;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -51,11 +51,17 @@ Discourse.AvatarSelectorView = Discourse.ModalBodyView.extend({
|
|||
|
||||
// when the upload is successful
|
||||
$upload.on("fileuploaddone", function (e, data) {
|
||||
// set some properties
|
||||
// indicates the users is using an uploaded avatar
|
||||
view.get("controller").setProperties({
|
||||
has_uploaded_avatar: true,
|
||||
use_uploaded_avatar: true,
|
||||
uploaded_avatar_template: data.result.url
|
||||
use_uploaded_avatar: true
|
||||
});
|
||||
// in order to be as much responsive as possible, we're cheating a bit here
|
||||
// indeed, the server gives us back the url to the file we've just uploaded
|
||||
// often, this file is not a square, so we need to crop it properly
|
||||
// this will also capture the first frame of animated avatars when they're not allowed
|
||||
Discourse.Utilities.cropAvatar(data.result.url, data.files[0].type).then(function(avatarTemplate) {
|
||||
view.get("controller").set("uploaded_avatar_template", avatarTemplate);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ class OptimizedImage < ActiveRecord::Base
|
|||
temp_file = Tempfile.new(["discourse-thumbnail", File.extname(original_path)])
|
||||
temp_path = temp_file.path
|
||||
|
||||
if ImageSorcery.new(original_path).convert(temp_path, resize: "#{width}x#{height}")
|
||||
if ImageSorcery.new("#{original_path}[0]").convert(temp_path, resize: "#{width}x#{height}")
|
||||
thumbnail = OptimizedImage.create!(
|
||||
upload_id: upload.id,
|
||||
sha1: Digest::SHA1.file(temp_path).hexdigest,
|
||||
|
|
|
@ -245,6 +245,7 @@ class SiteSetting < ActiveRecord::Base
|
|||
setting(:username_change_period, 3) # days
|
||||
|
||||
client_setting(:allow_uploaded_avatars, true)
|
||||
client_setting(:allow_animated_avatars, false)
|
||||
|
||||
def self.generate_api_key!
|
||||
self.api_key = SecureRandom.hex(32)
|
||||
|
|
|
@ -665,7 +665,8 @@ en:
|
|||
delete_all_posts_max: "The maximum number of posts that can be deleted at once with the Delete All Posts button. If a user has more than this many posts, the posts cannot all be deleted at once and the user can't be deleted."
|
||||
username_change_period: "The number of days after registration that accounts can change their username."
|
||||
|
||||
allow_uploaded_avatars: "Allow support for uploaded avatars"
|
||||
allow_uploaded_avatars: "Allow users to upload their custom avatars"
|
||||
allow_animated_avatars: "Allow users to use animated gif for avatars"
|
||||
|
||||
notification_types:
|
||||
mentioned: "%{display_username} mentioned you in %{link}"
|
||||
|
|
|
@ -15,6 +15,10 @@ module Jobs
|
|||
Discourse.store.path_for(upload)
|
||||
end
|
||||
|
||||
# we'll extract the first frame when it's a gif
|
||||
source = original_path
|
||||
source << "[0]" unless SiteSetting.allow_animated_avatars
|
||||
|
||||
[120, 45, 32, 25, 20].each do |s|
|
||||
# handle retina too
|
||||
[s, s * 2].each do |size|
|
||||
|
@ -22,7 +26,7 @@ module Jobs
|
|||
temp_file = Tempfile.new(["discourse-avatar", File.extname(original_path)])
|
||||
temp_path = temp_file.path
|
||||
# create a centered square thumbnail
|
||||
if ImageSorcery.new(original_path).convert(temp_path, gravity: "center", thumbnail: "#{size}x#{size}^", extent: "#{size}x#{size}", background: "transparent")
|
||||
if ImageSorcery.new(source).convert(temp_path, gravity: "center", thumbnail: "#{size}x#{size}^", extent: "#{size}x#{size}", background: "transparent")
|
||||
Discourse.store.store_avatar(temp_file, upload, size)
|
||||
end
|
||||
# close && remove temp file
|
||||
|
|
|
@ -132,3 +132,16 @@ test("avatarImg", function() {
|
|||
blank(Discourse.Utilities.avatarImg({avatarTemplate: "", size: 'tiny'}),
|
||||
"it doesn't render avatars for invalid avatar template");
|
||||
});
|
||||
|
||||
module("Discourse.Utilities.cropAvatar with animated avatars", {
|
||||
setup: function() { Discourse.SiteSettings.allow_animated_avatars = true; }
|
||||
});
|
||||
|
||||
asyncTestDiscourse("cropAvatar", function() {
|
||||
expect(1);
|
||||
|
||||
Discourse.Utilities.cropAvatar("/path/to/avatar.gif", "image/gif").then(function(avatarTemplate) {
|
||||
equal(avatarTemplate, "/path/to/avatar.gif", "returns the url to the gif when animated gif are enabled");
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue