mirror of
https://git.linux-kernel.at/oliver/ivatar.git
synced 2025-11-13 11:46:22 +00:00
Compare commits
22 Commits
django-4.1
...
1.7.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8292b5404 | ||
|
|
5730c2dabf | ||
|
|
dddd24e57f | ||
|
|
a6c5899f44 | ||
|
|
ba6f46c6eb | ||
|
|
ddfc1e7824 | ||
|
|
2761e801df | ||
|
|
555a8b0523 | ||
|
|
6d984a486a | ||
|
|
9dceb7a696 | ||
|
|
64575a9b99 | ||
|
|
a94954d58c | ||
|
|
f359532c30 | ||
|
|
a76d5b9225 | ||
|
|
71d69dde53 | ||
|
|
0b5271424c | ||
|
|
a492995836 | ||
|
|
ad39324650 | ||
|
|
ef02feed3b | ||
|
|
1a10861d2f | ||
|
|
b64f939344 | ||
|
|
ddaf6a6d8a |
@@ -13,7 +13,7 @@ repos:
|
||||
hooks:
|
||||
- id: black
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.3.0
|
||||
rev: v4.4.0
|
||||
hooks:
|
||||
- id: check-added-large-files
|
||||
- id: check-ast
|
||||
@@ -38,7 +38,7 @@ repos:
|
||||
- id: sort-simple-yaml
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 5.0.4
|
||||
rev: 6.0.0
|
||||
hooks:
|
||||
- id: flake8
|
||||
- repo: local
|
||||
|
||||
@@ -41,12 +41,14 @@ def file_format(image_type):
|
||||
"""
|
||||
Helper method returning a short image type
|
||||
"""
|
||||
if image_type == "JPEG":
|
||||
if image_type in ("JPEG", "MPO"):
|
||||
return "jpg"
|
||||
elif image_type == "PNG":
|
||||
return "png"
|
||||
elif image_type == "GIF":
|
||||
return "gif"
|
||||
elif image_type == "WEBP":
|
||||
return "webp"
|
||||
return None
|
||||
|
||||
|
||||
@@ -54,12 +56,14 @@ def pil_format(image_type):
|
||||
"""
|
||||
Helper method returning the 'encoder name' for PIL
|
||||
"""
|
||||
if image_type == "jpg" or image_type == "jpeg":
|
||||
if image_type in ("jpg", "jpeg", "mpo"):
|
||||
return "JPEG"
|
||||
elif image_type == "png":
|
||||
return "PNG"
|
||||
elif image_type == "gif":
|
||||
return "GIF"
|
||||
elif image_type == "webp":
|
||||
return "WEBP"
|
||||
|
||||
logger.info("Unsupported file format: %s", image_type)
|
||||
return None
|
||||
|
||||
@@ -748,6 +748,47 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
||||
response = self.client.get(url, follow=True)
|
||||
self.assertEqual(response.status_code, 200, "unable to fetch avatar?")
|
||||
|
||||
def test_upload_webp_image(self):
|
||||
"""
|
||||
Test if webp is correctly detected and can be viewed
|
||||
"""
|
||||
self.login()
|
||||
url = reverse("upload_photo")
|
||||
# rb => Read binary
|
||||
# Broken is _not_ broken - it's just an 'x' :-)
|
||||
with open(
|
||||
os.path.join(settings.STATIC_ROOT, "img", "broken.webp"), "rb"
|
||||
) as photo:
|
||||
response = self.client.post(
|
||||
url,
|
||||
{
|
||||
"photo": photo,
|
||||
"not_porn": True,
|
||||
"can_distribute": True,
|
||||
},
|
||||
follow=True,
|
||||
)
|
||||
self.assertEqual(
|
||||
str(list(response.context[0]["messages"])[0]),
|
||||
"Successfully uploaded",
|
||||
"WEBP upload failed?!",
|
||||
)
|
||||
self.assertEqual(
|
||||
self.user.photo_set.first().format,
|
||||
"webp",
|
||||
"Format must be webp, since we uploaded a webp!",
|
||||
)
|
||||
self.test_confirm_email()
|
||||
self.user.confirmedemail_set.first().photo = self.user.photo_set.first()
|
||||
urlobj = urlsplit(
|
||||
libravatar_url(
|
||||
email=self.user.confirmedemail_set.first().email,
|
||||
)
|
||||
)
|
||||
url = "%s?%s" % (urlobj.path, urlobj.query)
|
||||
response = self.client.get(url, follow=True)
|
||||
self.assertEqual(response.status_code, 200, "unable to fetch avatar?")
|
||||
|
||||
def test_upload_unsupported_tif_image(self): # pylint: disable=invalid-name
|
||||
"""
|
||||
Test if unsupported format is correctly detected
|
||||
|
||||
BIN
ivatar/static/img/broken.webp
Normal file
BIN
ivatar/static/img/broken.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
@@ -37,6 +37,7 @@ class Tester(TestCase):
|
||||
self.assertEqual(pil_format("jpeg"), "JPEG")
|
||||
self.assertEqual(pil_format("png"), "PNG")
|
||||
self.assertEqual(pil_format("gif"), "GIF")
|
||||
self.assertEqual(pil_format("webp"), "WEBP")
|
||||
self.assertEqual(pil_format("abc"), None)
|
||||
|
||||
def test_userprefs_str(self):
|
||||
|
||||
@@ -4,7 +4,8 @@ Simple module providing reusable random_string function
|
||||
"""
|
||||
import random
|
||||
import string
|
||||
from PIL import Image, ImageDraw
|
||||
from io import BytesIO
|
||||
from PIL import Image, ImageDraw, ImageSequence
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
||||
@@ -158,3 +159,25 @@ def is_trusted_url(url, url_filters):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def resize_animated_gif(input_pil: Image, size: list) -> BytesIO:
|
||||
def _thumbnail_frames(image):
|
||||
for frame in ImageSequence.Iterator(image):
|
||||
new_frame = frame.copy()
|
||||
new_frame.thumbnail(size)
|
||||
yield new_frame
|
||||
|
||||
frames = list(_thumbnail_frames(input_pil))
|
||||
output = BytesIO()
|
||||
output_image = frames[0]
|
||||
output_image.save(
|
||||
output,
|
||||
format="gif",
|
||||
save_all=True,
|
||||
optimize=False,
|
||||
append_images=frames[1:],
|
||||
disposal=input_pil.disposal_method,
|
||||
**input_pil.info,
|
||||
)
|
||||
return output
|
||||
|
||||
@@ -34,7 +34,7 @@ from .ivataraccount.models import ConfirmedEmail, ConfirmedOpenId
|
||||
from .ivataraccount.models import UnconfirmedEmail, UnconfirmedOpenId
|
||||
from .ivataraccount.models import Photo
|
||||
from .ivataraccount.models import pil_format, file_format
|
||||
from .utils import is_trusted_url, mm_ng
|
||||
from .utils import is_trusted_url, mm_ng, resize_animated_gif
|
||||
|
||||
URL_TIMEOUT = 5 # in seconds
|
||||
|
||||
@@ -151,7 +151,8 @@ class AvatarImageView(TemplateView):
|
||||
|
||||
if not trusted_url:
|
||||
print(
|
||||
"Default URL is not in trusted URLs: '%s' ; Kicking it!" % default
|
||||
"Default URL is not in trusted URLs: '%s' ; Kicking it!"
|
||||
% default
|
||||
)
|
||||
default = None
|
||||
|
||||
@@ -318,14 +319,23 @@ class AvatarImageView(TemplateView):
|
||||
|
||||
imgformat = obj.photo.format
|
||||
photodata = Image.open(BytesIO(obj.photo.data))
|
||||
# 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)
|
||||
|
||||
# Animated GIFs need additional handling
|
||||
if imgformat == "gif" and photodata.is_animated:
|
||||
# Debug only
|
||||
# print("Object is animated and has %i frames" % photodata.n_frames)
|
||||
data = resize_animated_gif(photodata, (size, size))
|
||||
else:
|
||||
# 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)
|
||||
photodata.save(data, pil_format(imgformat), quality=JPEG_QUALITY)
|
||||
|
||||
data.seek(0)
|
||||
obj.photo.access_count += 1
|
||||
obj.photo.save()
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
<p>
|
||||
<button type="submit" class="button">{% trans 'Login' %}</button>
|
||||
<input type="hidden" name="next" value="{{ request.build_absolute_uri }}{% url 'profile' %}" />
|
||||
<input type="hidden" name="next" value="{% url 'profile' %}" />
|
||||
|
||||
<button type="reset" class="button" onclick="window.history.back();">{% trans 'Cancel' %}</button>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user