Switch avatars to be built out of CSS and only overlayed with the gravatar when a non-default exists
This commit is contained in:
parent
2d8d0c6fd3
commit
27a9b84587
94 changed files with 663 additions and 303 deletions
|
@ -1,4 +1,5 @@
|
|||
import hashlib
|
||||
import math
|
||||
|
||||
class Avatar(object):
|
||||
def __init__(self, app=None):
|
||||
|
@ -7,8 +8,7 @@ class Avatar(object):
|
|||
|
||||
def _init_app(self, app):
|
||||
return AVATAR_CLASSES[app.config.get('AVATAR_KIND', 'Gravatar')](
|
||||
app.config['SERVER_HOSTNAME'],
|
||||
app.config['PREFERRED_URL_SCHEME'])
|
||||
app.config['PREFERRED_URL_SCHEME'], app.config['AVATAR_COLORS'], app.config['HTTPCLIENT'])
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.state, name, None)
|
||||
|
@ -16,48 +16,83 @@ class Avatar(object):
|
|||
|
||||
class BaseAvatar(object):
|
||||
""" Base class for all avatar implementations. """
|
||||
def __init__(self, server_hostname, preferred_url_scheme):
|
||||
self.server_hostname = server_hostname
|
||||
def __init__(self, preferred_url_scheme, colors, http_client):
|
||||
self.preferred_url_scheme = preferred_url_scheme
|
||||
self.colors = colors
|
||||
self.http_client = http_client
|
||||
|
||||
def get_url(self, email, size=16, name=None):
|
||||
""" Returns the full URL for viewing the avatar of the given email address, with
|
||||
an optional size.
|
||||
def get_mail_html(self, name, email_or_id, size=16, kind='user'):
|
||||
""" Returns the full HTML and CSS for viewing the avatar of the given name and email address,
|
||||
with an optional size.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
data = self.get_data(name, email_or_id, kind)
|
||||
url = self._get_url(data['hash'], size) if kind != 'team' else None
|
||||
font_size = size - 6
|
||||
|
||||
def compute_hash(self, email, name=None):
|
||||
""" Computes the avatar hash for the given email address. If the name is given and a default
|
||||
avatar is being computed, the name can be used in place of the email address. """
|
||||
raise NotImplementedError
|
||||
if url is not None:
|
||||
# Try to load the gravatar. If we get a non-404 response, then we use it in place of
|
||||
# the CSS avatar.
|
||||
response = self.http_client.get(url)
|
||||
if response.status_code == 200:
|
||||
return """<img src="%s" width="%s" height="%s" alt="%s"
|
||||
style="vertical-align: middle;">""" % (url, size, size, kind)
|
||||
|
||||
radius = '50%' if kind == 'team' else '0%'
|
||||
letter = 'Ω' if kind == 'team' and data['name'] == 'owners' else data['name'].upper()[0]
|
||||
|
||||
return """
|
||||
<span style="width: %spx; height: %spx; background-color: %s; font-size: %spx;
|
||||
line-height: %spx; margin-left: 2px; margin-right: 2px; display: inline-block;
|
||||
vertical-align: middle; text-align: center; color: white; border-radius: %s">
|
||||
%s
|
||||
</span>
|
||||
""" % (size, size, data['color'], font_size, size, radius, letter)
|
||||
|
||||
def get_data_for_user(self, user):
|
||||
return self.get_data(user.username, user.email, 'robot' if user.robot else 'user')
|
||||
|
||||
def get_data_for_team(self, team):
|
||||
return self.get_data(team.name, team.name, 'team')
|
||||
|
||||
def get_data_for_org(self, org):
|
||||
return self.get_data(org.username, org.email, 'org')
|
||||
|
||||
def get_data(self, name, email_or_id, kind='user'):
|
||||
""" Computes and returns the full data block for the avatar:
|
||||
{
|
||||
'name': name,
|
||||
'hash': The gravatar hash, if any.
|
||||
'color': The color for the avatar
|
||||
}
|
||||
"""
|
||||
colors = self.colors
|
||||
hash_value = hashlib.md5(email_or_id.strip().lower()).hexdigest()
|
||||
|
||||
byte_count = int(math.ceil(math.log(len(colors), 16)))
|
||||
byte_data = hash_value[0:byte_count]
|
||||
hash_color = colors[int(byte_data, 16) % len(colors)]
|
||||
|
||||
return {
|
||||
'name': name,
|
||||
'hash': hash_value,
|
||||
'color': hash_color,
|
||||
'kind': kind
|
||||
}
|
||||
|
||||
def _get_url(self, hash_value, size):
|
||||
""" Returns the URL for displaying the overlay avatar. """
|
||||
return None
|
||||
|
||||
|
||||
class GravatarAvatar(BaseAvatar):
|
||||
""" Avatar system that uses gravatar for generating avatars. """
|
||||
def compute_hash(self, email, name=None):
|
||||
email = email or ""
|
||||
return hashlib.md5(email.strip().lower()).hexdigest()
|
||||
|
||||
def get_url(self, email, size=16, name=None):
|
||||
computed = self.compute_hash(email, name=name)
|
||||
return '%s://www.gravatar.com/avatar/%s?d=identicon&size=%s' % (self.preferred_url_scheme,
|
||||
computed, size)
|
||||
def _get_url(self, hash_value, size=16):
|
||||
return '%s://www.gravatar.com/avatar/%s?d=404&size=%s' % (self.preferred_url_scheme,
|
||||
hash_value, size)
|
||||
|
||||
class LocalAvatar(BaseAvatar):
|
||||
""" Avatar system that uses the local system for generating avatars. """
|
||||
def compute_hash(self, email, name=None):
|
||||
email = email or ""
|
||||
if not name and not email:
|
||||
return ''
|
||||
|
||||
prefix = name if name else email
|
||||
return prefix[0] + hashlib.md5(email.strip().lower()).hexdigest()
|
||||
|
||||
def get_url(self, email, size=16, name=None):
|
||||
computed = self.compute_hash(email, name=name)
|
||||
return '%s://%s/avatar/%s?size=%s' % (self.preferred_url_scheme, self.server_hostname,
|
||||
computed, size)
|
||||
|
||||
pass
|
||||
|
||||
AVATAR_CLASSES = {
|
||||
'gravatar': GravatarAvatar,
|
||||
|
|
Reference in a new issue