From 58a7738fe754aa8055d6848c43763840cf079b35 Mon Sep 17 00:00:00 2001 From: Oliver Falk Date: Thu, 21 Feb 2019 10:17:42 +0100 Subject: [PATCH 01/17] Move exportaccounts to attic - only worked with old libravatar version on Python 2 --- exportaccounts.py | 139 ---------------------------------------------- 1 file changed, 139 deletions(-) delete mode 100755 exportaccounts.py diff --git a/exportaccounts.py b/exportaccounts.py deleted file mode 100755 index b921b99..0000000 --- a/exportaccounts.py +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# Copyright (C) 2018 Oliver Falk -# -# This file is part of Libravatar -# -# Libravatar is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published -# by the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Libravatar is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Libravatar. If not, see . - -import base64 -import gzip -import json -import os -import sys -from xml.sax import saxutils -import hashlib - -# pylint: disable=relative-import -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "libravatar.settings") -import libravatar.settings as settings -from libravatar.utils import create_logger, is_hex -import django -django.setup() -from django.contrib.auth.models import User - -os.umask(022) -LOGGER = create_logger('exportaccount') - -SCHEMA_ROOT = 'https://www.libravatar.org/schemas/export/0.2' -SCHEMA_XSD = '%s/export.xsd' % SCHEMA_ROOT - - -def xml_header(): - return ''' -\n''' % (SCHEMA_ROOT, SCHEMA_XSD, SCHEMA_ROOT) - - -def xml_footer(): - return '\n' - - -def xml_account(username, password): - escaped_username = saxutils.quoteattr(username) - escaped_site_url = saxutils.quoteattr(settings.SITE_URL) - escaped_password = saxutils.quoteattr(password) - return ' \n' % (escaped_username, escaped_password, escaped_site_url) - -def xml_email(emails): - returnstring = " \n" - for email in emails: - returnstring += ' ' + email.email.encode('utf-8') + '' + "\n" - returnstring += " \n" - return returnstring - -def xml_openid(openids): - returnstring = " \n" - for openid in openids: - returnstring += ' ' + openid.openid.encode('utf-8') + '' + "\n" - returnstring += " \n" - return returnstring - - -def xml_photos(photos): - s = ' \n' - for photo in photos: - (photo_filename, photo_format, id) = photo - encoded_photo = encode_photo(photo_filename, photo_format) - if encoded_photo: - s += ''' -%s - \n''' % (id, saxutils.quoteattr(photo_format), encoded_photo) - s += ' \n' - return s - - -def encode_photo(photo_filename, photo_format): - filename = settings.USER_FILES_ROOT + photo_filename + '.' + photo_format - if not os.path.isfile(filename): - LOGGER.warning('Photo not found: %s', filename) - return None - - photo_content = None - with open(filename) as photo: - photo_content = photo.read() - - if not photo_content: - LOGGER.warning('Could not read photo: %s', filename) - return None - - return base64.b64encode(photo_content) - - -def main(argv=None): - if argv is None: - argv = sys.argv - - if(len(sys.argv) > 1): - userobjs = User.objects.filter(username=sys.argv[1]) - else: - userobjs = User.objects.all() - - for user in userobjs: - hash_object = hashlib.new('sha256') - hash_object.update(user.username + user.password) - file_hash = hash_object.hexdigest() - photos = [] - for photo in user.photos.all(): - photo_details = (photo.filename, photo.format, photo.id) - photos.append(photo_details) - username = user.username - - dest_filename = settings.EXPORT_FILES_ROOT + file_hash + '.xml.gz' - - destination = gzip.open(dest_filename, 'w') - destination.write(xml_header()) - destination.write(xml_account(username, user.password)) - destination.write(xml_email(user.confirmed_emails.all())) - destination.write(xml_openid(user.confirmed_openids.all())) - destination.write(xml_photos(photos)) - destination.write(xml_footer()) - destination.close() - print(dest_filename) - return 0 - - -if __name__ == "__main__": - sys.exit(main()) From ddae7a6fabbd5739d141ff78f4bb1ce329a13356 Mon Sep 17 00:00:00 2001 From: Oliver Falk Date: Thu, 21 Feb 2019 11:22:27 +0100 Subject: [PATCH 02/17] Make wavatar same as identicon/retro for the moment --- ivatar/views.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ivatar/views.py b/ivatar/views.py index f330c34..4b00d6d 100644 --- a/ivatar/views.py +++ b/ivatar/views.py @@ -145,7 +145,9 @@ class AvatarImageView(TemplateView): data, content_type='image/png') - if str(default) == 'identicon' or str(default) == 'retro': + # TODO: Wavatar needs to be different + # TODO: identicon should be different from retro + if str(default) == 'identicon' or str(default) == 'retro' or str(default) == 'wavatar': # Taken from example code foreground = [ 'rgb(45,79,255)', From c06106d1a29bf1b28b850c57d2ab30e72fde0ec6 Mon Sep 17 00:00:00 2001 From: Oliver Falk Date: Thu, 21 Feb 2019 17:34:44 +0100 Subject: [PATCH 03/17] If user object has no password, set some random password; Else Django refuses to send out password reset mail --- ivatar/ivataraccount/views.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ivatar/ivataraccount/views.py b/ivatar/ivataraccount/views.py index 1ac6daf..40c3d0b 100644 --- a/ivatar/ivataraccount/views.py +++ b/ivatar/ivataraccount/views.py @@ -914,6 +914,9 @@ class PasswordResetView(PasswordResetViewOriginal): try: confirmed_email = ConfirmedEmail.objects.get(email=request.POST['email']) confirmed_email.user.email = confirmed_email.email + if not confirmed_email.user.password: + random_pass = User.objects.make_random_password() + confirmed_email.user.set_pasword(random_pass) confirmed_email.user.save() except Exception as exc: pass From 13be612892ea88b22954905688a0d2866852a794 Mon Sep 17 00:00:00 2001 From: Oliver Falk Date: Fri, 22 Feb 2019 14:25:28 +0100 Subject: [PATCH 04/17] Use my image in order to reduce the build time --- .gitlab-ci.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3214a43..f556861 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,10 +1,6 @@ -image: centos:centos7 +image: ofalk/centos7-python36 before_script: - - yum install -y -t https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm - - yum -y -t install python36 python34-pip python36-devel unzip mysql-devel gcc git openldap-devel - - pip3 install pip --upgrade - - pip install virtualenv --upgrade - virtualenv -p python3.6 /tmp/.virtualenv - source /tmp/.virtualenv/bin/activate - pip install -r requirements.txt From 54aba78e87c79862270346ccc2dbf2539cf08af8 Mon Sep 17 00:00:00 2001 From: Oliver Falk Date: Fri, 22 Feb 2019 16:15:59 +0100 Subject: [PATCH 05/17] Leave generation of identicon/wavatar to Gravatar, until we have our own solution --- ivatar/views.py | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/ivatar/views.py b/ivatar/views.py index 4b00d6d..38a1622 100644 --- a/ivatar/views.py +++ b/ivatar/views.py @@ -120,6 +120,14 @@ class AvatarImageView(TemplateView): # Return the default URL, as specified, or 404 Not Found, if default=404 if default: + # Proxy to gravatar to generate wavatar/identicon- lazy me + if str(default) == 'wavatar' or str(default) == 'identicon': + url = reverse_lazy('gravatarproxy', args=[kwargs['digest']]) \ + + '?s=%i' % size + '&default=%s&f=y' % default + return HttpResponseRedirect(url) + + + if str(default) == str(404): return HttpResponseNotFound(_('

