FEATURE: SVG letter avatars (based on @eviltrout's spike)

This commit is contained in:
Régis Hanol 2015-09-11 00:11:48 +02:00
parent 36abc6f9f7
commit cd77465788
8 changed files with 51 additions and 28 deletions

View File

@ -4,6 +4,7 @@ let _splitAvatars;
function defaultAvatar(username) {
const defaultAvatars = Discourse.SiteSettings.default_avatars;
if (defaultAvatars && defaultAvatars.length) {
_splitAvatars = _splitAvatars || defaultAvatars.split("\n");
@ -13,20 +14,13 @@ function defaultAvatar(username) {
}
}
return Discourse.getURLWithCDN("/letter_avatar/" +
username.toLowerCase() +
"/{size}/" +
Discourse.LetterAvatarVersion + ".png");
const extension = Discourse.SiteSettings.svg_letter_avatars ? "svg" : "png";
return Discourse.getURLWithCDN(`/letter_avatar/${username.toLowerCase()}/{size}/${Discourse.LetterAvatarVersion}.${extension}`);
}
export default function(username, uploadedAvatarId) {
if (uploadedAvatarId) {
return Discourse.getURLWithCDN("/user_avatar/" +
Discourse.BaseUrl +
"/" +
username.toLowerCase() +
"/{size}/" +
uploadedAvatarId + ".png");
return Discourse.getURLWithCDN(`/user_avatar/${Discourse.BaseUrl}/${username.toLowerCase()}/{size}/${uploadedAvatarId}.png`);
}
return defaultAvatar(username);
}

View File

@ -3,7 +3,7 @@ require_dependency 'letter_avatar'
class UserAvatarsController < ApplicationController
DOT = Base64.decode64("R0lGODlhAQABALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//wBiZCH5BAEAAA8ALAAAAAABAAEAAAQC8EUAOw==")
skip_before_filter :preload_json, :redirect_to_login_if_required, :check_xhr, :verify_authenticity_token, only: [:show, :show_letter]
skip_before_filter :preload_json, :redirect_to_login_if_required, :check_xhr, :verify_authenticity_token, only: [:show, :show_letter, :show_letter_svg]
def refresh_gravatar
user = User.find_by(username_lower: params[:username].downcase)
@ -19,6 +19,30 @@ class UserAvatarsController < ApplicationController
end
end
def show_letter_svg
params.require(:username)
params.require(:version)
params.require(:size)
no_cookies
size = params[:size].to_i
username = params[:username]
identity = LetterAvatar::Identity.from_username(username)
color = identity.color
svg = <<-SVG
<svg xmlns="http://www.w3.org/2000/svg" width="#{size}" height="#{size}">
<rect width="100%" height="100%" fill="rgb(#{color[0]},#{color[1]},#{color[2]})" />
<text font-size="#{size * 0.7}" font-weight="normal" font-family="Helvetica, sans-serif" fill="#FFF" fill-opacity=".8" text-anchor="middle" x="50%" y="75%">#{username[0].capitalize}</text>
</svg>
SVG
expires_in 1.year, public: true
render inline: svg, content_type: "image/svg+xml"
end
def show_letter
params.require(:username)
params.require(:version)

View File

@ -457,7 +457,7 @@ class User < ActiveRecord::Base
avatar_template = split_avatars[hash.abs % split_avatars.size]
end
else
"#{Discourse.base_uri}/letter_avatar/#{username.downcase}/{size}/#{LetterAvatar.version}.png"
letter_avatar_template(username)
end
end
@ -469,7 +469,8 @@ class User < ActiveRecord::Base
end
def self.letter_avatar_template(username)
"#{Discourse.base_uri}/letter_avatar/#{username.downcase}/{size}/#{LetterAvatar.version}.png"
extension = SiteSetting.svg_letter_avatars ? "svg" : "png"
"#{Discourse.base_uri}/letter_avatar/#{username.downcase}/{size}/#{LetterAvatar.version}.#{extension}"
end
def avatar_template

View File

@ -979,6 +979,8 @@ en:
avatar_sizes: "List of automatically generated avatar sizes."
svg_letter_avatars: "Use SVG for letter avatars"
enable_flash_video_onebox: "Enable embedding of swf and flv (Adobe Flash) links in oneboxes. WARNING: may introduce security risks."
default_invitee_trust_level: "Default trust level (0-4) for invited users."

View File

@ -299,6 +299,7 @@ Discourse::Application.routes.draw do
get "user-badges/:username" => "user_badges#username", constraints: {username: USERNAME_ROUTE_FORMAT}
post "user_avatar/:username/refresh_gravatar" => "user_avatars#refresh_gravatar", constraints: {username: USERNAME_ROUTE_FORMAT}
get "letter_avatar/:username/:size/:version.svg" => "user_avatars#show_letter_svg", format: :svg, constraints: { hostname: /[\w\.-]+/, size: /\d+/, username: USERNAME_ROUTE_FORMAT}
get "letter_avatar/:username/:size/:version.png" => "user_avatars#show_letter", format: false, constraints: { hostname: /[\w\.-]+/, size: /\d+/, username: USERNAME_ROUTE_FORMAT}
get "user_avatar/:hostname/:username/:size/:version.png" => "user_avatars#show", format: false, constraints: { hostname: /[\w\.-]+/, size: /\d+/, username: USERNAME_ROUTE_FORMAT }

View File

@ -572,6 +572,9 @@ files:
avatar_sizes:
default: '20|25|32|45|60|120'
type: list
svg_letter_avatars:
default: false
client: true
trust:
default_trust_level:

View File

@ -30,7 +30,6 @@ module Email
# images
@fragment.css('img').each do |img|
next if img['class'] == 'site-logo'
if img['class'] == "emoji" || img['src'] =~ /plugins\/emoji/
@ -58,7 +57,6 @@ module Email
# attachments
@fragment.css('a.attachment').each do |a|
# ensure all urls are absolute
if a['href'] =~ /^\/[^\/]/
a['href'] = "#{Discourse.base_url}#{a['href']}"

View File

@ -7,20 +7,20 @@ class LetterAvatar
FULLSIZE = 120 * 3
POINTSIZE = 280
class << self
class Identity
attr_accessor :color, :letter
class Identity
attr_accessor :color, :letter
def self.from_username(username)
identity = new
identity.color = LetterAvatar::COLORS[
Digest::MD5.hexdigest(username)[0...15].to_i(16) % LetterAvatar::COLORS.length
]
identity.letter = username[0].upcase
identity
end
def self.from_username(username)
identity = new
identity.color = LetterAvatar::COLORS[
Digest::MD5.hexdigest(username)[0...15].to_i(16) % LetterAvatar::COLORS.length
]
identity.letter = username[0].upcase
identity
end
end
class << self
def version
"#{VERSION}_#{image_magick_version}"
@ -32,7 +32,7 @@ class LetterAvatar
def generate(username, size, opts = nil)
DistributedMutex.synchronize("letter_avatar_#{version}_#{username}") do
identity = Identity.from_username(username)
identity = LetterAvatar::Identity.from_username(username)
cache = true
cache = false if opts && opts[:cache] == false