From 2986d883b381f17ef555c1596b2b4f29641ac3f2 Mon Sep 17 00:00:00 2001 From: wassafshahzad Date: Tue, 9 Jun 2026 10:42:22 +0200 Subject: [PATCH 1/2] feat: Added FXA profile image --- pontoon/base/models/user.py | 6 +++++ pontoon/base/tests/models/test_user.py | 32 ++++++++++++++++++++++++++ pontoon/settings/base.py | 2 +- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/pontoon/base/models/user.py b/pontoon/base/models/user.py index 9f89a208ef..de8494c688 100644 --- a/pontoon/base/models/user.py +++ b/pontoon/base/models/user.py @@ -23,6 +23,12 @@ def user_profile_url(self): def user_gravatar_url(self, size): + fxa_account = self.socialaccount_set.filter(provider="fxa").first() + if fxa_account: + fxa_avatar = fxa_account.extra_data.get("avatar") + if fxa_avatar: + return fxa_avatar + email = md5(self.email.lower().encode("utf-8")).hexdigest() data = { "s": str(size), diff --git a/pontoon/base/tests/models/test_user.py b/pontoon/base/tests/models/test_user.py index 7b573c4278..0b8736fbac 100644 --- a/pontoon/base/tests/models/test_user.py +++ b/pontoon/base/tests/models/test_user.py @@ -2,6 +2,7 @@ import pytest +from allauth.socialaccount.models import SocialAccount from notifications.models import Notification from notifications.signals import notify @@ -199,3 +200,34 @@ def test_serialized_notifications_new_string_without_created_time(user_a, projec assert notification["actor"]["url"] == ( f"/projects/{project_a.slug}/all-resources/?status=missing,pretranslated" ) + + +@pytest.mark.django_db +def test_gravatar_url_returns_fxa_avatar_when_linked(user_a): + SocialAccount.objects.create( + user=user_a, + provider="fxa", + uid="1234", + extra_data={"avatar": "https://profile.accounts.firefox.com/v1/avatar/abc"}, + ) + assert ( + user_a.gravatar_url(88) == "https://profile.accounts.firefox.com/v1/avatar/abc" + ) + + +@pytest.mark.django_db +def test_gravatar_url_falls_back_to_gravatar_when_no_fxa(user_a): + url = user_a.gravatar_url(88) + assert "gravatar.com/avatar/" in url + + +@pytest.mark.django_db +def test_gravatar_url_falls_back_to_gravatar_when_fxa_has_no_avatar(user_a): + SocialAccount.objects.create( + user=user_a, + provider="fxa", + uid="1234", + extra_data={}, + ) + url = user_a.gravatar_url(88) + assert "gravatar.com/avatar/" in url diff --git a/pontoon/settings/base.py b/pontoon/settings/base.py index b3114234e7..558fe8f319 100644 --- a/pontoon/settings/base.py +++ b/pontoon/settings/base.py @@ -1112,7 +1112,7 @@ def account_username(user): FXA_SECRET_KEY = os.environ.get("FXA_SECRET_KEY") FXA_OAUTH_ENDPOINT = os.environ.get("FXA_OAUTH_ENDPOINT", "") FXA_PROFILE_ENDPOINT = os.environ.get("FXA_PROFILE_ENDPOINT", "") -FXA_SCOPE = ["profile:uid", "profile:display_name", "profile:email"] +FXA_SCOPE = ["profile:uid", "profile:display_name", "profile:email", "profile:avatar"] # Github GITHUB_CLIENT_ID = os.environ.get("GITHUB_CLIENT_ID") From b34d041580863174730d4d541c4bc10904dcaf93 Mon Sep 17 00:00:00 2001 From: wassafshahzad Date: Tue, 9 Jun 2026 14:20:05 +0200 Subject: [PATCH 2/2] fix: changed property name and function name --- pontoon/base/models/comment.py | 2 +- pontoon/base/models/translation.py | 2 +- pontoon/base/models/user.py | 12 ++++++------ pontoon/base/templates/allauth/layouts/base.html | 2 +- pontoon/base/templates/widgets/latest_activity.html | 2 +- pontoon/base/templates/widgets/profile.html | 4 ++-- pontoon/base/tests/models/test_comment.py | 4 ++-- pontoon/base/tests/models/test_user.py | 8 +++----- pontoon/base/views.py | 8 ++++---- .../templates/contributors/contributors.html | 2 +- .../contributors/templates/contributors/profile.html | 2 +- .../templates/contributors/settings.html | 2 +- .../contributors/widgets/contributor_list.html | 2 +- .../messaging/templates/messaging/includes/sent.html | 2 +- 14 files changed, 26 insertions(+), 28 deletions(-) diff --git a/pontoon/base/models/comment.py b/pontoon/base/models/comment.py index 30aba44118..f2d653f5c7 100644 --- a/pontoon/base/models/comment.py +++ b/pontoon/base/models/comment.py @@ -32,7 +32,7 @@ def serialize(self, project_contact): "author": self.author.name_or_email, "username": self.author.username, "user_banner": self.author.banner(locale, project_contact), - "user_gravatar_url_small": self.author.gravatar_url(88), + "user_gravatar_url_small": self.author.avatar_url(88), "created_at": self.timestamp.strftime("%b %d, %Y %H:%M"), "date_iso": self.timestamp.isoformat(), "content": self.content, diff --git a/pontoon/base/models/translation.py b/pontoon/base/models/translation.py index 82682a7fbd..0e0594cf5a 100644 --- a/pontoon/base/models/translation.py +++ b/pontoon/base/models/translation.py @@ -90,7 +90,7 @@ def authors(self): "email": user.email, "display_name": user.name_or_email, "id": user.id, - "gravatar_url": user.gravatar_url(88), + "gravatar_url": user.avatar_url(88), "translation_count": user.translations_count, "role": user.user_role, } diff --git a/pontoon/base/models/user.py b/pontoon/base/models/user.py index de8494c688..464f8a5f23 100644 --- a/pontoon/base/models/user.py +++ b/pontoon/base/models/user.py @@ -22,7 +22,7 @@ def user_profile_url(self): ) -def user_gravatar_url(self, size): +def user_avatar_url(self, size): fxa_account = self.socialaccount_set.filter(provider="fxa").first() if fxa_account: fxa_avatar = fxa_account.extra_data.get("avatar") @@ -46,8 +46,8 @@ def user_gravatar_url(self, size): @property -def user_gravatar_url_small(self): - return user_gravatar_url(self, 88) +def user_avatar_url_small(self): + return user_avatar_url(self, 88) @property @@ -518,7 +518,7 @@ def user_serialize(self): """Serialize Project contact""" return { - "avatar": self.gravatar_url_small, + "avatar": self.avatar_url_small, "name": self.name_or_email, "url": self.profile_url, } @@ -539,8 +539,8 @@ def latest_action(self): User.add_to_class("profile_url", user_profile_url) -User.add_to_class("gravatar_url", user_gravatar_url) -User.add_to_class("gravatar_url_small", user_gravatar_url_small) +User.add_to_class("avatar_url", user_avatar_url) +User.add_to_class("avatar_url_small", user_avatar_url_small) User.add_to_class("name_or_email", user_name_or_email) User.add_to_class("contact_email", user_contact_email) User.add_to_class("display_name", user_display_name) diff --git a/pontoon/base/templates/allauth/layouts/base.html b/pontoon/base/templates/allauth/layouts/base.html index b8bf445348..a2e80b50c3 100644 --- a/pontoon/base/templates/allauth/layouts/base.html +++ b/pontoon/base/templates/allauth/layouts/base.html @@ -100,7 +100,7 @@ {% if user.is_authenticated %} diff --git a/pontoon/base/templates/widgets/latest_activity.html b/pontoon/base/templates/widgets/latest_activity.html index 6dba6ccbbd..3eca62f1b2 100644 --- a/pontoon/base/templates/widgets/latest_activity.html +++ b/pontoon/base/templates/widgets/latest_activity.html @@ -5,7 +5,7 @@ {% set action = latest_activity.type + ' by' %} {% set user = latest_activity.user.name_or_email %} {% set link = url('pontoon.contributors.contributor.username', latest_activity.user.username) %} - {% set avatar = latest_activity.user.gravatar_url(88) %} + {% set avatar = latest_activity.user.avatar_url(88) %} {% else %} {% set action = 'imported' %} {% set user = '' %} diff --git a/pontoon/base/templates/widgets/profile.html b/pontoon/base/templates/widgets/profile.html index 44963b706d..c74dcb615f 100644 --- a/pontoon/base/templates/widgets/profile.html +++ b/pontoon/base/templates/widgets/profile.html @@ -3,7 +3,7 @@ {% if user.is_authenticated %} @@ -21,7 +21,7 @@ > diff --git a/pontoon/base/tests/models/test_comment.py b/pontoon/base/tests/models/test_comment.py index e622cf16e5..effa277bcf 100644 --- a/pontoon/base/tests/models/test_comment.py +++ b/pontoon/base/tests/models/test_comment.py @@ -13,7 +13,7 @@ def test_serialize_comments(comment_a, team_comment_a): "user_banner": comment_a.author.banner( comment_a.translation.locale, project.contact ), - "user_gravatar_url_small": comment_a.author.gravatar_url(88), + "user_gravatar_url_small": comment_a.author.avatar_url(88), "created_at": comment_a.timestamp.strftime("%b %d, %Y %H:%M"), "date_iso": comment_a.timestamp.isoformat(), "content": comment_a.content, @@ -27,7 +27,7 @@ def test_serialize_comments(comment_a, team_comment_a): "user_banner": team_comment_a.author.banner( team_comment_a.locale, project.contact ), - "user_gravatar_url_small": team_comment_a.author.gravatar_url(88), + "user_gravatar_url_small": team_comment_a.author.avatar_url(88), "created_at": team_comment_a.timestamp.strftime("%b %d, %Y %H:%M"), "date_iso": team_comment_a.timestamp.isoformat(), "content": team_comment_a.content, diff --git a/pontoon/base/tests/models/test_user.py b/pontoon/base/tests/models/test_user.py index 0b8736fbac..18a3b9bed9 100644 --- a/pontoon/base/tests/models/test_user.py +++ b/pontoon/base/tests/models/test_user.py @@ -210,14 +210,12 @@ def test_gravatar_url_returns_fxa_avatar_when_linked(user_a): uid="1234", extra_data={"avatar": "https://profile.accounts.firefox.com/v1/avatar/abc"}, ) - assert ( - user_a.gravatar_url(88) == "https://profile.accounts.firefox.com/v1/avatar/abc" - ) + assert user_a.avatar_url(88) == "https://profile.accounts.firefox.com/v1/avatar/abc" @pytest.mark.django_db def test_gravatar_url_falls_back_to_gravatar_when_no_fxa(user_a): - url = user_a.gravatar_url(88) + url = user_a.avatar_url(88) assert "gravatar.com/avatar/" in url @@ -229,5 +227,5 @@ def test_gravatar_url_falls_back_to_gravatar_when_fxa_has_no_avatar(user_a): uid="1234", extra_data={}, ) - url = user_a.gravatar_url(88) + url = user_a.avatar_url(88) assert "gravatar.com/avatar/" in url diff --git a/pontoon/base/views.py b/pontoon/base/views.py index 0b7b444b75..c8f97e9b24 100755 --- a/pontoon/base/views.py +++ b/pontoon/base/views.py @@ -474,7 +474,7 @@ def get_translation_history(request): "user": u.name_or_email, "uid": u.id, "username": u.username, - "user_gravatar_url_small": u.gravatar_url(88), + "user_gravatar_url_small": u.avatar_url(88), "user_banner": u.banner(locale, project_contact), "date": t.date, "approved_user": User.display_name_or_blank(t.approved_user), @@ -834,7 +834,7 @@ def get_users(request): for u in users: payload.append( { - "gravatar": u.gravatar_url(44), + "gravatar": u.avatar_url(44), "name": u.name_or_email, "url": u.profile_url, "username": u.profile.username, @@ -1051,8 +1051,8 @@ def user_data(request): "tour_status": user.profile.tour_status, "has_dismissed_addon_promotion": user.profile.has_dismissed_addon_promotion, "logout_url": logout_url, - "gravatar_url_small": user.gravatar_url(88), - "gravatar_url_big": user.gravatar_url(176), + "gravatar_url_small": user.avatar_url(88), + "gravatar_url_big": user.avatar_url(176), "notifications": user.serialized_notifications, "theme": user.profile.theme, } diff --git a/pontoon/contributors/templates/contributors/contributors.html b/pontoon/contributors/templates/contributors/contributors.html index 4c0848dbd5..074bc86eb3 100644 --- a/pontoon/contributors/templates/contributors/contributors.html +++ b/pontoon/contributors/templates/contributors/contributors.html @@ -60,7 +60,7 @@

> diff --git a/pontoon/contributors/templates/contributors/profile.html b/pontoon/contributors/templates/contributors/profile.html index 5646a3c0b0..d8c0644589 100644 --- a/pontoon/contributors/templates/contributors/profile.html +++ b/pontoon/contributors/templates/contributors/profile.html @@ -48,7 +48,7 @@ {% endif %} diff --git a/pontoon/contributors/templates/contributors/settings.html b/pontoon/contributors/templates/contributors/settings.html index 029ebe210e..43ba5efa83 100644 --- a/pontoon/contributors/templates/contributors/settings.html +++ b/pontoon/contributors/templates/contributors/settings.html @@ -14,7 +14,7 @@
Update profile picture
diff --git a/pontoon/contributors/templates/contributors/widgets/contributor_list.html b/pontoon/contributors/templates/contributors/widgets/contributor_list.html index 7b8add9e81..0a998654a2 100644 --- a/pontoon/contributors/templates/contributors/widgets/contributor_list.html +++ b/pontoon/contributors/templates/contributors/widgets/contributor_list.html @@ -29,7 +29,7 @@ > diff --git a/pontoon/messaging/templates/messaging/includes/sent.html b/pontoon/messaging/templates/messaging/includes/sent.html index b289454177..a62b17e6c0 100644 --- a/pontoon/messaging/templates/messaging/includes/sent.html +++ b/pontoon/messaging/templates/messaging/includes/sent.html @@ -15,7 +15,7 @@ rel="noopener noreferrer" >