Image not found

')) @@ -145,9 +153,7 @@ class AvatarImageView(TemplateView): data, content_type='image/png') - # TODO: Wavatar needs to be different - # TODO: identicon should be different from retro - if str(default) == 'identicon' or str(default) == 'retro' or str(default) == 'wavatar': + if str(default) == 'retro': # Taken from example code foreground = [ 'rgb(45,79,255)', @@ -237,21 +243,22 @@ class GravatarProxyView(View): except: pass - # This part is special/hackish - # Check if the image returned by Gravatar is their default image, if so, - # redirect to our default instead. - gravatar_test_url = 'https://secure.gravatar.com/avatar/' + kwargs['digest'] \ - + '?s=%i' % 50 - try: - testdata = urlopen(gravatar_test_url, timeout=URL_TIMEOUT) - data = BytesIO(testdata.read()) - if hashlib.md5(data.read()).hexdigest() == '71bc262d627971d13fe6f3180b93062a': - return redir_default(default) - except Exception as exc: - print('Gravatar test url fetch failed: %s' % exc) + if str(default) != 'wavatar' and str(default) != 'identicon': + # This part is special/hackish + # Check if the image returned by Gravatar is their default image, if so, + # redirect to our default instead. + gravatar_test_url = 'https://secure.gravatar.com/avatar/' + kwargs['digest'] \ + + '?s=%i' % 50 + try: + testdata = urlopen(gravatar_test_url, timeout=URL_TIMEOUT) + data = BytesIO(testdata.read()) + if hashlib.md5(data.read()).hexdigest() == '71bc262d627971d13fe6f3180b93062a': + return redir_default(default) + except Exception as exc: + print('Gravatar test url fetch failed: %s' % exc) gravatar_url = 'https://secure.gravatar.com/avatar/' + kwargs['digest'] \ - + '?s=%i' % size + + '?s=%i' % size + '&d=%s' % default try: gravatarimagedata = urlopen(gravatar_url, timeout=URL_TIMEOUT) From 5d329a7727e28cb4afa239c83c565371c00e9cda Mon Sep 17 00:00:00 2001 From: Niklas Poslovski Date: Sat, 23 Feb 2019 17:20:00 +0100 Subject: [PATCH 06/17] Improve the design of the domain check --- ivatar/tools/templates/check_domain.html | 83 +++++++++++++----------- 1 file changed, 45 insertions(+), 38 deletions(-) diff --git a/ivatar/tools/templates/check_domain.html b/ivatar/tools/templates/check_domain.html index fd836c8..560779b 100644 --- a/ivatar/tools/templates/check_domain.html +++ b/ivatar/tools/templates/check_domain.html @@ -37,44 +37,51 @@ {% if result %}
- - - - - - - - - -
{% trans 'HTTP avatar server:' %} - {% if result.avatar_server_http %} - {{result.avatar_server_http}} - {% if result.avatar_server_http_ipv4 %} -
{{ result.avatar_server_http_ipv4 }} - {% else %} -
{% trans 'Warning: no A record for this hostname' %} - {% endif %} - {% if result.avatar_server_http_ipv6 %} -
{{ result.avatar_server_http_ipv6 }} - {% endif %} - {% else %} - {% trans 'use http://cdn.libravatar.org' %} - {% endif %} -
{% trans 'HTTPS avatar server:' %} - {% if result.avatar_server_https %} - {{result.avatar_server_https}} - {% if result.avatar_server_https_ipv4 %} -
{{ result.avatar_server_https_ipv4 }} - {% else %} -
{% trans 'Warning: no A record for this hostname' %} - {% endif %} - {% if result.avatar_server_https_ipv6 %} -
{{ result.avatar_server_https_ipv6 }} - {% endif %} - {% else %} - {% trans 'use https://seccdn.libravatar.org' %} - {% endif %} -
+

