diff --git a/requirements.txt b/requirements.txt index ba463154..0beaec04 100644 --- a/requirements.txt +++ b/requirements.txt @@ -25,3 +25,4 @@ requests==2.3.0 enum34==1.0 django-reversion==1.8.1 +easy-thumbnails==2.0 diff --git a/settings/common.py b/settings/common.py index 89885837..2caacaa1 100644 --- a/settings/common.py +++ b/settings/common.py @@ -191,6 +191,7 @@ INSTALLED_APPS = [ "rest_framework", "djmail", "django_jinja", + "easy_thumbnails", ] WSGI_APPLICATION = "taiga.wsgi.application" @@ -306,4 +307,21 @@ if "test" in sys.argv: print ("Try: \033[1;33mpy.test\033[0m") sys.exit(0) -DEFAULT_AVATAR_URL = "" +SOUTH_MIGRATION_MODULES = { + 'easy_thumbnails': 'easy_thumbnails.south_migrations', +} + +DEFAULT_AVATAR_SIZE = 80 # 80x80 pixels + +DEFAULT_AVATAR_URL = '' + +THUMBNAIL_ALIASES = { + '': { + 'avatar': {'size': (DEFAULT_AVATAR_SIZE, DEFAULT_AVATAR_SIZE), 'crop': True}, + }, +} + +GRAVATAR_DEFAULT_OPTIONS = { + 'default': DEFAULT_AVATAR_URL, # default avatar to show if there's no gravatar image + 'size': DEFAULT_AVATAR_SIZE +} diff --git a/taiga/base/utils/urls.py b/taiga/base/utils/urls.py new file mode 100644 index 00000000..2e5d71be --- /dev/null +++ b/taiga/base/utils/urls.py @@ -0,0 +1,20 @@ +import django_sites as sites + +URL_TEMPLATE = "{scheme}://{domain}/{path}" + + +def build_url(path, scheme="http", domain="localhost"): + return URL_TEMPLATE.format(scheme=scheme, domain=domain, path=path.lstrip("/")) + + +def is_absolute_url(path): + """Test wether or not `path` is absolute url.""" + return path.startswith("http") + + +def get_absolute_url(path): + """Return a path as an absolute url.""" + if is_absolute_url(path): + return path + site = sites.get_current() + return build_url(path, scheme=site.scheme, domain=site.domain) diff --git a/taiga/users/gravatar.py b/taiga/users/gravatar.py index dc48dba8..fc1fa0f0 100644 --- a/taiga/users/gravatar.py +++ b/taiga/users/gravatar.py @@ -3,22 +3,26 @@ from urllib.parse import urlencode from django.conf import settings +from taiga.base.utils.urls import get_absolute_url + + GRAVATAR_BASE_URL = "http://www.gravatar.com/avatar/{}?{}" def get_gravatar_url(email: str, **options) -> str: """Get the gravatar url associated to an email. - :param options: Additional options to gravatar: - - `d` defines what image url to show if no gravatar exists - - `s` defines the size of the avatar. - By default these options are: - - `d` is `settings.DEFAULT_AVATAR_URL` - - `s` gravatar's default value which is 80x80 px + :param options: Additional options to gravatar. + - `default` defines what image url to show if no gravatar exists + - `size` defines the size of the avatar. + By default the `settings.GRAVATAR_DEFAULT_OPTIONS` are used. :return: Gravatar url. """ - defaults = {'d': settings.DEFAULT_AVATAR_URL} + defaults = settings.GRAVATAR_DEFAULT_OPTIONS.copy() + default = defaults.get("default", None) + if default: + defaults["default"] = get_absolute_url(default) defaults.update(options) email_hash = hashlib.md5(email.lower().encode()).hexdigest() url = GRAVATAR_BASE_URL.format(email_hash, urlencode(defaults)) diff --git a/taiga/users/serializers.py b/taiga/users/serializers.py index 1108a14d..a82c95d5 100644 --- a/taiga/users/serializers.py +++ b/taiga/users/serializers.py @@ -15,10 +15,12 @@ # along with this program. If not, see . from django.utils.translation import ugettext_lazy as _ +from django.conf import settings from rest_framework import serializers from .models import User +from .services import get_photo_url from .gravatar import get_gravatar_url @@ -37,7 +39,11 @@ class UserSerializer(serializers.ModelSerializer): def get_photo(self, user): "Return the user photo or her gravatar" - return user.photo or get_gravatar_url(user.email) + if user.photo: + url = get_photo_url(user.photo) + else: + url = get_gravatar_url(user.email) + return url class RecoverySerializer(serializers.Serializer): diff --git a/taiga/users/services.py b/taiga/users/services.py index a8349c58..a7da3473 100644 --- a/taiga/users/services.py +++ b/taiga/users/services.py @@ -21,7 +21,10 @@ This model contains a domain logic for users application. from django.db.models.loading import get_model from django.db.models import Q +from easy_thumbnails.files import get_thumbnailer + from taiga.base import exceptions as exc +from taiga.base.utils.urls import get_absolute_url def get_and_validate_user(*, username:str, password:str) -> bool: @@ -44,3 +47,9 @@ def get_and_validate_user(*, username:str, password:str) -> bool: raise exc.WrongArguments("Username or password does not matches user.") return user + + +def get_photo_url(photo): + """Get a photo absolute url and the photo automatically cropped.""" + url = get_thumbnailer(photo)['avatar'].url + return get_absolute_url(url) diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py new file mode 100644 index 00000000..c9481155 --- /dev/null +++ b/tests/unit/test_utils.py @@ -0,0 +1,15 @@ +import django_sites as sites + +from taiga.base.utils.urls import get_absolute_url, is_absolute_url, build_url + + +def test_is_absolute_url(): + assert is_absolute_url("http://domain/path") + assert is_absolute_url("https://domain/path") + assert not is_absolute_url("://domain/path") + + +def test_get_absolute_url(): + site = sites.get_current() + assert get_absolute_url("http://domain/path") == "http://domain/path" + assert get_absolute_url("/path") == build_url("/path", domain=site.domain, scheme=site.scheme)