Skip to content
This repository has been archived by the owner on Jul 3, 2023. It is now read-only.

Commit

Permalink
- Bump to version 5.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
afabiani committed Feb 10, 2020
1 parent f5a81c0 commit 9a0fd60
Show file tree
Hide file tree
Showing 72 changed files with 3,713 additions and 855 deletions.
29 changes: 29 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,32 @@
5.0.0 (January 4, 2019)
-----------------------

* Added Django 2.1, 2.2, and 3.0 support.
* Added Python 3.7 and 3.8 support.
* Removed Python 1.9 and 1.10 support.
* Fixed bug where avatars couldn't be deleted if file was already deleted.

4.1.0 (December 20, 2017)
-------------------------

* Added Django 2.0 support.
* Added ``avatar_deleted`` signal.
* Ensure thumbnails are the correct orientation.

4.0.0 (May 27, 2017)
--------------------

* **Backwards incompatible:** Added ``AVATAR_PROVIDERS`` setting. Avatar providers are classes that return an avatar URL for a given user.
* Added ``verbose_name`` to ``Avatar`` model fields.
* Added the ability to override the ``alt`` attribute using the ``avatar`` template tag.
* Added Italian translations.
* Improved German translations.
* Fixed bug where ``rebuild_avatars`` would fail on Django 1.10+.
* Added Django 1.11 support.
* Added Python 3.6 support.
* Removed Django 1.7 and 1.8 support.
* Removed Python 3.3 support.

3.0.4 [2020-02-10]
------------------

Expand Down
16 changes: 3 additions & 13 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,4 @@
This application is mainly developed by Eric Florenzano.
This application was originally written by Eric Florenzano.
It is now maintained by Grant McConnaughey and a league of awesome contributors.

Ahmad Al-Ibrahim
Alex Gaynor
Ben Browitt
Brian Rosner
Daniel T. Alvarenga
Jakob Torp Svendsen
James Tauber
James Turnbull
Jannis Leidel
John Debs
Mathieu Pillard
Ross Poulton
See the full list here: https://github.com/grantmcconnaughey/django-avatar/graphs/contributors
1 change: 1 addition & 0 deletions avatar/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__version__ = '5.0.0'
36 changes: 35 additions & 1 deletion avatar/admin.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,38 @@
from django.contrib import admin
# Issue 182: six no longer included with Django 3.0
try:
from django.utils import six
except ImportError:
import six
from django.utils.translation import ugettext_lazy as _
from django.template.loader import render_to_string

from avatar.models import Avatar
from avatar.signals import avatar_updated
from avatar.utils import get_user_model


class AvatarAdmin(admin.ModelAdmin):
list_display = ('get_avatar', 'user', 'primary', "date_uploaded")
list_filter = ('primary',)
search_fields = ('user__%s' % getattr(get_user_model(), 'USERNAME_FIELD', 'username'),)
list_per_page = 50

def get_avatar(self, avatar_in):
context = dict({
'user': avatar_in.user,
'url': avatar_in.avatar.url,
'alt': six.text_type(avatar_in.user),
'size': 80,
})
return render_to_string('avatar/avatar_tag.html', context)

get_avatar.short_description = _('Avatar')
get_avatar.allow_tags = True

def save_model(self, request, obj, form, change):
super(AvatarAdmin, self).save_model(request, obj, form, change)
avatar_updated.send(sender=Avatar, user=request.user, avatar=obj)


admin.site.register(Avatar)
admin.site.register(Avatar, AvatarAdmin)
43 changes: 43 additions & 0 deletions avatar/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from django.conf import settings
from PIL import Image

from appconf import AppConf


class AvatarConf(AppConf):
DEFAULT_SIZE = 80
RESIZE_METHOD = Image.ANTIALIAS
STORAGE_DIR = 'avatars'
PATH_HANDLER = 'avatar.models.avatar_path_handler'
GRAVATAR_BASE_URL = 'https://www.gravatar.com/avatar/'
GRAVATAR_FIELD = 'email'
GRAVATAR_DEFAULT = None
AVATAR_GRAVATAR_FORCEDEFAULT = False
DEFAULT_URL = 'avatar/img/default.jpg'
MAX_AVATARS_PER_USER = 42
MAX_SIZE = 1024 * 1024
THUMB_FORMAT = 'JPEG'
THUMB_QUALITY = 85
HASH_FILENAMES = False
HASH_USERDIRNAMES = False
EXPOSE_USERNAMES = True
ALLOWED_FILE_EXTS = None
CACHE_TIMEOUT = 60 * 60
STORAGE = settings.DEFAULT_FILE_STORAGE
CLEANUP_DELETED = False
AUTO_GENERATE_SIZES = (DEFAULT_SIZE,)
FACEBOOK_GET_ID = None
CACHE_ENABLED = True
RANDOMIZE_HASHES = False
ADD_TEMPLATE = ''
CHANGE_TEMPLATE = ''
DELETE_TEMPLATE = ''
PROVIDERS = (
'avatar.providers.PrimaryAvatarProvider',
'avatar.providers.GravatarAvatarProvider',
'avatar.providers.DefaultAvatarProvider',
)

def configure_auto_generate_avatar_sizes(self, value):
return value or getattr(settings, 'AVATAR_AUTO_GENERATE_SIZES',
(self.DEFAULT_SIZE,))
89 changes: 54 additions & 35 deletions avatar/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,67 +3,86 @@
from django import forms
from django.forms import widgets
from django.utils.safestring import mark_safe