The following servers will be used for your domain

+
+
+

 HTTP Server

+
+
+{% if result.avatar_server_http %} + +

{{result.avatar_server_http}}

+
+{% if result.avatar_server_http_ipv4 %} +
{{ result.avatar_server_http_ipv4 }}
+{% endif %} +{% if result.avatar_server_http_ipv6 %} +
{{ result.avatar_server_http_ipv6 }}
+{% endif %} +{% else %} + +

http://cdn.libravatar.org

+
+{% endif %} +
+
+
+
+

 HTTPS Server

+
+
+{% if result.avatar_server_https %} + +

{{result.avatar_server_https}}

+
+{% if result.avatar_server_https_ipv4 %} +
{{ result.avatar_server_https_ipv4 }}
+{% endif %} +{% if result.avatar_server_https_ipv6 %} +
{{ result.avatar_server_https_ipv6 }}
+{% endif %} +{% else %} + +

https://seccdn.libravatar.org

+
+{% endif %} +
+
{% endif %}
From 057b9215e52bd9ff57ad9f808afa3441ed998364 Mon Sep 17 00:00:00 2001 From: Oliver Falk Date: Mon, 25 Feb 2019 08:47:54 +0100 Subject: [PATCH 07/17] Clean and fix issue #47 --- ivatar/tools/templates/check.html | 4 ++-- ivatar/tools/views.py | 16 ++++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/ivatar/tools/templates/check.html b/ivatar/tools/templates/check.html index 13435bb..0b48d58 100644 --- a/ivatar/tools/templates/check.html +++ b/ivatar/tools/templates/check.html @@ -56,8 +56,8 @@

