Add support for serving users' avatars through the Libravatar API

This commit is contained in:
Danielle McLean 2018-03-23 12:56:13 +11:00
parent 6fb289727c
commit 43348a89da
Signed by: 00dani
GPG key ID: 5A5D2D1AFF12EEC5
8 changed files with 152 additions and 37 deletions

View file

@ -0,0 +1,24 @@
# Generated by Django 2.0.3 on 2018-03-23 01:00
import computed_property.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0012_auto_20180129_1614'),
]
operations = [
migrations.AddField(
model_name='user',
name='email_md5',
field=computed_property.fields.ComputedCharField(compute_from='calc_email_md5', default='', editable=False, max_length=32, unique=True),
),
migrations.AddField(
model_name='user',
name='email_sha256',
field=computed_property.fields.ComputedCharField(compute_from='calc_email_sha256', default='', editable=False, max_length=64, unique=True),
),
]

View file

@ -1,7 +1,9 @@
from computed_property import ComputedCharField
from django.db import models
from django.contrib.auth.models import AbstractUser, UserManager as DjangoUserManager
from django.contrib.sites.models import Site as DjangoSite
from django.utils.functional import cached_property
from hashlib import md5, sha256
from meta.models import ModelMeta
from urllib.parse import urljoin
from lemoncurry import utils
@ -46,6 +48,21 @@ class User(ModelMeta, AbstractUser):
# This is gonna need to change if I ever decide to add multiple-user support ;)
url = '/'
email_md5 = ComputedCharField(
compute_from='calc_email_md5', max_length=32, unique=True
)
email_sha256 = ComputedCharField(
compute_from='calc_email_sha256', max_length=64, unique=True
)
@property
def calc_email_md5(self):
return md5(self.email.lower().encode('utf-8')).hexdigest()
@property
def calc_email_sha256(self):
return sha256(self.email.lower().encode('utf-8')).hexdigest()
@property
def name(self):
return '{0} {1}'.format(self.first_name, self.last_name)

8
users/urls.py Normal file
View file

@ -0,0 +1,8 @@
from django.conf.urls import url
from .views import libravatar
app_name = 'users'
urlpatterns = (
url('^avatar/(?P<hash>[a-z0-9]+)$', libravatar, name='libravatar'),
)

49
users/views.py Normal file
View file

@ -0,0 +1,49 @@
from django.http import HttpResponse, HttpResponseRedirect
from PIL import Image
from lemoncurry import utils
from .models import User
def try_libravatar_org(hash, get):
url = 'https://seccdn.libravatar.org/avatar/' + hash
if get:
url += '?' + get.urlencode()
return HttpResponseRedirect(url)
def libravatar(request, hash):
g = request.GET
size = g.get('s', g.get('size', 80))
try:
size = int(size)
except ValueError:
return utils.bad_req('size parameter must be an integer')
if not 1 <= size <= 128:
return utils.bad_req('size parameter must be between 1 and 128')
if len(hash) == 32:
where = {'email_md5': hash}
elif len(hash) == 64:
where = {'email_sha256': hash}
else:
return utils.bad_req('hash must be either md5 or sha256')
# If the user doesn't exist or lacks an avatar, see if libravatar.org has
# one for them - libravatar.org falls back to Gravatar when possible (only
# for MD5 hashes, since Gravatar doesn't support SHA-256), so this ensures
# all the most likely places are checked.
try:
user = User.objects.get(**where)
except User.DoesNotExist:
return try_libravatar_org(hash, g)
if not user.avatar:
return try_libravatar_org(hash, g)
im = Image.open(user.avatar)
im_resized = im.resize((size, size))
response = HttpResponse(content_type='image/'+im.format.lower())
im_resized.save(response, im.format)
return response