2013-03-22 20:55:28 +00:00
|
|
|
import hashlib
|
|
|
|
|
|
2022-08-09 19:17:24 +00:00
|
|
|
from django.contrib.auth import get_user_model
|
2010-06-26 14:49:35 +00:00
|
|
|
from django.core.cache import cache
|
2013-03-08 11:16:21 +00:00
|
|
|
from django.template.defaultfilters import slugify
|
2022-07-16 20:26:58 +00:00
|
|
|
from django.utils.encoding import force_bytes
|
2010-02-22 23:48:22 +00:00
|
|
|
|
2013-09-13 15:59:52 +00:00
|
|
|
from avatar.conf import settings
|
|
|
|
|
|
2010-06-26 14:49:35 +00:00
|
|
|
cached_funcs = set()
|
|
|
|
|
|
2013-08-01 11:12:49 +00:00
|
|
|
|
2013-03-15 10:45:24 +00:00
|
|
|
def get_username(user):
|
2022-07-16 21:18:09 +00:00
|
|
|
"""Return username of a User instance"""
|
2022-07-16 20:50:05 +00:00
|
|
|
if hasattr(user, "get_username"):
|
2013-03-15 10:45:24 +00:00
|
|
|
return user.get_username()
|
|
|
|
|
else:
|
|
|
|
|
return user.username
|
|
|
|
|
|
2013-08-01 11:12:49 +00:00
|
|
|
|
2022-08-15 08:08:35 +00:00
|
|
|
def get_user(userdescriptor):
|
|
|
|
|
"""Return user from a username/ID/ish identifier"""
|
|
|
|
|
User = get_user_model()
|
2023-10-16 10:34:48 +00:00
|
|
|
if isinstance(userdescriptor, int):
|
|
|
|
|
user = User.objects.filter(id=userdescriptor).first()
|
|
|
|
|
if user:
|
|
|
|
|
return user
|
|
|
|
|
elif userdescriptor.isdigit():
|
2022-08-15 08:08:35 +00:00
|
|
|
user = User.objects.filter(id=int(userdescriptor)).first()
|
|
|
|
|
if user:
|
|
|
|
|
return user
|
|
|
|
|
return User.objects.get_by_natural_key(userdescriptor)
|
2013-03-15 10:45:24 +00:00
|
|
|
|
2013-03-11 15:51:23 +00:00
|
|
|
|
2022-08-15 09:34:35 +00:00
|
|
|
def get_cache_key(user_or_username, prefix, width=None, height=None):
|
2010-06-26 15:20:44 +00:00
|
|
|
"""
|
|
|
|
|
Returns a cache key consisten of a username and image size.
|
|
|
|
|
"""
|
2013-08-01 11:12:49 +00:00
|
|
|
if isinstance(user_or_username, get_user_model()):
|
2013-03-15 10:45:24 +00:00
|
|
|
user_or_username = get_username(user_or_username)
|
2022-08-15 08:44:04 +00:00
|
|
|
key = f"{prefix}_{user_or_username}"
|
|
|
|
|
if width:
|
|
|
|
|
key += f"_{width}"
|
|
|
|
|
if height or width:
|
|
|
|
|
key += f"x{height or width}"
|
2022-07-16 21:25:43 +00:00
|
|
|
return "%s_%s" % (
|
2022-07-16 20:50:05 +00:00
|
|
|
slugify(key)[:100],
|
|
|
|
|
hashlib.md5(force_bytes(key)).hexdigest(),
|
|
|
|
|
)
|
2013-03-08 11:05:03 +00:00
|
|
|
|
2010-06-26 14:49:35 +00:00
|
|
|
|
2013-09-13 15:24:34 +00:00
|
|
|
def cache_set(key, value):
|
2013-09-13 15:59:52 +00:00
|
|
|
cache.set(key, value, settings.AVATAR_CACHE_TIMEOUT)
|
2013-09-13 15:24:34 +00:00
|
|
|
return value
|
|
|
|
|
|
|
|
|
|
|
2013-09-13 15:59:52 +00:00
|
|
|
def cache_result(default_size=settings.AVATAR_DEFAULT_SIZE):
|
2010-06-26 15:20:44 +00:00
|
|
|
"""
|
2022-08-15 08:08:35 +00:00
|
|
|
Decorator to cache the result of functions that take a ``user``, a
|
|
|
|
|
``width`` and a ``height`` value.
|
2010-06-26 15:20:44 +00:00
|
|
|
"""
|
2016-02-09 15:06:16 +00:00
|
|
|
if not settings.AVATAR_CACHE_ENABLED:
|
2022-07-16 20:50:05 +00:00
|
|
|
|
2016-01-18 10:14:15 +00:00
|
|
|
def decorator(func):
|
|
|
|
|
return func
|
2022-07-16 20:50:05 +00:00
|
|
|
|
2016-01-18 10:14:15 +00:00
|
|
|
return decorator
|
|
|
|
|
|
2013-09-13 15:24:34 +00:00
|
|
|
def decorator(func):
|
2022-08-15 08:08:35 +00:00
|
|
|
def cached_func(user, width=None, height=None, **kwargs):
|
2013-09-13 15:24:34 +00:00
|
|
|
prefix = func.__name__
|
|
|
|
|
cached_funcs.add(prefix)
|
2022-08-15 08:44:04 +00:00
|
|
|
key = get_cache_key(user, prefix, width or default_size, height)
|
2013-09-13 15:24:34 +00:00
|
|
|
result = cache.get(key)
|
|
|
|
|
if result is None:
|
2022-08-15 08:08:35 +00:00
|
|
|
result = func(user, width or default_size, height, **kwargs)
|
2013-09-13 15:24:34 +00:00
|
|
|
cache_set(key, result)
|
2019-02-07 16:27:18 +00:00
|
|
|
# add image size to set of cached sizes so we can invalidate them later
|
2022-08-15 08:44:04 +00:00
|
|
|
sizes_key = get_cache_key(user, "cached_sizes")
|
2019-02-07 16:27:18 +00:00
|
|
|
sizes = cache.get(sizes_key, set())
|
2022-08-15 09:34:35 +00:00
|
|
|
sizes.add((width or default_size, height or width or default_size))
|
2019-02-07 16:27:18 +00:00
|
|
|
cache_set(sizes_key, sizes)
|
2013-09-13 15:24:34 +00:00
|
|
|
return result
|
2022-07-16 20:50:05 +00:00
|
|
|
|
2013-09-13 15:24:34 +00:00
|
|
|
return cached_func
|
2022-07-16 20:50:05 +00:00
|
|
|
|
2013-09-13 15:24:34 +00:00
|
|
|
return decorator
|
2010-06-26 14:49:35 +00:00
|
|
|
|
2013-08-01 11:12:49 +00:00
|
|
|
|
2022-08-15 08:08:35 +00:00
|
|
|
def invalidate_cache(user, width=None, height=None):
|
2010-06-26 15:20:44 +00:00
|
|
|
"""
|
2022-08-15 08:08:35 +00:00
|
|
|
Function to be called when saving or changing a user's avatars.
|
2010-06-26 15:20:44 +00:00
|
|
|
"""
|
2022-08-15 08:44:04 +00:00
|
|
|
sizes_key = get_cache_key(user, "cached_sizes")
|
2019-02-07 16:27:18 +00:00
|
|
|
sizes = cache.get(sizes_key, set())
|
2022-08-15 08:08:35 +00:00
|
|
|
if width is not None:
|
|
|
|
|
sizes.add((width, height or width))
|
2010-06-26 14:49:35 +00:00
|
|
|
for prefix in cached_funcs:
|
|
|
|
|
for size in sizes:
|
2022-08-15 08:08:35 +00:00
|
|
|
if isinstance(size, int):
|
2022-08-15 08:44:04 +00:00
|
|
|
cache.delete(get_cache_key(user, prefix, size))
|
2022-08-15 08:08:35 +00:00
|
|
|
else:
|
|
|
|
|
# Size is specified with height and width.
|
2022-08-15 08:44:04 +00:00
|
|
|
cache.delete(get_cache_key(user, prefix, size[0], size[1]))
|
2022-08-15 09:34:35 +00:00
|
|
|
cache.set(sizes_key, set())
|
2010-02-22 23:48:22 +00:00
|
|
|
|
2013-08-01 11:12:49 +00:00
|
|
|
|
2010-02-22 23:48:22 +00:00
|
|
|
def get_default_avatar_url():
|
2022-07-16 20:50:05 +00:00
|
|
|
base_url = getattr(settings, "STATIC_URL", None)
|
2010-02-22 23:48:22 +00:00
|
|
|
if not base_url:
|
2022-07-16 20:50:05 +00:00
|
|
|
base_url = getattr(settings, "MEDIA_URL", "")
|
2013-09-13 15:59:52 +00:00
|
|
|
|
|
|
|
|
# Don't use base_url if the default url starts with http:// of https://
|
2022-07-16 20:50:05 +00:00
|
|
|
if settings.AVATAR_DEFAULT_URL.startswith(("http://", "https://")):
|
2013-09-13 15:59:52 +00:00
|
|
|
return settings.AVATAR_DEFAULT_URL
|
2010-02-22 23:48:22 +00:00
|
|
|
# We'll be nice and make sure there are no duplicated forward slashes
|
2022-07-16 20:50:05 +00:00
|
|
|
ends = base_url.endswith("/")
|
2013-09-13 15:59:52 +00:00
|
|
|
|
2022-07-16 20:50:05 +00:00
|
|
|
begins = settings.AVATAR_DEFAULT_URL.startswith("/")
|
2010-02-22 23:48:22 +00:00
|
|
|
if ends and begins:
|
|
|
|
|
base_url = base_url[:-1]
|
|
|
|
|
elif not ends and not begins:
|
2022-07-16 20:50:05 +00:00
|
|
|
return "%s/%s" % (base_url, settings.AVATAR_DEFAULT_URL)
|
2013-09-13 15:59:52 +00:00
|
|
|
|
2022-07-16 20:50:05 +00:00
|
|
|
return "%s%s" % (base_url, settings.AVATAR_DEFAULT_URL)
|
2010-02-22 23:48:22 +00:00
|
|
|
|
2013-08-01 11:12:49 +00:00
|
|
|
|
2022-08-15 08:08:35 +00:00
|
|
|
def get_primary_avatar(user, width=settings.AVATAR_DEFAULT_SIZE, height=None):
|
2013-08-01 11:12:49 +00:00
|
|
|
User = get_user_model()
|
2010-02-22 23:48:22 +00:00
|
|
|
if not isinstance(user, User):
|
|
|
|
|
try:
|
2013-03-15 10:45:24 +00:00
|
|
|
user = get_user(user)
|
2010-02-22 23:48:22 +00:00
|
|
|
except User.DoesNotExist:
|
2010-02-25 11:45:23 +00:00
|
|
|
return None
|
2010-06-24 15:59:14 +00:00
|
|
|
try:
|
|
|
|
|
# Order by -primary first; this means if a primary=True avatar exists
|
|
|
|
|
# it will be first, and then ordered by date uploaded, otherwise a
|
|
|
|
|
# primary=False avatar will be first. Exactly the fallback behavior we
|
|
|
|
|
# want.
|
|
|
|
|
avatar = user.avatar_set.order_by("-primary", "-date_uploaded")[0]
|
|
|
|
|
except IndexError:
|
2010-02-22 23:48:22 +00:00
|
|
|
avatar = None
|
|
|
|
|
if avatar:
|
2022-08-15 08:08:35 +00:00
|
|
|
if not avatar.thumbnail_exists(width, height):
|
|
|
|
|
avatar.create_thumbnail(width, height)
|
2010-03-17 15:21:02 +00:00
|
|
|
return avatar
|