mirror of
https://git.linux-kernel.at/oliver/ivatar.git
synced 2025-11-11 10:46:24 +00:00
Code cleanup/refactoring
This commit is contained in:
2
.flake8
2
.flake8
@@ -1,5 +1,5 @@
|
||||
[flake8]
|
||||
ignore = E501, W503, E402, C901, E231
|
||||
ignore = E501, W503, E402, C901, E231, E702
|
||||
max-line-length = 79
|
||||
max-complexity = 18
|
||||
select = B,C,E,F,W,T4,B9
|
||||
|
||||
@@ -118,9 +118,7 @@ class UploadPhotoForm(forms.Form):
|
||||
photo.ip_address = get_client_ip(request)[0]
|
||||
photo.data = data.read()
|
||||
photo.save()
|
||||
if not photo.pk:
|
||||
return None
|
||||
return photo
|
||||
return None if not photo.pk else photo
|
||||
|
||||
|
||||
class AddOpenIDForm(forms.Form):
|
||||
@@ -141,13 +139,16 @@ class AddOpenIDForm(forms.Form):
|
||||
"""
|
||||
# Lowercase hostname port of the URL
|
||||
url = urlsplit(self.cleaned_data["openid"])
|
||||
data = urlunsplit(
|
||||
(url.scheme.lower(), url.netloc.lower(), url.path, url.query, url.fragment)
|
||||
return urlunsplit(
|
||||
(
|
||||
url.scheme.lower(),
|
||||
url.netloc.lower(),
|
||||
url.path,
|
||||
url.query,
|
||||
url.fragment,
|
||||
)
|
||||
)
|
||||
|
||||
# TODO: Domain restriction as in libravatar?
|
||||
return data
|
||||
|
||||
def save(self, user):
|
||||
"""
|
||||
Save the model, ensuring some safety
|
||||
|
||||
@@ -346,7 +346,7 @@ class ConfirmedEmail(BaseAccountModel):
|
||||
handle = bs.normalize_handle(handle)
|
||||
avatar = bs.get_profile(handle)
|
||||
if not avatar:
|
||||
raise Exception("Invalid Bluesky handle")
|
||||
raise ValueError("Invalid Bluesky handle")
|
||||
self.bluesky_handle = handle
|
||||
self.save()
|
||||
|
||||
@@ -499,7 +499,7 @@ class ConfirmedOpenId(BaseAccountModel):
|
||||
handle = bs.normalize_handle(handle)
|
||||
avatar = bs.get_profile(handle)
|
||||
if not avatar:
|
||||
raise Exception("Invalid Bluesky handle")
|
||||
raise ValueError("Invalid Bluesky handle")
|
||||
self.bluesky_handle = handle
|
||||
self.save()
|
||||
|
||||
|
||||
@@ -32,8 +32,6 @@ def read_gzdata(gzdata=None):
|
||||
"""
|
||||
Read gzipped data file
|
||||
"""
|
||||
emails = [] # pylint: disable=invalid-name
|
||||
openids = [] # pylint: disable=invalid-name
|
||||
photos = [] # pylint: disable=invalid-name
|
||||
username = None # pylint: disable=invalid-name
|
||||
password = None # pylint: disable=invalid-name
|
||||
@@ -45,8 +43,8 @@ def read_gzdata(gzdata=None):
|
||||
content = fh.read()
|
||||
fh.close()
|
||||
root = xml.etree.ElementTree.fromstring(content)
|
||||
if not root.tag == "{%s}user" % SCHEMAROOT:
|
||||
print("Unknown export format: %s" % root.tag)
|
||||
if root.tag != "{%s}user" % SCHEMAROOT:
|
||||
print(f"Unknown export format: {root.tag}")
|
||||
exit(-1)
|
||||
|
||||
# Username
|
||||
@@ -56,23 +54,21 @@ def read_gzdata(gzdata=None):
|
||||
if item[0] == "password":
|
||||
password = item[1]
|
||||
|
||||
# Emails
|
||||
for email in root.findall("{%s}emails" % SCHEMAROOT)[0]:
|
||||
if email.tag == "{%s}email" % SCHEMAROOT:
|
||||
emails.append({"email": email.text, "photo_id": email.attrib["photo_id"]})
|
||||
|
||||
# OpenIDs
|
||||
for openid in root.findall("{%s}openids" % SCHEMAROOT)[0]:
|
||||
if openid.tag == "{%s}openid" % SCHEMAROOT:
|
||||
openids.append(
|
||||
{"openid": openid.text, "photo_id": openid.attrib["photo_id"]}
|
||||
)
|
||||
|
||||
emails = [
|
||||
{"email": email.text, "photo_id": email.attrib["photo_id"]}
|
||||
for email in root.findall("{%s}emails" % SCHEMAROOT)[0]
|
||||
if email.tag == "{%s}email" % SCHEMAROOT
|
||||
]
|
||||
openids = [
|
||||
{"openid": openid.text, "photo_id": openid.attrib["photo_id"]}
|
||||
for openid in root.findall("{%s}openids" % SCHEMAROOT)[0]
|
||||
if openid.tag == "{%s}openid" % SCHEMAROOT
|
||||
]
|
||||
# Photos
|
||||
for photo in root.findall("{%s}photos" % SCHEMAROOT)[0]:
|
||||
if photo.tag == "{%s}photo" % SCHEMAROOT:
|
||||
try:
|
||||
# Safty measures to make sure we do not try to parse
|
||||
# Safety measures to make sure we do not try to parse
|
||||
# a binary encoded string
|
||||
photo.text = photo.text.strip("'")
|
||||
photo.text = photo.text.strip("\\n")
|
||||
@@ -80,26 +76,14 @@ def read_gzdata(gzdata=None):
|
||||
data = base64.decodebytes(bytes(photo.text, "utf-8"))
|
||||
except binascii.Error as exc:
|
||||
print(
|
||||
"Cannot decode photo; Encoding: %s, Format: %s, Id: %s: %s"
|
||||
% (
|
||||
photo.attrib["encoding"],
|
||||
photo.attrib["format"],
|
||||
photo.attrib["id"],
|
||||
exc,
|
||||
)
|
||||
f'Cannot decode photo; Encoding: {photo.attrib["encoding"]}, Format: {photo.attrib["format"]}, Id: {photo.attrib["id"]}: {exc}'
|
||||
)
|
||||
continue
|
||||
try:
|
||||
Image.open(BytesIO(data))
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
print(
|
||||
"Cannot decode photo; Encoding: %s, Format: %s, Id: %s: %s"
|
||||
% (
|
||||
photo.attrib["encoding"],
|
||||
photo.attrib["format"],
|
||||
photo.attrib["id"],
|
||||
exc,
|
||||
)
|
||||
f'Cannot decode photo; Encoding: {photo.attrib["encoding"]}, Format: {photo.attrib["format"]}, Id: {photo.attrib["id"]}: {exc}'
|
||||
)
|
||||
continue
|
||||
else:
|
||||
|
||||
@@ -461,17 +461,8 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
||||
},
|
||||
follow=True,
|
||||
) # Create test addresses + 1 too much
|
||||
# Check the response context for form errors
|
||||
self.assertTrue(
|
||||
hasattr(response, "context"), "Response does not have a context"
|
||||
)
|
||||
form = response.context.get("form")
|
||||
self.assertIsNotNone(form, "No form found in response context")
|
||||
|
||||
# Verify form errors
|
||||
self.assertFalse(form.is_valid(), "Form should not be valid")
|
||||
self.assertIn(
|
||||
"Too many unconfirmed mail addresses!", form.errors.get("__all__", [])
|
||||
return self._check_form_validity(
|
||||
response, "Too many unconfirmed mail addresses!", "__all__"
|
||||
)
|
||||
|
||||
def test_add_mail_address_twice(self):
|
||||
@@ -491,17 +482,8 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
||||
},
|
||||
follow=True,
|
||||
)
|
||||
# Check the response context for form errors
|
||||
self.assertTrue(
|
||||
hasattr(response, "context"), "Response does not have a context"
|
||||
)
|
||||
form = response.context.get("form")
|
||||
self.assertIsNotNone(form, "No form found in response context")
|
||||
|
||||
# Verify form errors
|
||||
self.assertFalse(form.is_valid(), "Form should not be valid")
|
||||
self.assertIn(
|
||||
"Address already added, currently unconfirmed", form.errors.get("email", [])
|
||||
return self._check_form_validity(
|
||||
response, "Address already added, currently unconfirmed", "email"
|
||||
)
|
||||
|
||||
def test_add_already_confirmed_email_self(self): # pylint: disable=invalid-name
|
||||
@@ -520,17 +502,8 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
||||
follow=True,
|
||||
)
|
||||
|
||||
# Check the response context for form errors
|
||||
self.assertTrue(
|
||||
hasattr(response, "context"), "Response does not have a context"
|
||||
)
|
||||
form = response.context.get("form")
|
||||
self.assertIsNotNone(form, "No form found in response context")
|
||||
|
||||
# Verify form errors
|
||||
self.assertFalse(form.is_valid(), "Form should not be valid")
|
||||
self.assertIn(
|
||||
"Address already confirmed (by you)", form.errors.get("email", [])
|
||||
return self._check_form_validity(
|
||||
response, "Address already confirmed (by you)", "email"
|
||||
)
|
||||
|
||||
def test_add_already_confirmed_email_other(self): # pylint: disable=invalid-name
|
||||
@@ -556,17 +529,8 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
||||
follow=True,
|
||||
)
|
||||
|
||||
# Check the response context for form errors
|
||||
self.assertTrue(
|
||||
hasattr(response, "context"), "Response does not have a context"
|
||||
)
|
||||
form = response.context.get("form")
|
||||
self.assertIsNotNone(form, "No form found in response context")
|
||||
|
||||
# Verify form errors
|
||||
self.assertFalse(form.is_valid(), "Form should not be valid")
|
||||
self.assertIn(
|
||||
"Address already confirmed (by someone else)", form.errors.get("email", [])
|
||||
return self._check_form_validity(
|
||||
response, "Address already confirmed (by someone else)", "email"
|
||||
)
|
||||
|
||||
def test_remove_unconfirmed_non_existing_email(
|
||||
@@ -712,120 +676,59 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
||||
"""
|
||||
Test if gif 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.gif"), "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",
|
||||
self._extracted_from_test_upload_webp_image_5(
|
||||
"broken.gif",
|
||||
"GIF upload failed?!",
|
||||
)
|
||||
self.assertEqual(
|
||||
self.user.photo_set.first().format,
|
||||
"gif",
|
||||
"Format must be gif, since we uploaded a GIF!",
|
||||
)
|
||||
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 = f"{urlobj.path}?{urlobj.query}"
|
||||
response = self.client.get(url, follow=True)
|
||||
self.assertEqual(response.status_code, 200, "unable to fetch avatar?")
|
||||
|
||||
def test_upload_jpg_image(self):
|
||||
"""
|
||||
Test if jpg 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.jpg"), "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",
|
||||
self._extracted_from_test_upload_webp_image_5(
|
||||
"broken.jpg",
|
||||
"JPEG upload failed?!",
|
||||
)
|
||||
self.assertEqual(
|
||||
self.user.photo_set.first().format,
|
||||
"jpg",
|
||||
"Format must be jpeg, since we uploaded a jpeg!",
|
||||
)
|
||||
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 = f"{urlobj.path}?{urlobj.query}"
|
||||
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._extracted_from_test_upload_webp_image_5(
|
||||
"broken.webp",
|
||||
"WEBP upload failed?!",
|
||||
"webp",
|
||||
"Format must be webp, since we uploaded a webp!",
|
||||
)
|
||||
|
||||
def _extracted_from_test_upload_webp_image_5(
|
||||
self, filename, message1, format, message2
|
||||
):
|
||||
"""
|
||||
Helper function for common checks for gif, jpg, webp
|
||||
"""
|
||||
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:
|
||||
with open(os.path.join(settings.STATIC_ROOT, "img", filename), "rb") as photo:
|
||||
response = self.client.post(
|
||||
url,
|
||||
{
|
||||
"photo": photo,
|
||||
"not_porn": True,
|
||||
"can_distribute": True,
|
||||
},
|
||||
{"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!",
|
||||
message1,
|
||||
)
|
||||
self.assertEqual(self.user.photo_set.first().format, format, message2)
|
||||
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,
|
||||
)
|
||||
libravatar_url(email=self.user.confirmedemail_set.first().email)
|
||||
)
|
||||
url = f"{urlobj.path}?{urlobj.query}"
|
||||
response = self.client.get(url, follow=True)
|
||||
@@ -839,7 +742,7 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
||||
url = reverse("upload_photo")
|
||||
# rb => Read binary
|
||||
with open(
|
||||
os.path.join(settings.STATIC_ROOT, "img", "hackergotchi_test.tif"), "rb"
|
||||
os.path.join(settings.STATIC_ROOT, "img", "broken.tif"), "rb"
|
||||
) as photo:
|
||||
response = self.client.post(
|
||||
url,
|
||||
@@ -1062,8 +965,6 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
||||
if confirm:
|
||||
self._manual_confirm()
|
||||
|
||||
# TODO Rename this here and in `test_add_openid`
|
||||
|
||||
def test_add_openid_twice(self):
|
||||
"""
|
||||
Test if adding OpenID a second time works - it shouldn't
|
||||
@@ -1095,20 +996,9 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
||||
"There must only be one unconfirmed ID!",
|
||||
)
|
||||
|
||||
# Check the response context for form errors
|
||||
self.assertTrue(
|
||||
hasattr(response, "context"), "Response does not have a context"
|
||||
self._check_form_validity(
|
||||
response, "OpenID already added, but not confirmed yet!", "openid"
|
||||
)
|
||||
form = response.context.get("form")
|
||||
self.assertIsNotNone(form, "No form found in response context")
|
||||
|
||||
# Verify form errors
|
||||
self.assertFalse(form.is_valid(), "Form should not be valid")
|
||||
self.assertIn(
|
||||
"OpenID already added, but not confirmed yet!",
|
||||
form.errors.get("openid", []),
|
||||
)
|
||||
|
||||
# Manual confirm, since testing is _really_ hard!
|
||||
unconfirmed = self.user.unconfirmedopenid_set.first()
|
||||
confirmed = ConfirmedOpenId()
|
||||
@@ -1127,18 +1017,24 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
||||
follow=True,
|
||||
)
|
||||
|
||||
# Check the response context for form errors
|
||||
return self._check_form_validity(
|
||||
response, "OpenID already added and confirmed!", "openid"
|
||||
)
|
||||
|
||||
def _check_form_validity(self, response, message, field):
|
||||
"""
|
||||
Helper method to check form, used in several test functions,
|
||||
deduplicating code
|
||||
"""
|
||||
|
||||
self.assertTrue(
|
||||
hasattr(response, "context"), "Response does not have a context"
|
||||
)
|
||||
form = response.context.get("form")
|
||||
self.assertIsNotNone(form, "No form found in response context")
|
||||
|
||||
# Verify form errors
|
||||
self.assertFalse(form.is_valid(), "Form should not be valid")
|
||||
self.assertIn(
|
||||
"OpenID already added and confirmed!", form.errors.get("openid", [])
|
||||
)
|
||||
result = response.context.get("form")
|
||||
self.assertIsNotNone(result, "No form found in response context")
|
||||
self.assertFalse(result.is_valid(), "Form should not be valid")
|
||||
self.assertIn(message, result.errors.get(field, []))
|
||||
return result
|
||||
|
||||
def test_assign_photo_to_openid(self):
|
||||
"""
|
||||
@@ -2023,37 +1919,10 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
||||
fh_gzip = gzip.open(BytesIO(response.content), "rb")
|
||||
fh = BytesIO(response.content)
|
||||
|
||||
response = self.client.post(
|
||||
reverse("upload_export"),
|
||||
data={"not_porn": "on", "can_distribute": "on", "export_file": fh_gzip},
|
||||
follow=True,
|
||||
)
|
||||
fh_gzip.close()
|
||||
self.assertEqual(response.status_code, 200, "Upload worked")
|
||||
self.assertContains(
|
||||
response,
|
||||
"Unable to parse file: Not a gzipped file",
|
||||
1,
|
||||
200,
|
||||
"Upload didn't work?",
|
||||
)
|
||||
|
||||
# Second test - correctly gzipped content
|
||||
response = self.client.post(
|
||||
reverse("upload_export"),
|
||||
data={"not_porn": "on", "can_distribute": "on", "export_file": fh},
|
||||
follow=True,
|
||||
)
|
||||
fh.close()
|
||||
|
||||
self.assertEqual(response.status_code, 200, "Upload worked")
|
||||
self.assertContains(
|
||||
response,
|
||||
"Choose items to be imported",
|
||||
1,
|
||||
200,
|
||||
"Upload didn't work?",
|
||||
response = self._uploading_export_check(
|
||||
fh_gzip, "Unable to parse file: Not a gzipped file"
|
||||
)
|
||||
response = self._uploading_export_check(fh, "Choose items to be imported")
|
||||
self.assertContains(
|
||||
response,
|
||||
"asdf@asdf.local",
|
||||
@@ -2062,6 +1931,21 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
||||
"Upload didn't work?",
|
||||
)
|
||||
|
||||
def _uploading_export_check(self, fh, message):
|
||||
"""
|
||||
Helper function to upload an export
|
||||
"""
|
||||
result = self.client.post(
|
||||
reverse("upload_export"),
|
||||
data={"not_porn": "on", "can_distribute": "on", "export_file": fh},
|
||||
follow=True,
|
||||
)
|
||||
fh.close()
|
||||
self.assertEqual(result.status_code, 200, "Upload worked")
|
||||
self.assertContains(result, message, 1, 200, "Upload didn't work?")
|
||||
|
||||
return result
|
||||
|
||||
def test_preferences_page(self):
|
||||
"""
|
||||
Test if preferences page works
|
||||
|
||||
@@ -179,23 +179,10 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
||||
"""
|
||||
self.login()
|
||||
confirmed = self.create_confirmed_openid()
|
||||
url = reverse("assign_bluesky_handle_to_openid", args=[confirmed.id])
|
||||
response = self.client.post(
|
||||
url,
|
||||
{
|
||||
"bluesky_handle": self.bsky_test_account,
|
||||
},
|
||||
follow=True,
|
||||
)
|
||||
self.assertEqual(
|
||||
response.status_code, 200, "Adding Bluesky handle to OpenID fails?"
|
||||
)
|
||||
# Fetch object again, as it has changed because of the request
|
||||
confirmed.refresh_from_db(fields=["bluesky_handle"])
|
||||
self.assertEqual(
|
||||
confirmed.bluesky_handle,
|
||||
self.bsky_test_account,
|
||||
"Setting Bluesky handle doesn't work?",
|
||||
self._assign_handle_to(
|
||||
"assign_bluesky_handle_to_openid",
|
||||
confirmed,
|
||||
"Adding Bluesky handle to OpenID fails?",
|
||||
)
|
||||
|
||||
def test_assign_bluesky_handle_to_email(self):
|
||||
@@ -205,18 +192,22 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
||||
"""
|
||||
self.login()
|
||||
confirmed = self.create_confirmed_email()
|
||||
url = reverse("assign_bluesky_handle_to_email", args=[confirmed.id])
|
||||
self._assign_handle_to(
|
||||
"assign_bluesky_handle_to_email",
|
||||
confirmed,
|
||||
"Adding Bluesky handle to Email fails?",
|
||||
)
|
||||
|
||||
def _assign_handle_to(self, endpoint, confirmed, message):
|
||||
"""
|
||||
Helper method to assign a handle to reduce code duplication
|
||||
Since the endpoints are similar, we can reuse the code
|
||||
"""
|
||||
url = reverse(endpoint, args=[confirmed.id])
|
||||
response = self.client.post(
|
||||
url,
|
||||
{
|
||||
"bluesky_handle": self.bsky_test_account,
|
||||
},
|
||||
follow=True,
|
||||
url, {"bluesky_handle": self.bsky_test_account}, follow=True
|
||||
)
|
||||
self.assertEqual(
|
||||
response.status_code, 200, "Adding Bluesky handle to Email fails?"
|
||||
)
|
||||
# Fetch object again, as it has changed because of the request
|
||||
self.assertEqual(response.status_code, 200, message)
|
||||
confirmed.refresh_from_db(fields=["bluesky_handle"])
|
||||
self.assertEqual(
|
||||
confirmed.bluesky_handle,
|
||||
@@ -230,26 +221,7 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
||||
"""
|
||||
self.login()
|
||||
confirmed = self.create_confirmed_email()
|
||||
confirmed.bluesky_handle = self.bsky_test_account
|
||||
confirmed.save()
|
||||
|
||||
url = reverse("assign_photo_email", args=[confirmed.id])
|
||||
response = self.client.post(
|
||||
url,
|
||||
{
|
||||
"photoNone": True,
|
||||
},
|
||||
follow=True,
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200, "Unassigning Photo doesn't work?")
|
||||
# Fetch object again, as it has changed because of the request
|
||||
confirmed.refresh_from_db(fields=["bluesky_handle"])
|
||||
self.assertEqual(
|
||||
confirmed.bluesky_handle,
|
||||
None,
|
||||
"Removing Bluesky handle doesn't work?",
|
||||
)
|
||||
self._assign_bluesky_handle(confirmed, "assign_photo_email")
|
||||
|
||||
def test_assign_photo_to_openid_removes_bluesky_handle(self):
|
||||
"""
|
||||
@@ -257,23 +229,19 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
||||
"""
|
||||
self.login()
|
||||
confirmed = self.create_confirmed_openid()
|
||||
self._assign_bluesky_handle(confirmed, "assign_photo_openid")
|
||||
|
||||
def _assign_bluesky_handle(self, confirmed, endpoint):
|
||||
"""
|
||||
Helper method to assign a Bluesky handle
|
||||
Since the endpoints are similar, we can reuse the code
|
||||
"""
|
||||
confirmed.bluesky_handle = self.bsky_test_account
|
||||
confirmed.save()
|
||||
|
||||
url = reverse("assign_photo_openid", args=[confirmed.id])
|
||||
response = self.client.post(
|
||||
url,
|
||||
{
|
||||
"photoNone": True,
|
||||
},
|
||||
follow=True,
|
||||
)
|
||||
|
||||
url = reverse(endpoint, args=[confirmed.id])
|
||||
response = self.client.post(url, {"photoNone": True}, follow=True)
|
||||
self.assertEqual(response.status_code, 200, "Unassigning Photo doesn't work?")
|
||||
# Fetch object again, as it has changed because of the request
|
||||
confirmed.refresh_from_db(fields=["bluesky_handle"])
|
||||
self.assertEqual(
|
||||
confirmed.bluesky_handle,
|
||||
None,
|
||||
"Removing Bluesky handle doesn't work?",
|
||||
confirmed.bluesky_handle, None, "Removing Bluesky handle doesn't work?"
|
||||
)
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
"""
|
||||
View classes for ivatar/ivataraccount/
|
||||
"""
|
||||
|
||||
from io import BytesIO
|
||||
from ivatar.utils import urlopen, Bluesky
|
||||
import base64
|
||||
import binascii
|
||||
import contextlib
|
||||
from xml.sax import saxutils
|
||||
import gzip
|
||||
|
||||
@@ -87,23 +89,8 @@ class CreateView(SuccessMessageMixin, FormView):
|
||||
# If the username looks like a mail address, automagically
|
||||
# add it as unconfirmed mail and set it also as user's
|
||||
# email address
|
||||
try:
|
||||
# This will error out if it's not a valid address
|
||||
valid = validate_email(form.cleaned_data["username"])
|
||||
user.email = valid.email
|
||||
user.save()
|
||||
# The following will also error out if it already exists
|
||||
unconfirmed = UnconfirmedEmail()
|
||||
unconfirmed.email = valid.email
|
||||
unconfirmed.user = user
|
||||
unconfirmed.save()
|
||||
unconfirmed.send_confirmation_mail(
|
||||
url=self.request.build_absolute_uri("/")[:-1]
|
||||
)
|
||||
# In any exception cases, we just skip it
|
||||
except Exception: # pylint: disable=broad-except
|
||||
pass
|
||||
|
||||
with contextlib.suppress(Exception):
|
||||
self._extracted_from_form_valid_(form, user)
|
||||
login(self.request, user)
|
||||
pref = UserPreference.objects.create(
|
||||
user_id=user.pk
|
||||
@@ -112,13 +99,26 @@ class CreateView(SuccessMessageMixin, FormView):
|
||||
return HttpResponseRedirect(reverse_lazy("profile"))
|
||||
return HttpResponseRedirect(reverse_lazy("login")) # pragma: no cover
|
||||
|
||||
def _extracted_from_form_valid_(self, form, user):
|
||||
# This will error out if it's not a valid address
|
||||
valid = validate_email(form.cleaned_data["username"])
|
||||
user.email = valid.email
|
||||
user.save()
|
||||
# The following will also error out if it already exists
|
||||
unconfirmed = UnconfirmedEmail()
|
||||
unconfirmed.email = valid.email
|
||||
unconfirmed.user = user
|
||||
unconfirmed.save()
|
||||
unconfirmed.send_confirmation_mail(
|
||||
url=self.request.build_absolute_uri("/")[:-1]
|
||||
)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""
|
||||
Handle get for create view
|
||||
"""
|
||||
if request.user:
|
||||
if request.user.is_authenticated:
|
||||
return HttpResponseRedirect(reverse_lazy("profile"))
|
||||
if request.user and request.user.is_authenticated:
|
||||
return HttpResponseRedirect(reverse_lazy("profile"))
|
||||
return super().get(self, request, args, kwargs)
|
||||
|
||||
|
||||
@@ -379,9 +379,7 @@ class AssignBlueskyHandleToEmailView(SuccessMessageMixin, TemplateView):
|
||||
|
||||
bs.get_avatar(bluesky_handle)
|
||||
except Exception as e:
|
||||
messages.error(
|
||||
request, _("Handle '%s' not found: %s" % (bluesky_handle, e))
|
||||
)
|
||||
messages.error(request, _(f"Handle '{bluesky_handle}' not found: {e}"))
|
||||
return HttpResponseRedirect(reverse_lazy("profile"))
|
||||
email.set_bluesky_handle(bluesky_handle)
|
||||
email.photo = None
|
||||
@@ -425,9 +423,7 @@ class AssignBlueskyHandleToOpenIdView(SuccessMessageMixin, TemplateView):
|
||||
|
||||
bs.get_avatar(bluesky_handle)
|
||||
except Exception as e:
|
||||
messages.error(
|
||||
request, _("Handle '%s' not found: %s" % (bluesky_handle, e))
|
||||
)
|
||||
messages.error(request, _(f"Handle '{bluesky_handle}' not found: {e}"))
|
||||
return HttpResponseRedirect(
|
||||
reverse_lazy(
|
||||
"assign_photo_openid", kwargs={"openid_id": int(kwargs["open_id"])}
|
||||
@@ -436,7 +432,7 @@ class AssignBlueskyHandleToOpenIdView(SuccessMessageMixin, TemplateView):
|
||||
try:
|
||||
openid.set_bluesky_handle(bluesky_handle)
|
||||
except Exception as e:
|
||||
messages.error(request, _("Error: %s" % (e)))
|
||||
messages.error(request, _(f"Error: {e}"))
|
||||
return HttpResponseRedirect(
|
||||
reverse_lazy(
|
||||
"assign_photo_openid", kwargs={"openid_id": int(kwargs["open_id"])}
|
||||
@@ -474,29 +470,25 @@ class ImportPhotoView(SuccessMessageMixin, TemplateView):
|
||||
messages.error(self.request, _("Address does not exist"))
|
||||
return context
|
||||
|
||||
addr = kwargs.get("email_addr", None)
|
||||
|
||||
if addr:
|
||||
gravatar = get_gravatar_photo(addr)
|
||||
if gravatar:
|
||||
if addr := kwargs.get("email_addr", None):
|
||||
if gravatar := get_gravatar_photo(addr):
|
||||
context["photos"].append(gravatar)
|
||||
|
||||
libravatar_service_url = libravatar_url(
|
||||
if libravatar_service_url := libravatar_url(
|
||||
email=addr,
|
||||
default=404,
|
||||
size=AVATAR_MAX_SIZE,
|
||||
)
|
||||
if libravatar_service_url:
|
||||
):
|
||||
try:
|
||||
urlopen(libravatar_service_url)
|
||||
except OSError as exc:
|
||||
print("Exception caught during photo import: {}".format(exc))
|
||||
print(f"Exception caught during photo import: {exc}")
|
||||
else:
|
||||
context["photos"].append(
|
||||
{
|
||||
"service_url": libravatar_service_url,
|
||||
"thumbnail_url": libravatar_service_url + "&s=80",
|
||||
"image_url": libravatar_service_url + "&s=512",
|
||||
"thumbnail_url": f"{libravatar_service_url}&s=80",
|
||||
"image_url": f"{libravatar_service_url}&s=512",
|
||||
"width": 80,
|
||||
"height": 80,
|
||||
"service_name": "Libravatar",
|
||||
@@ -515,7 +507,7 @@ class ImportPhotoView(SuccessMessageMixin, TemplateView):
|
||||
imported = None
|
||||
|
||||
email_id = kwargs.get("email_id", request.POST.get("email_id", None))
|
||||
addr = kwargs.get("emali_addr", request.POST.get("email_addr", None))
|
||||
addr = kwargs.get("email", request.POST.get("email_addr", None))
|
||||
|
||||
if email_id:
|
||||
email = ConfirmedEmail.objects.filter(id=email_id, user=request.user)
|
||||
@@ -565,9 +557,9 @@ class RawImageView(DetailView):
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
photo = self.model.objects.get(pk=kwargs["pk"]) # pylint: disable=no-member
|
||||
if not photo.user.id == request.user.id and not request.user.is_staff:
|
||||
if photo.user.id != request.user.id and not request.user.is_staff:
|
||||
return HttpResponseRedirect(reverse_lazy("home"))
|
||||
return HttpResponse(BytesIO(photo.data), content_type="image/%s" % photo.format)
|
||||
return HttpResponse(BytesIO(photo.data), content_type=f"image/{photo.format}")
|
||||
|
||||
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
@@ -643,17 +635,16 @@ class AddOpenIDView(SuccessMessageMixin, FormView):
|
||||
success_url = reverse_lazy("profile")
|
||||
|
||||
def form_valid(self, form):
|
||||
openid_id = form.save(self.request.user)
|
||||
if not openid_id:
|
||||
if openid_id := form.save(self.request.user):
|
||||
# At this point we have an unconfirmed OpenID, but
|
||||
# we do not add the message, that we successfully added it,
|
||||
# since this is misleading
|
||||
return HttpResponseRedirect(
|
||||
reverse_lazy("openid_redirection", args=[openid_id])
|
||||
)
|
||||
else:
|
||||
return render(self.request, self.template_name, {"form": form})
|
||||
|
||||
# At this point we have an unconfirmed OpenID, but
|
||||
# we do not add the message, that we successfully added it,
|
||||
# since this is misleading
|
||||
return HttpResponseRedirect(
|
||||
reverse_lazy("openid_redirection", args=[openid_id])
|
||||
)
|
||||
|
||||
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
class RemoveUnconfirmedOpenIDView(View):
|
||||
@@ -703,7 +694,7 @@ class RemoveConfirmedOpenIDView(View):
|
||||
openidobj.delete()
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
# Why it is not there?
|
||||
print("How did we get here: %s" % exc)
|
||||
print(f"How did we get here: {exc}")
|
||||
openid.delete()
|
||||
messages.success(request, _("ID removed"))
|
||||
except self.model.DoesNotExist: # pylint: disable=no-member
|
||||
@@ -740,7 +731,7 @@ class RedirectOpenIDView(View):
|
||||
try:
|
||||
auth_request = openid_consumer.begin(user_url)
|
||||
except consumer.DiscoveryFailure as exc:
|
||||
messages.error(request, _("OpenID discovery failed: %s" % exc))
|
||||
messages.error(request, _(f"OpenID discovery failed: {exc}"))
|
||||
return HttpResponseRedirect(reverse_lazy("profile"))
|
||||
except UnicodeDecodeError as exc: # pragma: no cover
|
||||
msg = _(
|
||||
@@ -752,7 +743,7 @@ class RedirectOpenIDView(View):
|
||||
"message": exc,
|
||||
}
|
||||
)
|
||||
print("message: %s" % msg)
|
||||
print(f"message: {msg}")
|
||||
messages.error(request, msg)
|
||||
|
||||
if auth_request is None: # pragma: no cover
|
||||
@@ -886,19 +877,13 @@ class CropPhotoView(TemplateView):
|
||||
}
|
||||
email = openid = None
|
||||
if "email" in request.POST:
|
||||
try:
|
||||
with contextlib.suppress(ConfirmedEmail.DoesNotExist):
|
||||
email = ConfirmedEmail.objects.get(email=request.POST["email"])
|
||||
except ConfirmedEmail.DoesNotExist: # pylint: disable=no-member
|
||||
pass # Ignore automatic assignment
|
||||
|
||||
if "openid" in request.POST:
|
||||
try:
|
||||
with contextlib.suppress(ConfirmedOpenId.DoesNotExist):
|
||||
openid = ConfirmedOpenId.objects.get( # pylint: disable=no-member
|
||||
openid=request.POST["openid"]
|
||||
)
|
||||
except ConfirmedOpenId.DoesNotExist: # pylint: disable=no-member
|
||||
pass # Ignore automatic assignment
|
||||
|
||||
return photo.perform_crop(request, dimensions, email, openid)
|
||||
|
||||
|
||||
@@ -934,14 +919,14 @@ class UserPreferenceView(FormView, UpdateView):
|
||||
if request.POST["email"] not in addresses:
|
||||
messages.error(
|
||||
self.request,
|
||||
_("Mail address not allowed: %s" % request.POST["email"]),
|
||||
_(f'Mail address not allowed: {request.POST["email"]}'),
|
||||
)
|
||||
else:
|
||||
self.request.user.email = request.POST["email"]
|
||||
self.request.user.save()
|
||||
messages.info(self.request, _("Mail address changed."))
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
messages.error(self.request, _("Error setting new mail address: %s" % e))
|
||||
messages.error(self.request, _(f"Error setting new mail address: {e}"))
|
||||
|
||||
try:
|
||||
if request.POST["first_name"] or request.POST["last_name"]:
|
||||
@@ -953,7 +938,7 @@ class UserPreferenceView(FormView, UpdateView):
|
||||
messages.info(self.request, _("Last name changed."))
|
||||
self.request.user.save()
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
messages.error(self.request, _("Error setting names: %s" % e))
|
||||
messages.error(self.request, _(f"Error setting names: {e}"))
|
||||
|
||||
return HttpResponseRedirect(reverse_lazy("user_preference"))
|
||||
|
||||
@@ -1021,15 +1006,14 @@ class UploadLibravatarExportView(SuccessMessageMixin, FormView):
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
# DEBUG
|
||||
print(
|
||||
"Exception during adding mail address (%s): %s"
|
||||
% (email, exc)
|
||||
f"Exception during adding mail address ({email}): {exc}"
|
||||
)
|
||||
|
||||
if arg.startswith("photo"):
|
||||
try:
|
||||
data = base64.decodebytes(bytes(request.POST[arg], "utf-8"))
|
||||
except binascii.Error as exc:
|
||||
print("Cannot decode photo: %s" % exc)
|
||||
print(f"Cannot decode photo: {exc}")
|
||||
continue
|
||||
try:
|
||||
pilobj = Image.open(BytesIO(data))
|
||||
@@ -1043,7 +1027,7 @@ class UploadLibravatarExportView(SuccessMessageMixin, FormView):
|
||||
photo.data = out.read()
|
||||
photo.save()
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
print("Exception during save: %s" % exc)
|
||||
print(f"Exception during save: {exc}")
|
||||
continue
|
||||
|
||||
return HttpResponseRedirect(reverse_lazy("profile"))
|
||||
@@ -1063,7 +1047,7 @@ class UploadLibravatarExportView(SuccessMessageMixin, FormView):
|
||||
},
|
||||
)
|
||||
except Exception as e:
|
||||
messages.error(self.request, _("Unable to parse file: %s" % e))
|
||||
messages.error(self.request, _(f"Unable to parse file: {e}"))
|
||||
return HttpResponseRedirect(reverse_lazy("upload_export"))
|
||||
|
||||
|
||||
@@ -1089,13 +1073,12 @@ class ResendConfirmationMailView(View):
|
||||
try:
|
||||
email.send_confirmation_mail(url=request.build_absolute_uri("/")[:-1])
|
||||
messages.success(
|
||||
request, "%s: %s" % (_("Confirmation mail sent to"), email.email)
|
||||
request, f'{_("Confirmation mail sent to")}: {email.email}'
|
||||
)
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
messages.error(
|
||||
request,
|
||||
"%s %s: %s"
|
||||
% (_("Unable to send confirmation email for"), email.email, exc),
|
||||
f'{_("Unable to send confirmation email for")} {email.email}: {exc}',
|
||||
)
|
||||
return HttpResponseRedirect(reverse_lazy("profile"))
|
||||
|
||||
@@ -1129,12 +1112,9 @@ class ProfileView(TemplateView):
|
||||
if "profile_username" in kwargs:
|
||||
if not request.user.is_staff:
|
||||
return HttpResponseRedirect(reverse_lazy("profile"))
|
||||
try:
|
||||
with contextlib.suppress(Exception):
|
||||
u = User.objects.get(username=kwargs["profile_username"])
|
||||
request.user = u
|
||||
except Exception: # pylint: disable=broad-except
|
||||
pass
|
||||
|
||||
self._confirm_claimed_openid()
|
||||
return super().get(self, request, args, kwargs)
|
||||
|
||||
@@ -1165,7 +1145,7 @@ class ProfileView(TemplateView):
|
||||
openid=openids.first().claimed_id
|
||||
).exists():
|
||||
return
|
||||
print("need to confirm: %s" % openids.first())
|
||||
print(f"need to confirm: {openids.first()}")
|
||||
confirmed = ConfirmedOpenId()
|
||||
confirmed.user = self.request.user
|
||||
confirmed.ip_address = get_client_ip(self.request)[0]
|
||||
@@ -1183,7 +1163,7 @@ class PasswordResetView(PasswordResetViewOriginal):
|
||||
Since we have the mail addresses in ConfirmedEmail model,
|
||||
we need to set the email on the user object in order for the
|
||||
PasswordResetView class to pick up the correct user.
|
||||
In case we have the mail address in the User objecct, we still
|
||||
In case we have the mail address in the User object, we still
|
||||
need to assign a random password in order for PasswordResetView
|
||||
class to pick up the user - else it will silently do nothing.
|
||||
"""
|
||||
@@ -1200,16 +1180,13 @@ class PasswordResetView(PasswordResetViewOriginal):
|
||||
# If we find the user there, we need to set the mail
|
||||
# attribute on the user object accordingly
|
||||
if not user:
|
||||
try:
|
||||
with contextlib.suppress(ObjectDoesNotExist):
|
||||
confirmed_email = ConfirmedEmail.objects.get(
|
||||
email=request.POST["email"]
|
||||
)
|
||||
user = confirmed_email.user
|
||||
user.email = confirmed_email.email
|
||||
user.save()
|
||||
except ObjectDoesNotExist:
|
||||
pass
|
||||
|
||||
# If we found the user, set a random password. Else, the
|
||||
# ResetPasswordView class will silently ignore the password
|
||||
# reset request
|
||||
@@ -1249,7 +1226,6 @@ class DeleteAccountView(SuccessMessageMixin, FormView):
|
||||
messages.error(request, _("No password given"))
|
||||
return HttpResponseRedirect(reverse_lazy("delete"))
|
||||
|
||||
raise _("No password given")
|
||||
# should delete all confirmed/unconfirmed/photo objects
|
||||
request.user.delete()
|
||||
return super().post(self, request, args, kwargs)
|
||||
@@ -1272,7 +1248,7 @@ class ExportView(SuccessMessageMixin, TemplateView):
|
||||
Handle real export
|
||||
"""
|
||||
SCHEMA_ROOT = "https://www.libravatar.org/schemas/export/0.2"
|
||||
SCHEMA_XSD = "%s/export.xsd" % SCHEMA_ROOT
|
||||
SCHEMA_XSD = f"{SCHEMA_ROOT}/export.xsd"
|
||||
|
||||
def xml_header():
|
||||
return (
|
||||
@@ -1354,8 +1330,8 @@ class ExportView(SuccessMessageMixin, TemplateView):
|
||||
bytesobj.seek(0)
|
||||
|
||||
response = HttpResponse(content_type="application/gzip")
|
||||
response["Content-Disposition"] = (
|
||||
'attachment; filename="libravatar-export_%s.xml.gz"' % user.username
|
||||
)
|
||||
response[
|
||||
"Content-Disposition"
|
||||
] = f'attachment; filename="libravatar-export_{user.username}.xml.gz"'
|
||||
response.write(bytesobj.read())
|
||||
return response
|
||||
|
||||
BIN
ivatar/static/img/broken.tif
Normal file
BIN
ivatar/static/img/broken.tif
Normal file
Binary file not shown.
Binary file not shown.
@@ -100,6 +100,7 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
||||
"""
|
||||
Bluesky client needs credentials, so it's limited with testing here now
|
||||
"""
|
||||
|
||||
if BLUESKY_APP_PASSWORD and BLUESKY_IDENTIFIER:
|
||||
b = Bluesky()
|
||||
profile = b.get_profile("ofalk.bsky.social")
|
||||
|
||||
@@ -33,10 +33,9 @@ class CheckDomainView(FormView):
|
||||
success_url = reverse("tools_check_domain")
|
||||
|
||||
def form_valid(self, form):
|
||||
result = {}
|
||||
super().form_valid(form)
|
||||
domain = form.cleaned_data["domain"]
|
||||
result["avatar_server_http"] = lookup_avatar_server(domain, False)
|
||||
result = {"avatar_server_http": lookup_avatar_server(domain, False)}
|
||||
if result["avatar_server_http"]:
|
||||
result["avatar_server_http_ipv4"] = lookup_ip_address(
|
||||
result["avatar_server_http"], False
|
||||
@@ -80,8 +79,6 @@ class CheckView(FormView):
|
||||
mail_hash = None
|
||||
mail_hash256 = None
|
||||
openid_hash = None
|
||||
size = 80
|
||||
|
||||
super().form_valid(form)
|
||||
|
||||
if form.cleaned_data["default_url"]:
|
||||
@@ -94,8 +91,7 @@ class CheckView(FormView):
|
||||
else:
|
||||
default_url = None
|
||||
|
||||
if "size" in form.cleaned_data:
|
||||
size = form.cleaned_data["size"]
|
||||
size = form.cleaned_data["size"] if "size" in form.cleaned_data else 80
|
||||
if form.cleaned_data["mail"]:
|
||||
mailurl = libravatar_url(
|
||||
email=form.cleaned_data["mail"], size=size, default=default_url
|
||||
@@ -121,7 +117,7 @@ class CheckView(FormView):
|
||||
if not form.cleaned_data["openid"].startswith(
|
||||
"http://"
|
||||
) and not form.cleaned_data["openid"].startswith("https://"):
|
||||
form.cleaned_data["openid"] = "http://%s" % form.cleaned_data["openid"]
|
||||
form.cleaned_data["openid"] = f'http://{form.cleaned_data["openid"]}'
|
||||
openidurl = libravatar_url(
|
||||
openid=form.cleaned_data["openid"], size=size, default=default_url
|
||||
)
|
||||
@@ -139,34 +135,33 @@ class CheckView(FormView):
|
||||
openid=form.cleaned_data["openid"], email=None
|
||||
)[0]
|
||||
|
||||
if "DEVELOPMENT" in SITE_NAME:
|
||||
if DEBUG:
|
||||
if mailurl:
|
||||
mailurl = mailurl.replace(
|
||||
"https://avatars.linux-kernel.at",
|
||||
"http://" + self.request.get_host(),
|
||||
)
|
||||
if mailurl_secure:
|
||||
mailurl_secure = mailurl_secure.replace(
|
||||
"https://avatars.linux-kernel.at",
|
||||
"http://" + self.request.get_host(),
|
||||
)
|
||||
if mailurl_secure_256:
|
||||
mailurl_secure_256 = mailurl_secure_256.replace(
|
||||
"https://avatars.linux-kernel.at",
|
||||
"http://" + self.request.get_host(),
|
||||
)
|
||||
if "DEVELOPMENT" in SITE_NAME and DEBUG:
|
||||
if mailurl:
|
||||
mailurl = mailurl.replace(
|
||||
"https://avatars.linux-kernel.at",
|
||||
f"http://{self.request.get_host()}",
|
||||
)
|
||||
if mailurl_secure:
|
||||
mailurl_secure = mailurl_secure.replace(
|
||||
"https://avatars.linux-kernel.at",
|
||||
f"http://{self.request.get_host()}",
|
||||
)
|
||||
if mailurl_secure_256:
|
||||
mailurl_secure_256 = mailurl_secure_256.replace(
|
||||
"https://avatars.linux-kernel.at",
|
||||
f"http://{self.request.get_host()}",
|
||||
)
|
||||
|
||||
if openidurl:
|
||||
openidurl = openidurl.replace(
|
||||
"https://avatars.linux-kernel.at",
|
||||
"http://" + self.request.get_host(),
|
||||
)
|
||||
if openidurl_secure:
|
||||
openidurl_secure = openidurl_secure.replace(
|
||||
"https://avatars.linux-kernel.at",
|
||||
"http://" + self.request.get_host(),
|
||||
)
|
||||
if openidurl:
|
||||
openidurl = openidurl.replace(
|
||||
"https://avatars.linux-kernel.at",
|
||||
f"http://{self.request.get_host()}",
|
||||
)
|
||||
if openidurl_secure:
|
||||
openidurl_secure = openidurl_secure.replace(
|
||||
"https://avatars.linux-kernel.at",
|
||||
f"http://{self.request.get_host()}",
|
||||
)
|
||||
print(mailurl, openidurl, mailurl_secure, mailurl_secure_256, openidurl_secure)
|
||||
|
||||
return render(
|
||||
@@ -202,15 +197,15 @@ def lookup_avatar_server(domain, https):
|
||||
|
||||
service_name = None
|
||||
if https:
|
||||
service_name = "_avatars-sec._tcp.%s" % domain
|
||||
service_name = f"_avatars-sec._tcp.{domain}"
|
||||
else:
|
||||
service_name = "_avatars._tcp.%s" % domain
|
||||
service_name = f"_avatars._tcp.{domain}"
|
||||
|
||||
DNS.DiscoverNameServers()
|
||||
try:
|
||||
dns_request = DNS.Request(name=service_name, qtype="SRV").req()
|
||||
except DNS.DNSError as message:
|
||||
print("DNS Error: %s (%s)" % (message, domain))
|
||||
print(f"DNS Error: {message} ({domain})")
|
||||
return None
|
||||
|
||||
if dns_request.header["status"] == "NXDOMAIN":
|
||||
@@ -218,7 +213,7 @@ def lookup_avatar_server(domain, https):
|
||||
return None
|
||||
|
||||
if dns_request.header["status"] != "NOERROR":
|
||||
print("DNS Error: status=%s (%s)" % (dns_request.header["status"], domain))
|
||||
print(f'DNS Error: status={dns_request.header["status"]} ({domain})')
|
||||
return None
|
||||
|
||||
records = []
|
||||
@@ -243,7 +238,7 @@ def lookup_avatar_server(domain, https):
|
||||
target, port = srv_hostname(records)
|
||||
|
||||
if target and ((https and port != 443) or (not https and port != 80)):
|
||||
return "%s:%s" % (target, port)
|
||||
return f"{target}:{port}"
|
||||
|
||||
return target
|
||||
|
||||
@@ -273,7 +268,7 @@ def srv_hostname(records):
|
||||
# Take care - this if is only a if, if the above if
|
||||
# uses continue at the end. else it should be an elsif
|
||||
if ret["priority"] < top_priority:
|
||||
# reset the aretay (ret has higher priority)
|
||||
# reset the priority (ret has higher priority)
|
||||
top_priority = ret["priority"]
|
||||
total_weight = 0
|
||||
priority_records = []
|
||||
@@ -283,7 +278,7 @@ def srv_hostname(records):
|
||||
if ret["weight"] > 0:
|
||||
priority_records.append((total_weight, ret))
|
||||
else:
|
||||
# zero-weigth elements must come first
|
||||
# zero-weight elements must come first
|
||||
priority_records.insert(0, (0, ret))
|
||||
|
||||
if len(priority_records) == 1:
|
||||
@@ -315,11 +310,11 @@ def lookup_ip_address(hostname, ipv6):
|
||||
else:
|
||||
dns_request = DNS.Request(name=hostname, qtype=DNS.Type.A).req()
|
||||
except DNS.DNSError as message:
|
||||
print("DNS Error: %s (%s)" % (message, hostname))
|
||||
print(f"DNS Error: {message} ({hostname})")
|
||||
return None
|
||||
|
||||
if dns_request.header["status"] != "NOERROR":
|
||||
print("DNS Error: status=%s (%s)" % (dns_request.header["status"], hostname))
|
||||
print(f'DNS Error: status={dns_request.header["status"]} ({hostname})')
|
||||
return None
|
||||
|
||||
for answer in dns_request.answers:
|
||||
@@ -330,9 +325,5 @@ def lookup_ip_address(hostname, ipv6):
|
||||
):
|
||||
continue # skip CNAME records
|
||||
|
||||
if ipv6:
|
||||
return inet_ntop(AF_INET6, answer["data"])
|
||||
|
||||
return answer["data"]
|
||||
|
||||
return inet_ntop(AF_INET6, answer["data"]) if ipv6 else answer["data"]
|
||||
return None
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
"""
|
||||
ivatar URL configuration
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include, re_path
|
||||
from django.conf.urls.static import static
|
||||
@@ -69,12 +71,9 @@ urlpatterns = [ # pylint: disable=invalid-name
|
||||
]
|
||||
|
||||
MAINTENANCE = False
|
||||
try:
|
||||
with contextlib.suppress(Exception):
|
||||
if settings.MAINTENANCE:
|
||||
MAINTENANCE = True
|
||||
except Exception: # pylint: disable=bare-except
|
||||
pass
|
||||
|
||||
if MAINTENANCE:
|
||||
urlpatterns.append(
|
||||
path("", TemplateView.as_view(template_name="maintenance.html"), name="home")
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
"""
|
||||
Simple module providing reusable random_string function
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
import random
|
||||
import string
|
||||
from io import BytesIO
|
||||
@@ -13,10 +15,8 @@ from urllib.request import urlopen as urlopen_orig
|
||||
|
||||
BLUESKY_IDENTIFIER = None
|
||||
BLUESKY_APP_PASSWORD = None
|
||||
try:
|
||||
with contextlib.suppress(Exception):
|
||||
from ivatar.settings import BLUESKY_IDENTIFIER, BLUESKY_APP_PASSWORD
|
||||
except Exception: # pylint: disable=broad-except
|
||||
pass
|
||||
|
||||
|
||||
def urlopen(url, timeout=URL_TIMEOUT):
|
||||
@@ -66,8 +66,7 @@ class Bluesky:
|
||||
Return the normalized handle for given handle
|
||||
"""
|
||||
# Normalize Bluesky handle in case someone enters an '@' at the beginning
|
||||
if handle.startswith("@"):
|
||||
handle = handle[1:]
|
||||
handle = handle.removeprefix("@")
|
||||
# Remove trailing spaces or spaces at the beginning
|
||||
while handle.startswith(" "):
|
||||
handle = handle[1:]
|
||||
@@ -88,9 +87,7 @@ class Bluesky:
|
||||
)
|
||||
profile_response.raise_for_status()
|
||||
except Exception as exc:
|
||||
print(
|
||||
"Bluesky profile fetch failed with HTTP error: %s" % exc
|
||||
) # pragma: no cover
|
||||
print(f"Bluesky profile fetch failed with HTTP error: {exc}")
|
||||
return None
|
||||
|
||||
return profile_response.json()
|
||||
@@ -126,12 +123,12 @@ def openid_variations(openid):
|
||||
if openid.startswith("https://"):
|
||||
openid = openid.replace("https://", "http://")
|
||||
if openid[-1] != "/":
|
||||
openid = openid + "/"
|
||||
openid = f"{openid}/"
|
||||
|
||||
# http w/o trailing slash
|
||||
var1 = openid[0:-1]
|
||||
var1 = openid[:-1]
|
||||
var2 = openid.replace("http://", "https://")
|
||||
var3 = var2[0:-1]
|
||||
var3 = var2[:-1]
|
||||
return (openid, var1, var2, var3)
|
||||
|
||||
|
||||
@@ -149,43 +146,43 @@ def mm_ng(
|
||||
idhash = "e0"
|
||||
|
||||
# How large is the circle?
|
||||
circlesize = size * 0.6
|
||||
circle_size = size * 0.6
|
||||
|
||||
# Coordinates for the circle
|
||||
start_x = int(size * 0.2)
|
||||
end_x = start_x + circlesize
|
||||
end_x = start_x + circle_size
|
||||
start_y = int(size * 0.05)
|
||||
end_y = start_y + circlesize
|
||||
end_y = start_y + circle_size
|
||||
|
||||
# All are the same, based on the input hash
|
||||
# this should always result in a "gray-ish" background
|
||||
red = idhash[0:2]
|
||||
green = idhash[0:2]
|
||||
blue = idhash[0:2]
|
||||
red = idhash[:2]
|
||||
green = idhash[:2]
|
||||
blue = idhash[:2]
|
||||
|
||||
# Add some red (i/a) and make sure it's not over 255
|
||||
red = hex(int(red, 16) + add_red).replace("0x", "")
|
||||
if int(red, 16) > 255:
|
||||
red = "ff"
|
||||
if len(red) == 1:
|
||||
red = "0%s" % red
|
||||
red = f"0{red}"
|
||||
|
||||
# Add some green (i/a) and make sure it's not over 255
|
||||
green = hex(int(green, 16) + add_green).replace("0x", "")
|
||||
if int(green, 16) > 255:
|
||||
green = "ff"
|
||||
if len(green) == 1:
|
||||
green = "0%s" % green
|
||||
green = f"0{green}"
|
||||
|
||||
# Add some blue (i/a) and make sure it's not over 255
|
||||
blue = hex(int(blue, 16) + add_blue).replace("0x", "")
|
||||
if int(blue, 16) > 255:
|
||||
blue = "ff"
|
||||
if len(blue) == 1:
|
||||
blue = "0%s" % blue
|
||||
blue = f"0{blue}"
|
||||
|
||||
# Assemable the bg color "string" in webnotation. Eg. '#d3d3d3'
|
||||
bg_color = "#" + red + green + blue
|
||||
# Assemble the bg color "string" in web notation. Eg. '#d3d3d3'
|
||||
bg_color = f"#{red}{green}{blue}"
|
||||
|
||||
# Image
|
||||
image = Image.new("RGB", (size, size))
|
||||
@@ -200,7 +197,7 @@ def mm_ng(
|
||||
# Draw MMs 'body'
|
||||
draw.polygon(
|
||||
(
|
||||
(start_x + circlesize / 2, size / 2.5),
|
||||
(start_x + circle_size / 2, size / 2.5),
|
||||
(size * 0.15, size),
|
||||
(size - size * 0.15, size),
|
||||
),
|
||||
|
||||
179
ivatar/views.py
179
ivatar/views.py
@@ -2,6 +2,8 @@
|
||||
"""
|
||||
views under /
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
from io import BytesIO
|
||||
from os import path
|
||||
import hashlib
|
||||
@@ -47,17 +49,11 @@ def get_size(request, size=DEFAULT_AVATAR_SIZE):
|
||||
if "size" in request.GET:
|
||||
sizetemp = request.GET["size"]
|
||||
if sizetemp:
|
||||
if sizetemp != "" and sizetemp is not None and sizetemp != "0":
|
||||
try:
|
||||
if sizetemp not in ["", "0"]:
|
||||
with contextlib.suppress(ValueError):
|
||||
if int(sizetemp) > 0:
|
||||
size = int(sizetemp)
|
||||
# Should we receive something we cannot convert to int, leave
|
||||
# the user with the default value of 80
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if size > int(AVATAR_MAX_SIZE):
|
||||
size = int(AVATAR_MAX_SIZE)
|
||||
size = min(size, int(AVATAR_MAX_SIZE))
|
||||
return size
|
||||
|
||||
|
||||
@@ -119,8 +115,7 @@ class AvatarImageView(TemplateView):
|
||||
|
||||
# Check the cache first
|
||||
if CACHE_RESPONSE:
|
||||
centry = caches["filesystem"].get(uri)
|
||||
if centry:
|
||||
if centry := caches["filesystem"].get(uri):
|
||||
# For DEBUG purpose only
|
||||
# print('Cached entry for %s' % uri)
|
||||
return HttpResponse(
|
||||
@@ -150,8 +145,7 @@ class AvatarImageView(TemplateView):
|
||||
|
||||
if not trusted_url:
|
||||
print(
|
||||
"Default URL is not in trusted URLs: '%s' ; Kicking it!"
|
||||
% default
|
||||
f"Default URL is not in trusted URLs: '{default}'; Kicking it!"
|
||||
)
|
||||
default = None
|
||||
|
||||
@@ -177,20 +171,17 @@ class AvatarImageView(TemplateView):
|
||||
obj = model.objects.get(digest_sha256=kwargs["digest"])
|
||||
except ObjectDoesNotExist:
|
||||
model = ConfirmedOpenId
|
||||
try:
|
||||
with contextlib.suppress(Exception):
|
||||
d = kwargs["digest"] # pylint: disable=invalid-name
|
||||
# OpenID is tricky. http vs. https, versus trailing slash or not
|
||||
# However, some users eventually have added their variations already
|
||||
# and therfore we need to use filter() and first()
|
||||
# and therefore we need to use filter() and first()
|
||||
obj = model.objects.filter(
|
||||
Q(digest=d)
|
||||
| Q(alt_digest1=d)
|
||||
| Q(alt_digest2=d)
|
||||
| Q(alt_digest3=d)
|
||||
).first()
|
||||
except Exception: # pylint: disable=bare-except
|
||||
pass
|
||||
|
||||
# Handle the special case of Bluesky
|
||||
if obj:
|
||||
if obj.bluesky_handle:
|
||||
@@ -218,7 +209,7 @@ class AvatarImageView(TemplateView):
|
||||
)
|
||||
# Ensure we do not convert None to string 'None'
|
||||
if default:
|
||||
url += "&default=%s" % default
|
||||
url += f"&default={default}"
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
# Return the default URL, as specified, or 404 Not Found, if default=404
|
||||
@@ -228,7 +219,7 @@ class AvatarImageView(TemplateView):
|
||||
url = (
|
||||
reverse_lazy("gravatarproxy", args=[kwargs["digest"]])
|
||||
+ "?s=%i" % size
|
||||
+ "&default=%s&f=y" % default
|
||||
+ f"&default={default}&f=y"
|
||||
)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
@@ -238,46 +229,25 @@ class AvatarImageView(TemplateView):
|
||||
if str(default) == "monsterid":
|
||||
monsterdata = BuildMonster(seed=kwargs["digest"], size=(size, size))
|
||||
data = BytesIO()
|
||||
monsterdata.save(data, "PNG", quality=JPEG_QUALITY)
|
||||
data.seek(0)
|
||||
response = CachingHttpResponse(uri, data, content_type="image/png")
|
||||
response["Cache-Control"] = "max-age=%i" % CACHE_IMAGES_MAX_AGE
|
||||
return response
|
||||
|
||||
return self._return_cached_png(monsterdata, data, uri)
|
||||
if str(default) == "robohash":
|
||||
roboset = "any"
|
||||
if request.GET.get("robohash"):
|
||||
roboset = request.GET.get("robohash")
|
||||
roboset = request.GET.get("robohash") or "any"
|
||||
robohash = Robohash(kwargs["digest"])
|
||||
robohash.assemble(roboset=roboset, sizex=size, sizey=size)
|
||||
data = BytesIO()
|
||||
robohash.img.save(data, format="png")
|
||||
data.seek(0)
|
||||
response = CachingHttpResponse(uri, data, content_type="image/png")
|
||||
response["Cache-Control"] = "max-age=%i" % CACHE_IMAGES_MAX_AGE
|
||||
return response
|
||||
|
||||
return self._return_cached_response(data, uri)
|
||||
if str(default) == "retro":
|
||||
identicon = Identicon.render(kwargs["digest"])
|
||||
data = BytesIO()
|
||||
img = Image.open(BytesIO(identicon))
|
||||
img = img.resize((size, size), Image.LANCZOS)
|
||||
img.save(data, "PNG", quality=JPEG_QUALITY)
|
||||
data.seek(0)
|
||||
response = CachingHttpResponse(uri, data, content_type="image/png")
|
||||
response["Cache-Control"] = "max-age=%i" % CACHE_IMAGES_MAX_AGE
|
||||
return response
|
||||
|
||||
return self._return_cached_png(img, data, uri)
|
||||
if str(default) == "pagan":
|
||||
paganobj = pagan.Avatar(kwargs["digest"])
|
||||
data = BytesIO()
|
||||
img = paganobj.img.resize((size, size), Image.LANCZOS)
|
||||
img.save(data, "PNG", quality=JPEG_QUALITY)
|
||||
data.seek(0)
|
||||
response = CachingHttpResponse(uri, data, content_type="image/png")
|
||||
response["Cache-Control"] = "max-age=%i" % CACHE_IMAGES_MAX_AGE
|
||||
return response
|
||||
|
||||
return self._return_cached_png(img, data, uri)
|
||||
if str(default) == "identicon":
|
||||
p = Pydenticon5() # pylint: disable=invalid-name
|
||||
# In order to make use of the whole 32 bytes digest, we need to redigest them.
|
||||
@@ -286,42 +256,16 @@ class AvatarImageView(TemplateView):
|
||||
).hexdigest()
|
||||
img = p.draw(newdigest, size, 0)
|
||||
data = BytesIO()
|
||||
img.save(data, "PNG", quality=JPEG_QUALITY)
|
||||
data.seek(0)
|
||||
response = CachingHttpResponse(uri, data, content_type="image/png")
|
||||
response["Cache-Control"] = "max-age=%i" % CACHE_IMAGES_MAX_AGE
|
||||
return response
|
||||
|
||||
return self._return_cached_png(img, data, uri)
|
||||
if str(default) == "mmng":
|
||||
mmngimg = mm_ng(idhash=kwargs["digest"], size=size)
|
||||
data = BytesIO()
|
||||
mmngimg.save(data, "PNG", quality=JPEG_QUALITY)
|
||||
data.seek(0)
|
||||
response = CachingHttpResponse(uri, data, content_type="image/png")
|
||||
response["Cache-Control"] = "max-age=%i" % CACHE_IMAGES_MAX_AGE
|
||||
return response
|
||||
|
||||
if str(default) == "mm" or str(default) == "mp":
|
||||
# If mm is explicitly given, we need to catch that
|
||||
static_img = path.join(
|
||||
"static", "img", "mm", "%s%s" % (str(size), ".png")
|
||||
)
|
||||
if not path.isfile(static_img):
|
||||
# We trust this exists!!!
|
||||
static_img = path.join("static", "img", "mm", "512.png")
|
||||
# We trust static/ is mapped to /static/
|
||||
return HttpResponseRedirect("/" + static_img)
|
||||
return self._return_cached_png(mmngimg, data, uri)
|
||||
if str(default) in {"mm", "mp"}:
|
||||
return self._redirect_static_w_size("mm", size)
|
||||
return HttpResponseRedirect(default)
|
||||
|
||||
static_img = path.join(
|
||||
"static", "img", "nobody", "%s%s" % (str(size), ".png")
|
||||
)
|
||||
if not path.isfile(static_img):
|
||||
# We trust this exists!!!
|
||||
static_img = path.join("static", "img", "nobody", "512.png")
|
||||
# We trust static/ is mapped to /static/
|
||||
return HttpResponseRedirect("/" + static_img)
|
||||
|
||||
return self._redirect_static_w_size("nobody", size)
|
||||
imgformat = obj.photo.format
|
||||
photodata = Image.open(BytesIO(obj.photo.data))
|
||||
|
||||
@@ -348,10 +292,32 @@ class AvatarImageView(TemplateView):
|
||||
obj.save()
|
||||
if imgformat == "jpg":
|
||||
imgformat = "jpeg"
|
||||
response = CachingHttpResponse(uri, data, content_type="image/%s" % imgformat)
|
||||
response = CachingHttpResponse(uri, data, content_type=f"image/{imgformat}")
|
||||
response["Cache-Control"] = "max-age=%i" % CACHE_IMAGES_MAX_AGE
|
||||
return response
|
||||
|
||||
def _redirect_static_w_size(self, arg0, size):
|
||||
"""
|
||||
Helper method to redirect to static image with size i/a
|
||||
"""
|
||||
# If mm is explicitly given, we need to catch that
|
||||
static_img = path.join("static", "img", arg0, f"{str(size)}.png")
|
||||
if not path.isfile(static_img):
|
||||
# We trust this exists!!!
|
||||
static_img = path.join("static", "img", arg0, "512.png")
|
||||
# We trust static/ is mapped to /static/
|
||||
return HttpResponseRedirect(f"/{static_img}")
|
||||
|
||||
def _return_cached_response(self, data, uri):
|
||||
data.seek(0)
|
||||
response = CachingHttpResponse(uri, data, content_type="image/png")
|
||||
response["Cache-Control"] = "max-age=%i" % CACHE_IMAGES_MAX_AGE
|
||||
return response
|
||||
|
||||
def _return_cached_png(self, arg0, data, uri):
|
||||
arg0.save(data, "PNG", quality=JPEG_QUALITY)
|
||||
return self._return_cached_response(data, uri)
|
||||
|
||||
|
||||
class GravatarProxyView(View):
|
||||
"""
|
||||
@@ -374,19 +340,16 @@ class GravatarProxyView(View):
|
||||
+ "&forcedefault=y"
|
||||
)
|
||||
if default is not None:
|
||||
url += "&default=%s" % default
|
||||
url += f"&default={default}"
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
size = get_size(request)
|
||||
gravatarimagedata = None
|
||||
default = None
|
||||
|
||||
try:
|
||||
with contextlib.suppress(Exception):
|
||||
if str(request.GET["default"]) != "None":
|
||||
default = request.GET["default"]
|
||||
except Exception: # pylint: disable=bare-except
|
||||
pass
|
||||
|
||||
if str(default) != "wavatar":
|
||||
# This part is special/hackish
|
||||
# Check if the image returned by Gravatar is their default image, if so,
|
||||
@@ -406,35 +369,34 @@ class GravatarProxyView(View):
|
||||
if exc.code == 404:
|
||||
cache.set(gravatar_test_url, "default", 60)
|
||||
else:
|
||||
print("Gravatar test url fetch failed: %s" % exc)
|
||||
print(f"Gravatar test url fetch failed: {exc}")
|
||||
return redir_default(default)
|
||||
|
||||
gravatar_url = (
|
||||
"https://secure.gravatar.com/avatar/" + kwargs["digest"] + "?s=%i" % size
|
||||
)
|
||||
if default:
|
||||
gravatar_url += "&d=%s" % default
|
||||
gravatar_url += f"&d={default}"
|
||||
|
||||
try:
|
||||
if cache.get(gravatar_url) == "err":
|
||||
print("Cached Gravatar fetch failed with URL error: %s" % gravatar_url)
|
||||
print(f"Cached Gravatar fetch failed with URL error: {gravatar_url}")
|
||||
return redir_default(default)
|
||||
|
||||
gravatarimagedata = urlopen(gravatar_url)
|
||||
except HTTPError as exc:
|
||||
if exc.code != 404 and exc.code != 503:
|
||||
if exc.code not in [404, 503]:
|
||||
print(
|
||||
"Gravatar fetch failed with an unexpected %s HTTP error: %s"
|
||||
% (exc.code, gravatar_url)
|
||||
f"Gravatar fetch failed with an unexpected {exc.code} HTTP error: {gravatar_url}"
|
||||
)
|
||||
cache.set(gravatar_url, "err", 30)
|
||||
return redir_default(default)
|
||||
except URLError as exc:
|
||||
print("Gravatar fetch failed with URL error: %s" % exc.reason)
|
||||
print(f"Gravatar fetch failed with URL error: {exc.reason}")
|
||||
cache.set(gravatar_url, "err", 30)
|
||||
return redir_default(default)
|
||||
except SSLError as exc:
|
||||
print("Gravatar fetch failed with SSL error: %s" % exc.reason)
|
||||
print(f"Gravatar fetch failed with SSL error: {exc.reason}")
|
||||
cache.set(gravatar_url, "err", 30)
|
||||
return redir_default(default)
|
||||
try:
|
||||
@@ -442,13 +404,13 @@ class GravatarProxyView(View):
|
||||
img = Image.open(data)
|
||||
data.seek(0)
|
||||
response = HttpResponse(
|
||||
data.read(), content_type="image/%s" % file_format(img.format)
|
||||
data.read(), content_type=f"image/{file_format(img.format)}"
|
||||
)
|
||||
response["Cache-Control"] = "max-age=%i" % CACHE_IMAGES_MAX_AGE
|
||||
return response
|
||||
|
||||
except ValueError as exc:
|
||||
print("Value error: %s" % exc)
|
||||
print(f"Value error: {exc}")
|
||||
return redir_default(default)
|
||||
|
||||
# We shouldn't reach this point... But make sure we do something
|
||||
@@ -474,7 +436,7 @@ class BlueskyProxyView(View):
|
||||
+ "&forcedefault=y"
|
||||
)
|
||||
if default is not None:
|
||||
url += "&default=%s" % default
|
||||
url += f"&default={default}"
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
size = get_size(request)
|
||||
@@ -482,12 +444,9 @@ class BlueskyProxyView(View):
|
||||
blueskyimagedata = None
|
||||
default = None
|
||||
|
||||
try:
|
||||
with contextlib.suppress(Exception):
|
||||
if str(request.GET["default"]) != "None":
|
||||
default = request.GET["default"]
|
||||
except Exception: # pylint: disable=bare-except
|
||||
pass
|
||||
|
||||
identity = None
|
||||
|
||||
# First check for email, as this is the most common
|
||||
@@ -517,12 +476,9 @@ class BlueskyProxyView(View):
|
||||
bs = Bluesky()
|
||||
bluesky_url = None
|
||||
# Try with the cache first
|
||||
try:
|
||||
with contextlib.suppress(Exception):
|
||||
if cache.get(identity.bluesky_handle):
|
||||
bluesky_url = cache.get(identity.bluesky_handle)
|
||||
except Exception: # pylint: disable=bare-except
|
||||
pass
|
||||
|
||||
if not bluesky_url:
|
||||
try:
|
||||
bluesky_url = bs.get_avatar(identity.bluesky_handle)
|
||||
@@ -532,30 +488,29 @@ class BlueskyProxyView(View):
|
||||
|
||||
try:
|
||||
if cache.get(bluesky_url) == "err":
|
||||
print("Cached Bluesky fetch failed with URL error: %s" % bluesky_url)
|
||||
print(f"Cached Bluesky fetch failed with URL error: {bluesky_url}")
|
||||
return redir_default(default)
|
||||
|
||||
blueskyimagedata = urlopen(bluesky_url)
|
||||
except HTTPError as exc:
|
||||
if exc.code != 404 and exc.code != 503:
|
||||
if exc.code not in [404, 503]:
|
||||
print(
|
||||
"Bluesky fetch failed with an unexpected %s HTTP error: %s"
|
||||
% (exc.code, bluesky_url)
|
||||
f"Bluesky fetch failed with an unexpected {exc.code} HTTP error: {bluesky_url}"
|
||||
)
|
||||
cache.set(bluesky_url, "err", 30)
|
||||
return redir_default(default)
|
||||
except URLError as exc:
|
||||
print("Bluesky fetch failed with URL error: %s" % exc.reason)
|
||||
print(f"Bluesky fetch failed with URL error: {exc.reason}")
|
||||
cache.set(bluesky_url, "err", 30)
|
||||
return redir_default(default)
|
||||
except SSLError as exc:
|
||||
print("Bluesky fetch failed with SSL error: %s" % exc.reason)
|
||||
print(f"Bluesky fetch failed with SSL error: {exc.reason}")
|
||||
cache.set(bluesky_url, "err", 30)
|
||||
return redir_default(default)
|
||||
try:
|
||||
data = BytesIO(blueskyimagedata.read())
|
||||
img = Image.open(data)
|
||||
format = img.format
|
||||
img_format = img.format
|
||||
if max(img.size) > size:
|
||||
aspect = img.size[0] / float(img.size[1])
|
||||
if aspect > 1:
|
||||
@@ -564,16 +519,16 @@ class BlueskyProxyView(View):
|
||||
new_size = (int(size * aspect), size)
|
||||
img = img.resize(new_size)
|
||||
data = BytesIO()
|
||||
img.save(data, format=format)
|
||||
img.save(data, format=img_format)
|
||||
|
||||
data.seek(0)
|
||||
response = HttpResponse(
|
||||
data.read(), content_type="image/%s" % file_format(format)
|
||||
data.read(), content_type=f"image/{file_format(format)}"
|
||||
)
|
||||
response["Cache-Control"] = "max-age=%i" % CACHE_IMAGES_MAX_AGE
|
||||
return response
|
||||
except ValueError as exc:
|
||||
print("Value error: %s" % exc)
|
||||
print(f"Value error: {exc}")
|
||||
return redir_default(default)
|
||||
|
||||
# We shouldn't reach this point... But make sure we do something
|
||||
|
||||
Reference in New Issue
Block a user