# Issue 182: six no longer included with Django 3.0
try:
from django.utils import six
except ImportError:
import six
from django.utils.translation import ugettext_lazy as _
from django.template.defaultfilters import filesizeformat

from avatar.conf import settings
from avatar.models import Avatar
from avatar.settings import (AVATAR_MAX_AVATARS_PER_USER, AVATAR_MAX_SIZE,
AVATAR_ALLOWED_FILE_EXTS, AVATAR_DEFAULT_SIZE)


def avatar_img(avatar, size):
if not avatar.thumbnail_exists(size):
avatar.create_thumbnail(size)
return mark_safe("""<img src="%s" alt="%s" width="%s" height="%s" />""" %
(avatar.avatar_url(size), str(avatar), size, size))
return mark_safe('<img src="%s" alt="%s" width="%s" height="%s" />' %
(avatar.avatar_url(size), six.text_type(avatar),
size, size))


class UploadAvatarForm(forms.Form):
avatar = forms.ImageField(label=_("avatar"))

avatar = forms.ImageField()

def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
super(UploadAvatarForm, self).__init__(*args, **kwargs)

def clean_avatar(self):
data = self.cleaned_data['avatar']
if AVATAR_ALLOWED_FILE_EXTS:
(root, ext) = os.path.splitext(data.name.lower())
if ext not in AVATAR_ALLOWED_FILE_EXTS:
raise forms.ValidationError(
_("%(ext)s is an invalid file extension. Authorized extensions are : %(valid_exts_list)s") %
{ 'ext' : ext, 'valid_exts_list' : ", ".join(AVATAR_ALLOWED_FILE_EXTS) })
if data.size > AVATAR_MAX_SIZE:
raise forms.ValidationError(
_("Your file is too big (%(size)s), the maximum allowed size is %(max_valid_size)s") %
{ 'size' : filesizeformat(data.size), 'max_valid_size' : filesizeformat(AVATAR_MAX_SIZE)} )

if settings.AVATAR_ALLOWED_FILE_EXTS:
root, ext = os.path.splitext(data.name.lower())
if ext not in settings.AVATAR_ALLOWED_FILE_EXTS:
valid_exts = ", ".join(settings.AVATAR_ALLOWED_FILE_EXTS)
error = _("%(ext)s is an invalid file extension. "
"Authorized extensions are : %(valid_exts_list)s")
raise forms.ValidationError(error
% {'ext': ext,
'valid_exts_list': valid_exts})

if data.size > settings.AVATAR_MAX_SIZE:
error = _("Your file is too big (%(size)s), "
"the maximum allowed size is %(max_valid_size)s")
raise forms.ValidationError(error
% {'size': filesizeformat(data.size),
'max_valid_size': filesizeformat(settings.AVATAR_MAX_SIZE)})

count = Avatar.objects.filter(user=self.user).count()
if AVATAR_MAX_AVATARS_PER_USER > 1 and \
count >= AVATAR_MAX_AVATARS_PER_USER:
raise forms.ValidationError(
_("You already have %(nb_avatars)d avatars, and the maximum allowed is %(nb_max_avatars)d.") %
{ 'nb_avatars' : count, 'nb_max_avatars' : AVATAR_MAX_AVATARS_PER_USER})
return

if 1 < settings.AVATAR_MAX_AVATARS_PER_USER <= count:
error = _("You already have %(nb_avatars)d avatars, "
"and the maximum allowed is %(nb_max_avatars)d.")
raise forms.ValidationError(error % {
'nb_avatars': count,
'nb_max_avatars': settings.AVATAR_MAX_AVATARS_PER_USER,
})
return


class PrimaryAvatarForm(forms.Form):

def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
size = kwargs.pop('size', AVATAR_DEFAULT_SIZE)
kwargs.pop('user')
size = kwargs.pop('size', settings.AVATAR_DEFAULT_SIZE)
avatars = kwargs.pop('avatars')
super(PrimaryAvatarForm, self).__init__(*args, **kwargs)
self.fields['choice'] = forms.ChoiceField(
choices=[(c.id, avatar_img(c, size)) for c in avatars],
widget=widgets.RadioSelect)
choices = [(avatar.id, avatar_img(avatar, size)) for avatar in avatars]
self.fields['choice'] = forms.ChoiceField(label=_("Choices"),
choices=choices,
widget=widgets.RadioSelect)


class DeleteAvatarForm(forms.Form):

def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
size = kwargs.pop('size', AVATAR_DEFAULT_SIZE)
kwargs.pop('user')
size = kwargs.pop('size', settings.AVATAR_DEFAULT_SIZE)
avatars = kwargs.pop('avatars')
super(DeleteAvatarForm, self).__init__(*args, **kwargs)
self.fields['choices'] = forms.MultipleChoiceField(
choices=[(c.id, avatar_img(c, size)) for c in avatars],
widget=widgets.CheckboxSelectMultiple)
choices = [(avatar.id, avatar_img(avatar, size)) for avatar in avatars]
self.fields['choices'] = forms.MultipleChoiceField(label=_("Choices"),
choices=choices,
widget=widgets.CheckboxSelectMultiple)
Binary file modified avatar/locale/de/LC_MESSAGES/django.mo
Binary file not shown.
Loading

0 comments on commit 9a0fd60

Please sign in to comment.