SHA256  

diff --git a/ivatar/tools/views.py b/ivatar/tools/views.py index a34ceda..fb9cbe4 100644 --- a/ivatar/tools/views.py +++ b/ivatar/tools/views.py @@ -66,15 +66,17 @@ class CheckView(FormView): else: default_url = None + if 'size' in form.cleaned_data: + size = form.cleaned_data['size'] if form.cleaned_data['mail']: mailurl = libravatar_url( email=form.cleaned_data['mail'], - size=form.cleaned_data['size'], + size=size, default=default_url) mailurl = mailurl.replace(LIBRAVATAR_BASE_URL, BASE_URL) mailurl_secure = libravatar_url( email=form.cleaned_data['mail'], - size=form.cleaned_data['size'], + size=size, https=True, default=default_url) mailurl_secure = mailurl_secure.replace( @@ -86,18 +88,20 @@ class CheckView(FormView): hash_obj = hashlib.new('sha256') hash_obj.update(form.cleaned_data['mail'].encode('utf-8')) mail_hash256 = hash_obj.hexdigest() - size = form.cleaned_data['size'] + mailurl_secure_256 = mailurl_secure.replace( + mail_hash, + mail_hash256) if form.cleaned_data['openid']: if not form.cleaned_data['openid'].startswith('http://') and not form.cleaned_data['openid'].startswith('https://'): form.cleaned_data['openid'] = 'http://%s' % form.cleaned_data['openid'] openidurl = libravatar_url( openid=form.cleaned_data['openid'], - size=form.cleaned_data['size'], + size=size, default=default_url) openidurl = openidurl.replace(LIBRAVATAR_BASE_URL, BASE_URL) openidurl_secure = libravatar_url( openid=form.cleaned_data['openid'], - size=form.cleaned_data['size'], + size=size, https=True, default=default_url) openidurl_secure = openidurl_secure.replace( @@ -106,13 +110,13 @@ class CheckView(FormView): openid_hash = parse_user_identity( openid=form.cleaned_data['openid'], email=None)[0] - size = form.cleaned_data['size'] return render(self.request, self.template_name, { 'form': form, 'mailurl': mailurl, 'openidurl': openidurl, 'mailurl_secure': mailurl_secure, + 'mailurl_secure_256': mailurl_secure_256, 'openidurl_secure': openidurl_secure, 'mail_hash': mail_hash, 'mail_hash256': mail_hash256, From 423bd2737ef5c417683eacf57635f47974218001 Mon Sep 17 00:00:00 2001 From: Oliver Falk Date: Mon, 25 Feb 2019 12:56:24 +0100 Subject: [PATCH 08/17] Fix issue #46 --- ivatar/views.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ivatar/views.py b/ivatar/views.py index 38a1622..962ece2 100644 --- a/ivatar/views.py +++ b/ivatar/views.py @@ -202,8 +202,12 @@ class AvatarImageView(TemplateView): imgformat = obj.photo.format photodata = Image.open(BytesIO(obj.photo.data)) - - photodata.thumbnail((size, size), Image.ANTIALIAS) + # If the image is smaller than what was requested, we need + # to use the function resize + if photodata.size[0] < size or photodata.size[1] < size: + photodata = photodata.resize((size, size), Image.ANTIALIAS) + else: + photodata.thumbnail((size, size), Image.ANTIALIAS) data = BytesIO() photodata.save(data, pil_format(imgformat), quality=JPEG_QUALITY) data.seek(0) From af9f3f9a1a07f47855598c63ec52cef5660a7c0c Mon Sep 17 00:00:00 2001 From: Oliver Falk Date: Mon, 25 Feb 2019 15:46:23 +0100 Subject: [PATCH 09/17] Fallback to smtp backend if no MAILGUN is available --- config.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/config.py b/config.py index f12f91a..9c01d2b 100644 --- a/config.py +++ b/config.py @@ -102,11 +102,14 @@ else: if 'test' in sys.argv or 'collectstatic' in sys.argv: EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend' else: - ANYMAIL = { # pragma: no cover - 'MAILGUN_API_KEY': os.environ['IVATAR_MAILGUN_API_KEY'], - 'MAILGUN_SENDER_DOMAIN': os.environ['IVATAR_MAILGUN_SENDER_DOMAIN'], - } - EMAIL_BACKEND = 'anymail.backends.mailgun.EmailBackend' # pragma: no cover + try: + ANYMAIL = { # pragma: no cover + 'MAILGUN_API_KEY': os.environ['IVATAR_MAILGUN_API_KEY'], + 'MAILGUN_SENDER_DOMAIN': os.environ['IVATAR_MAILGUN_SENDER_DOMAIN'], + } + EMAIL_BACKEND = 'anymail.backends.mailgun.EmailBackend' # pragma: no cover + except Exception as exc: + EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' SERVER_EMAIL = os.environ.get('SERVER_EMAIL', 'ivatar@mg.linux-kernel.at') DEFAULT_FROM_EMAIL = os.environ.get('DEFAULT_FROM_EMAIL', 'ivatar@mg.linux-kernel.at') From f7f573e99d0eb970757be142aee9531ae7672863 Mon Sep 17 00:00:00 2001 From: Oliver Falk Date: Thu, 28 Feb 2019 12:21:02 +0100 Subject: [PATCH 10/17] Identicons are now generated by ivatar and switch retro to a more modern version - but only does 5x5 (like GitHub) --- ivatar/views.py | 53 ++++++++++++++++++++++-------------------------- requirements.txt | 3 ++- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/ivatar/views.py b/ivatar/views.py index 962ece2..387c42c 100644 --- a/ivatar/views.py +++ b/ivatar/views.py @@ -16,7 +16,9 @@ from django.urls import reverse_lazy from PIL import Image from monsterid.id import build_monster as BuildMonster -from pydenticon import Generator as IdenticonGenerator +import Identicon +from pydenticon5 import Pydenticon5 + from robohash import Robohash from ivatar.settings import AVATAR_MAX_SIZE, JPEG_QUALITY, DEFAULT_AVATAR_SIZE @@ -120,8 +122,8 @@ class AvatarImageView(TemplateView): # Return the default URL, as specified, or 404 Not Found, if default=404 if default: - # Proxy to gravatar to generate wavatar/identicon- lazy me - if str(default) == 'wavatar' or str(default) == 'identicon': + # Proxy to gravatar to generate wavatar - lazy me + if str(default) == 'wavatar': url = reverse_lazy('gravatarproxy', args=[kwargs['digest']]) \ + '?s=%i' % size + '&default=%s&f=y' % default return HttpResponseRedirect(url) @@ -154,31 +156,24 @@ class AvatarImageView(TemplateView): content_type='image/png') if str(default) == 'retro': - # Taken from example code - foreground = [ - 'rgb(45,79,255)', - 'rgb(254,180,44)', - 'rgb(226,121,234)', - 'rgb(30,179,253)', - 'rgb(232,77,65)', - 'rgb(49,203,115)', - 'rgb(141,69,170)'] - background = 'rgb(224,224,224)' - padwidth = int(size/10) - if padwidth < 10: - padwidth = 10 - if size < 60: - padwidth = 0 - padding = (padwidth, padwidth, padwidth, padwidth) - # Since padding is _added_ around the generated image, we - # need to reduce the image size by padding*2 (left/right, top/bottom) - size = size - 2*padwidth - generator = IdenticonGenerator( - 10, 10, digest=hashlib.sha1, - foreground=foreground, background=background) - data = generator.generate( - kwargs['digest'], size, size, - output_format='png', padding=padding, inverted=False) + identicon = Identicon.render(kwargs['digest']) + data = BytesIO() + img = Image.open(BytesIO(identicon)) + img = img.resize((size, size), Image.ANTIALIAS) + img.save(data, 'PNG', quality=JPEG_QUALITY) + data.seek(0) + return HttpResponse( + data, + content_type='image/png') + + if str(default) == 'identicon': + p = Pydenticon5() + # In order to make use of the whole 32 bytes digest, we need to redigest them. + newdigest = hashlib.md5(bytes(kwargs['digest'], 'utf-8')).hexdigest() + img = p.draw(newdigest, size, 0) + data = BytesIO() + img.save(data, 'PNG', quality=JPEG_QUALITY) + data.seek(0) return HttpResponse( data, content_type='image/png') @@ -247,7 +242,7 @@ class GravatarProxyView(View): except: pass - if str(default) != 'wavatar' and str(default) != 'identicon': + if str(default) != 'wavatar': # This part is special/hackish # Check if the image returned by Gravatar is their default image, if so, # redirect to our default instead. diff --git a/requirements.txt b/requirements.txt index fb00e31..9a9a4dc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -33,6 +33,7 @@ mysqlclient psycopg2 notsetuptools git+https://github.com/ofalk/monsterid.git -git+https://github.com/azaghal/pydenticon.git git+https://github.com/ofalk/Robohash.git@devel python-memcached +git+https://github.com/ercpe/pydenticon5.git +git+https://github.com/flavono123/identicon.git From a70a453f43bf163d346f3ece869e1e93d137051d Mon Sep 17 00:00:00 2001 From: Oliver Falk Date: Thu, 28 Feb 2019 12:34:40 +0100 Subject: [PATCH 11/17] The usual PIL vs. Pillow issues, therefore use my fork for this --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9a9a4dc..daf939f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -36,4 +36,4 @@ git+https://github.com/ofalk/monsterid.git git+https://github.com/ofalk/Robohash.git@devel python-memcached git+https://github.com/ercpe/pydenticon5.git -git+https://github.com/flavono123/identicon.git +git+https://github.com/ofalk/identicon.git From 5bb3bac1613d0dc4a4eb4f60eedef59b57300e1b Mon Sep 17 00:00:00 2001 From: Oliver Falk Date: Thu, 28 Feb 2019 12:56:24 +0100 Subject: [PATCH 12/17] First install Pillow and afterwards the rest --- .gitlab-ci.yml | 1 + requirements.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f556861..f8f61d0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,6 +3,7 @@ image: ofalk/centos7-python36 before_script: - virtualenv -p python3.6 /tmp/.virtualenv - source /tmp/.virtualenv/bin/activate + - pip install Pillow - pip install -r requirements.txt - pip install python-coveralls - pip install coverage diff --git a/requirements.txt b/requirements.txt index daf939f..9a9a4dc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -36,4 +36,4 @@ git+https://github.com/ofalk/monsterid.git git+https://github.com/ofalk/Robohash.git@devel python-memcached git+https://github.com/ercpe/pydenticon5.git -git+https://github.com/ofalk/identicon.git +git+https://github.com/flavono123/identicon.git From c5c3bd8f1b90b5cd0dbfb2d3c6125ce8043f627a Mon Sep 17 00:00:00 2001 From: Oliver Falk Date: Thu, 28 Feb 2019 13:27:22 +0100 Subject: [PATCH 13/17] Enhance the check possibilities --- ivatar/tools/forms.py | 4 +++- ivatar/tools/templates/check.html | 10 ++++++++-- templates/navbar.html | 6 +++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/ivatar/tools/forms.py b/ivatar/tools/forms.py index f25237e..c0dc995 100644 --- a/ivatar/tools/forms.py +++ b/ivatar/tools/forms.py @@ -56,8 +56,10 @@ class CheckForm(forms.Form): required=True, ) - default_url = forms.URLField( + default_url = forms.CharField( label=_('Default URL'), + min_length=1, + max_length=MAX_LENGTH_URL, required=False, ) diff --git a/ivatar/tools/templates/check.html b/ivatar/tools/templates/check.html index 0b48d58..eb08fb9 100644 --- a/ivatar/tools/templates/check.html +++ b/ivatar/tools/templates/check.html @@ -8,6 +8,12 @@

{% trans 'Check e-mail or openid' %}

+ {% if form.errors %} +

{% trans "Please correct errors below:" %}
+ {{ form.errors|join:', ' }} +

+ {% endif %} +
{% csrf_token %} @@ -17,8 +23,8 @@
-
-
+
+
diff --git a/templates/navbar.html b/templates/navbar.html index eb790eb..7179a25 100644 --- a/templates/navbar.html +++ b/templates/navbar.html @@ -45,8 +45,12 @@ From 14db71cca03b804dafae94eb1d21017f088b0c70 Mon Sep 17 00:00:00 2001 From: Oliver Falk Date: Thu, 28 Feb 2019 13:35:01 +0100 Subject: [PATCH 14/17] Increase version --- config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.py b/config.py index 9c01d2b..bdadc3c 100644 --- a/config.py +++ b/config.py @@ -52,7 +52,7 @@ OPENID_CREATE_USERS = True OPENID_UPDATE_DETAILS_FROM_SREG = True SITE_NAME = os.environ.get('SITE_NAME', 'libravatar') -IVATAR_VERSION = '1.0' +IVATAR_VERSION = '1.1' SECURE_BASE_URL = os.environ.get('SECURE_BASE_URL', 'https://avatars.linux-kernel.at/avatar/') BASE_URL = os.environ.get('BASE_URL', 'http://avatars.linux-kernel.at/avatar/') From c9774113098c84c37f663fdbac44066763779686 Mon Sep 17 00:00:00 2001 From: Oliver Falk Date: Thu, 28 Feb 2019 14:14:10 +0100 Subject: [PATCH 15/17] Fix issue #49 --- ivatar/ivataraccount/models.py | 6 +++--- ivatar/ivataraccount/test_views.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ivatar/ivataraccount/models.py b/ivatar/ivataraccount/models.py index 1200666..05b2137 100644 --- a/ivatar/ivataraccount/models.py +++ b/ivatar/ivataraccount/models.py @@ -37,10 +37,10 @@ from .gravatar import get_photo as get_gravatar_photo def file_format(image_type): ''' - Helper method returning a 3 character long image type + Helper method returning a short image type ''' if image_type == 'JPEG': - return 'jpg' + return 'jpeg' elif image_type == 'PNG': return 'png' elif image_type == 'GIF': @@ -52,7 +52,7 @@ def pil_format(image_type): ''' Helper method returning the 'encoder name' for PIL ''' - if image_type == 'jpg': + if image_type == 'jpg' or image_type == 'jpeg': return 'JPEG' elif image_type == 'png': return 'PNG' diff --git a/ivatar/ivataraccount/test_views.py b/ivatar/ivataraccount/test_views.py index 1058980..57ab8dc 100644 --- a/ivatar/ivataraccount/test_views.py +++ b/ivatar/ivataraccount/test_views.py @@ -358,7 +358,7 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods # Probably not the best way to access the content type self.assertEqual( response['Content-Type'], - 'image/jpg', + 'image/jpeg', 'Content type wrong!?') self.assertEqual( @@ -638,10 +638,10 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods self.assertEqual( str(list(response.context[0]['messages'])[0]), 'Successfully uploaded', - 'JPG upload failed?!') + 'JPEG upload failed?!') self.assertEqual( - self.user.photo_set.first().format, 'jpg', - 'Format must be jpg, since we uploaded a jpg!') + self.user.photo_set.first().format, 'jpeg', + 'Format must be jpeg, since we uploaded a jpeg!') self.test_confirm_email() self.user.confirmedemail_set.first().photo = self.user.photo_set.first() urlobj = urlsplit( From 2d62e658e4cd5b0bd666211f3c0f91325389ac22 Mon Sep 17 00:00:00 2001 From: Oliver Falk Date: Thu, 28 Feb 2019 17:02:15 +0100 Subject: [PATCH 16/17] Implement the pagan lib (for fun) --- ivatar/views.py | 11 ++++++++++- requirements.txt | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ivatar/views.py b/ivatar/views.py index 387c42c..4f56b43 100644 --- a/ivatar/views.py +++ b/ivatar/views.py @@ -18,7 +18,7 @@ from PIL import Image from monsterid.id import build_monster as BuildMonster import Identicon from pydenticon5 import Pydenticon5 - +import pagan from robohash import Robohash from ivatar.settings import AVATAR_MAX_SIZE, JPEG_QUALITY, DEFAULT_AVATAR_SIZE @@ -166,6 +166,15 @@ class AvatarImageView(TemplateView): data, content_type='image/png') + if str(default) == 'pagan': + img = pagan.Avatar(kwargs['digest']) + data = BytesIO() + img.img.save(data, 'PNG', quality=JPEG_QUALITY) + data.seek(0) + return HttpResponse( + data, + content_type='image/png') + if str(default) == 'identicon': p = Pydenticon5() # In order to make use of the whole 32 bytes digest, we need to redigest them. diff --git a/requirements.txt b/requirements.txt index 9a9a4dc..d2bdc99 100644 --- a/requirements.txt +++ b/requirements.txt @@ -37,3 +37,4 @@ git+https://github.com/ofalk/Robohash.git@devel python-memcached git+https://github.com/ercpe/pydenticon5.git git+https://github.com/flavono123/identicon.git +pagan From 60fbf93a4f2c897ad7d0c72f86ae39994de65129 Mon Sep 17 00:00:00 2001 From: Oliver Falk Date: Thu, 28 Feb 2019 17:15:04 +0100 Subject: [PATCH 17/17] Make sure we do the pagan avatar in the right size --- ivatar/views.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ivatar/views.py b/ivatar/views.py index 4f56b43..a50b571 100644 --- a/ivatar/views.py +++ b/ivatar/views.py @@ -167,9 +167,10 @@ class AvatarImageView(TemplateView): content_type='image/png') if str(default) == 'pagan': - img = pagan.Avatar(kwargs['digest']) + paganobj = pagan.Avatar(kwargs['digest']) data = BytesIO() - img.img.save(data, 'PNG', quality=JPEG_QUALITY) + img = paganobj.img.resize((size, size), Image.ANTIALIAS) + img.save(data, 'PNG', quality=JPEG_QUALITY) data.seek(0) return HttpResponse( data,