mirror of
https://github.com/jazzband/django-avatar.git
synced 2026-03-16 22:20:30 +00:00
Completely refactored this module. The model has been changed to reflect more sane defaults. A new set of templatetags have been created as well, however, the gravatar public API has been preserved. The only thing that still needs to be done is to rewrite the administrative views for uploading/deleting/changing avatars.
git-svn-id: http://django-avatar.googlecode.com/svn/trunk@19 c76b2324-5f53-0410-85ac-b1078a54aeeb
This commit is contained in:
parent
2139fc5979
commit
810ce3f8e4
5 changed files with 149 additions and 52 deletions
|
|
@ -1,14 +1,18 @@
|
|||
from django.contrib.auth.models import User
|
||||
from models import Avatar
|
||||
from django.db.models import signals
|
||||
from django.contrib.auth.models import User
|
||||
from django.conf import settings
|
||||
|
||||
def create_avatar(sender=None, instance=None, **kwargs):
|
||||
avatar, created = Avatar.objects.get_or_create(user=instance)
|
||||
avatar.save()
|
||||
def delete_avatar(sender=None, instance=None, **kwargs):
|
||||
try:
|
||||
Avatar.objects.get(user=instance).delete()
|
||||
except Avatar.DoesNotExist:
|
||||
pass
|
||||
signals.post_save.connect(create_avatar, sender=User)
|
||||
signals.post_delete.connect(delete_avatar, sender=User)
|
||||
AUTO_GENERATE_AVATAR_SIZES = getattr(settings, 'AUTO_GENERATE_AVATAR_SIZES', (80,))
|
||||
|
||||
def update_email_hash(sender=None, instance=None, **kwargs):
|
||||
for avatar in instance.avatar_set.all():
|
||||
avatar.save()
|
||||
signals.post_save.connect(update_email_hash, sender=User)
|
||||
|
||||
def create_default_thumbnails(instance=None, created=False, **kwargs):
|
||||
if created:
|
||||
for size in AUTO_GENERATE_AVATAR_SIZES:
|
||||
instance.create_thumbnail(size)
|
||||
avatar, created = Avatar.objects.get_or_create(user=instance)
|
||||
signals.post_save.connect(create_default_thumbnails, sender=Avatar)
|
||||
|
|
@ -1,31 +1,82 @@
|
|||
import re
|
||||
import datetime
|
||||
import os.path
|
||||
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.files.storage import default_storage
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
try:
|
||||
from hashlib import md5
|
||||
except ImportError:
|
||||
from md5 import new as md5
|
||||
try:
|
||||
from PIL import ImageFile
|
||||
except ImportError:
|
||||
import ImageFile
|
||||
try:
|
||||
from PIL import Image
|
||||
except ImportError:
|
||||
import Image
|
||||
|
||||
RATING_CHOICES = (
|
||||
('g', 'G'),
|
||||
('pg', 'PG'),
|
||||
('r', 'R'),
|
||||
('x', 'X'),
|
||||
)
|
||||
WIDTH_HEIGHT_RE = re.compile(r'\[(\d+)x(\d+)\]')
|
||||
|
||||
AUTO_GENERATE_AVATAR_SIZES = getattr(settings, 'AUTO_GENERATE_AVATAR_SIZES', (80,))
|
||||
AVATAR_RESIZE_METHOD = getattr(settings, 'AVATAR_RESIZE_METHOD', Image.ANTIALIAS)
|
||||
|
||||
def avatar_file_path(instance=None, filename=None):
|
||||
return 'avatars/%s/[%sx%s]%s' % (instance.user.username,
|
||||
instance.avatar.width, instance.avatar.height, filename)
|
||||
|
||||
class Avatar(models.Model):
|
||||
email_hash = models.CharField(max_length=128, blank=True)
|
||||
user = models.OneToOneField(User)
|
||||
# This should have a subdirectory of each user's avatar, but first we need
|
||||
# patch #5361 to land into Trunk.
|
||||
avatar = models.FileField(upload_to='avatars/', default='DEFAULT')
|
||||
rating = models.CharField(choices=RATING_CHOICES, max_length=2, default='g')
|
||||
user = models.ForeignKey(User)
|
||||
primary = models.BooleanField(default=False)
|
||||
avatar = models.ImageField(upload_to=avatar_file_path, blank=True)
|
||||
date_uploaded = models.DateTimeField(default=datetime.datetime.now)
|
||||
|
||||
def __unicode__(self):
|
||||
return u'Gravatar for %s' % self.user
|
||||
return _(u'Avatar for %s' % self.user)
|
||||
|
||||
def save(self):
|
||||
self.email_hash = md5(self.user.email).hexdigest().lower()
|
||||
super(Avatar, self).save()
|
||||
if self.primary:
|
||||
avatars = Avatar.objects.filter(user=self.user, primary=True)
|
||||
avatars.update(primary=False)
|
||||
super(Avatar, self).save()
|
||||
|
||||
def thumbnail_exists(self, size):
|
||||
if size in AUTO_GENERATE_AVATAR_SIZES:
|
||||
return True
|
||||
new_path_fragment = '[%sx%s]' % (size, size)
|
||||
path = WIDTH_HEIGHT_RE.sub(new_path_fragment, self.avatar.path)
|
||||
return default_storage.exists(path)
|
||||
|
||||
def create_thumbnail(self, size):
|
||||
orig = default_storage.open(self.avatar.path, 'rb').read()
|
||||
p = ImageFile.Parser()
|
||||
p.feed(orig)
|
||||
try:
|
||||
image = p.close()
|
||||
except IOError:
|
||||
return # What should we do here? Render a "sorry, didn't work" img?
|
||||
(w, h) = image.size
|
||||
if w > h:
|
||||
diff = (w - h) / 2
|
||||
image = image.crop((diff, 0, w - diff, h))
|
||||
else:
|
||||
diff = (h - w) / 2
|
||||
image = image.crop((0, diff, w, h - diff))
|
||||
image = image.resize((size, size), AVATAR_RESIZE_METHOD)
|
||||
thumb = default_storage.open(self.avatar_path(size), 'wb')
|
||||
image.save(thumb, "JPEG")
|
||||
|
||||
def avatar_url(self, size):
|
||||
new_url_fragment = '[%sx%s]' % (size, size)
|
||||
return WIDTH_HEIGHT_RE.sub(new_url_fragment, self.avatar.url)
|
||||
|
||||
def avatar_path(self, size):
|
||||
new_path_fragment = '[%sx%s]' % (size, size)
|
||||
return WIDTH_HEIGHT_RE.sub(new_path_fragment, self.avatar.path)
|
||||
0
avatar/templatetags/__init__.py
Normal file
0
avatar/templatetags/__init__.py
Normal file
55
avatar/templatetags/avatar_tags.py
Normal file
55
avatar/templatetags/avatar_tags.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import os.path
|
||||
|
||||
from django import template
|
||||
from django.utils.html import escape
|
||||
from django.contrib.auth.models import User
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
register = template.Library()
|
||||
|
||||
AVATAR_GRAVATAR_BACKUP = getattr(settings, 'AVATAR_GRAVATAR_BACKUP', True)
|
||||
AVATAR_DEFAULT_URL = getattr(settings, 'AVATAR_DEFAULT_URL',
|
||||
settings.MEDIA_URL + os.path.join(os.path.dirname(__file__), 'default.jpg'))
|
||||
|
||||
def avatar_url(user, size=80):
|
||||
if not isinstance(user, User):
|
||||
try:
|
||||
user = User.objects.get(username=user)
|
||||
except User.DoesNotExist:
|
||||
return AVATAR_DEFAULT_URL
|
||||
avatars = user.avatar_set.order_by('-date_uploaded')
|
||||
primary = avatars.filter(primary=True)
|
||||
if primary.count() > 0:
|
||||
avatar = primary[0]
|
||||
elif avatars.count() > 0:
|
||||
avatar = avatars[0]
|
||||
else:
|
||||
avatar = None
|
||||
if avatar is not None:
|
||||
if not avatar.thumbnail_exists(size):
|
||||
avatar.create_thumbnail(size)
|
||||
return avatar.avatar_url(size)
|
||||
else:
|
||||
if AVATAR_GRAVATAR_BACKUP:
|
||||
return "http://www.gravatar.com/avatar/%s/?" % (
|
||||
hashlib.md5(email).hexdigest(),
|
||||
urllib.urlencode({'s': str(size)}),)
|
||||
else:
|
||||
return AVATAR_DEFAULT_URL
|
||||
register.simple_tag(avatar_url)
|
||||
|
||||
def avatar(user, size=80):
|
||||
if not isinstance(user, User):
|
||||
try:
|
||||
user = User.objects.get(username=user)
|
||||
alt = unicode(user)
|
||||
url = avatar_url(user, size)
|
||||
except User.DoesNotExist:
|
||||
url = AVATAR_DEFAULT_URL
|
||||
alt = _("Default Avatar")
|
||||
else:
|
||||
alt = unicode(user)
|
||||
url = avatar_url(user, size)
|
||||
return """<img src="%s" alt="%s" />""" % (url, alt)
|
||||
register.simple_tag(avatar)
|
||||
|
|
@ -1,14 +1,6 @@
|
|||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
try:
|
||||
from PIL import ImageFile
|
||||
except ImportError:
|
||||
import ImageFile
|
||||
try:
|
||||
from PIL import Image
|
||||
except ImportError:
|
||||
import Image
|
||||
import shutil
|
||||
|
||||
from models import Avatar
|
||||
|
|
@ -20,17 +12,25 @@ from django.template import RequestContext
|
|||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.core.cache import cache
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
from StringIO import StringIO
|
||||
|
||||
try:
|
||||
from PIL import ImageFile
|
||||
except ImportError:
|
||||
import ImageFile
|
||||
try:
|
||||
from PIL import Image
|
||||
except ImportError:
|
||||
import Image
|
||||
|
||||
MAX_MEGABYTES = getattr(settings, 'AVATAR_MAX_FILESIZE', 10)
|
||||
MAX_WIDTH = getattr(settings, 'AVATAR_MAX_WIDTH', 512)
|
||||
DEFAULT_WIDTH = getattr(settings, 'AVATAR_DEFAULT_WIDTH', 80)
|
||||
AVATAR_CACHE_SECONDS = getattr(settings, 'AVATAR_CACHE_SECONDS', None)
|
||||
AVATAR_RESIZE_METHOD = getattr(settings, 'AVATAR_RESIZE_METHOD', Image.ANTIALIAS)
|
||||
|
||||
def _get_next(request):
|
||||
"""
|
||||
|
|
@ -52,7 +52,7 @@ def _get_next(request):
|
|||
raise Http404 # No next url was supplied in GET or POST.
|
||||
return next
|
||||
|
||||
def img(request, email_hash, resize_method=Image.ANTIALIAS):
|
||||
def img(request, email_hash):
|
||||
if email_hash.endswith('.jpg'):
|
||||
email_hash = email_hash[:-4]
|
||||
try:
|
||||
|
|
@ -63,21 +63,16 @@ def img(request, email_hash, resize_method=Image.ANTIALIAS):
|
|||
size = MAX_WIDTH
|
||||
rating = request.GET.get('r', 'g') # Unused, for now.
|
||||
default = request.GET.get('d', '')
|
||||
if AVATAR_CACHE_SECONDS:
|
||||
cache_key = '%s-%s' % (email_hash, str(size))
|
||||
cached = cache.get(cache_key)
|
||||
if cached:
|
||||
return cached
|
||||
data = None
|
||||
try:
|
||||
avatar = Avatar.objects.get(email_hash=email_hash)
|
||||
except Avatar.DoesNotExist:
|
||||
avatar = Avatar.objects.filter(email_hash=email_hash).order_by('-primary', '-date_uploaded')[0]
|
||||
except IndexError:
|
||||
avatar = None
|
||||
except Avatar.MultipleObjectsReturned:
|
||||
avatar = None
|
||||
try:
|
||||
if avatar is not None:
|
||||
data = open(avatar.get_avatar_filename(), 'r').read()
|
||||
data = open(avatar.avatar.path, 'r').read()
|
||||
except IOError:
|
||||
pass
|
||||
if not data and default:
|
||||
|
|
@ -106,11 +101,9 @@ def img(request, email_hash, resize_method=Image.ANTIALIAS):
|
|||
else:
|
||||
diff = (height - width) / 2
|
||||
image = image.crop((0, diff, width, height - diff))
|
||||
image = image.resize((size, size), resize_method)
|
||||
image = image.resize((size, size), AVATAR_RESIZE_METHOD)
|
||||
response = HttpResponse(mimetype='image/jpeg')
|
||||
image.save(response, "JPEG")
|
||||
if AVATAR_CACHE_SECONDS:
|
||||
cache.set(cache_key, response, AVATAR_CACHE_SECONDS)
|
||||
return response
|
||||
|
||||
def change(request, extra_context={}, next_override=None):
|
||||
|
|
@ -134,9 +127,6 @@ def change(request, extra_context={}, next_override=None):
|
|||
avatar.save()
|
||||
request.user.message_set.create(
|
||||
message=_("Successfully updated your avatar."))
|
||||
if AVATAR_CACHE_SECONDS:
|
||||
for i in xrange(512):
|
||||
cache.delete('%s-%s' % (avatar.email_hash, str(i)))
|
||||
return HttpResponseRedirect(next_override or _get_next(request))
|
||||
return render_to_response(
|
||||
'avatar/change.html',
|
||||
|
|
@ -159,9 +149,6 @@ def delete(request, extra_context={}, next_override=None):
|
|||
avatar.save()
|
||||
request.user.message_set.create(
|
||||
message=_("Successfully removed your avatar."))
|
||||
if AVATAR_CACHE_SECONDS:
|
||||
for i in xrange(512):
|
||||
cache.delete('%s-%s' % (avatar.email_hash, str(i)))
|
||||
next = next_override or _get_next(request)
|
||||
return HttpResponseRedirect(next)
|
||||
return render_to_response(
|
||||
|
|
|
|||
Loading…
Reference in a new issue