From 2efc64da7e4cb8d62c3def77602a264a87621e1d Mon Sep 17 00:00:00 2001 From: wassafshahzad Date: Thu, 11 Jun 2026 13:52:49 +0200 Subject: [PATCH 1/3] feat: Added FXA profile image --- pontoon/base/models/comment.py | 4 +-- pontoon/base/models/translation.py | 4 +-- pontoon/base/models/user.py | 4 +-- .../base/templates/allauth/layouts/base.html | 2 +- .../templates/widgets/latest_activity.html | 2 +- pontoon/base/templates/widgets/profile.html | 4 +-- pontoon/base/tests/models/test_comment.py | 6 ++-- pontoon/base/tests/test_user_utils.py | 33 ++++++++++++++++++- pontoon/base/user_utils.py | 11 +++++-- pontoon/base/views.py | 10 +++--- .../templates/contributors/contributors.html | 2 +- .../templates/contributors/profile.html | 2 +- .../templates/contributors/settings.html | 2 +- .../widgets/contributor_list.html | 2 +- 14 files changed, 63 insertions(+), 25 deletions(-) diff --git a/pontoon/base/models/comment.py b/pontoon/base/models/comment.py index c7459258a1..a9c49b2077 100644 --- a/pontoon/base/models/comment.py +++ b/pontoon/base/models/comment.py @@ -5,7 +5,7 @@ from django.utils import timezone from pontoon.base.models.user import User -from pontoon.base.user_utils import gravatar_url, user_banner +from pontoon.base.user_utils import avatar_url, user_banner if TYPE_CHECKING: @@ -45,7 +45,7 @@ def serialize(self, project_contact): "user_banner": user_banner(author, locale, project_contact) if author else "", - "user_gravatar_url_small": gravatar_url(author) if author else "", + "user_gravatar_url_small": avatar_url(author) if author else "", "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 e7d4d30678..92055bb4be 100644 --- a/pontoon/base/models/translation.py +++ b/pontoon/base/models/translation.py @@ -17,7 +17,7 @@ from pontoon.base.models.project_locale import ProjectLocale from pontoon.base.models.user import User from pontoon.base.simple_preview import get_simple_preview -from pontoon.base.user_utils import gravatar_url +from pontoon.base.user_utils import avatar_url from pontoon.checks import DB_FORMATS from pontoon.checks.utils import save_failed_checks @@ -99,7 +99,7 @@ def authors(self): "email": user.email, "display_name": user.name_or_email, "id": user.id, - "gravatar_url": gravatar_url(user), + "gravatar_url": avatar_url(user), "translation_count": user.translations_count, "role": user.user_role, } diff --git a/pontoon/base/models/user.py b/pontoon/base/models/user.py index 96b2c9157d..b79f8ce9f2 100644 --- a/pontoon/base/models/user.py +++ b/pontoon/base/models/user.py @@ -9,8 +9,8 @@ unread_notifications_display, ) from pontoon.base.user_utils import ( + avatar_url, can_translate_locales, - gravatar_url, manager_for_locales, translator_for_locales, ) @@ -92,7 +92,7 @@ def latest_action(user: User) -> ActionLog | None: AuthUser.add_to_class("display_name_and_email", display_name_and_email) AuthUser.add_to_class("latest_action", latest_action) -AuthUser.add_to_class("gravatar_url", gravatar_url) +AuthUser.add_to_class("avatar_url", avatar_url) AuthUser.add_to_class("translator_for_locales", translator_for_locales) AuthUser.add_to_class("manager_for_locales", manager_for_locales) AuthUser.add_to_class("can_translate_locales", can_translate_locales) diff --git a/pontoon/base/templates/allauth/layouts/base.html b/pontoon/base/templates/allauth/layouts/base.html index 4d2d4245d1..12943d1229 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 bbe0e4ed38..e7f33009ad 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() %} + {% set avatar = latest_activity.user.avatar_url() %} {% 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 aa8133cd5b..911717eb66 100644 --- a/pontoon/base/tests/models/test_comment.py +++ b/pontoon/base/tests/models/test_comment.py @@ -1,6 +1,6 @@ import pytest -from pontoon.base.user_utils import gravatar_url, user_banner +from pontoon.base.user_utils import avatar_url, user_banner from pontoon.test.factories import ProjectFactory @@ -14,7 +14,7 @@ def test_serialize_comments(comment_a, team_comment_a): "user_banner": user_banner( comment_a.author, comment_a.translation.locale, project.contact ), - "user_gravatar_url_small": gravatar_url(comment_a.author), + "user_gravatar_url_small": avatar_url(comment_a.author), "created_at": comment_a.timestamp.strftime("%b %d, %Y %H:%M"), "date_iso": comment_a.timestamp.isoformat(), "content": comment_a.content, @@ -28,7 +28,7 @@ def test_serialize_comments(comment_a, team_comment_a): "user_banner": user_banner( team_comment_a.author, team_comment_a.locale, project.contact ), - "user_gravatar_url_small": gravatar_url(team_comment_a.author), + "user_gravatar_url_small": avatar_url(team_comment_a.author), "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/test_user_utils.py b/pontoon/base/tests/test_user_utils.py index 52b081e7a5..ce4ccad981 100644 --- a/pontoon/base/tests/test_user_utils.py +++ b/pontoon/base/tests/test_user_utils.py @@ -1,7 +1,9 @@ import pytest +from allauth.socialaccount.models import SocialAccount + from pontoon.base.models.user import User -from pontoon.base.user_utils import user_banner, user_locale_role, user_role +from pontoon.base.user_utils import avatar_url, user_banner, user_locale_role, user_role @pytest.mark.django_db @@ -91,3 +93,32 @@ def test_user_banner(user_a, user_b, user_c, user_d, gt_user, locale_a, project_ # System user (Google Translate) project_contact = gt_user assert user_banner(gt_user, locale_a, project_contact)[1] == "" + + +@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 avatar_url(user_a) == "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 = avatar_url(user_a) + 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 = avatar_url(user_a) + assert "gravatar.com/avatar/" in url diff --git a/pontoon/base/user_utils.py b/pontoon/base/user_utils.py index 70d7c2d3f5..c50fa88378 100644 --- a/pontoon/base/user_utils.py +++ b/pontoon/base/user_utils.py @@ -2,6 +2,7 @@ from typing import TYPE_CHECKING, Any, Collection from urllib.parse import quote, urlencode +from allauth.socialaccount.models import SocialAccount from dateutil.relativedelta import relativedelta from guardian.shortcuts import get_objects_for_user @@ -20,7 +21,13 @@ def is_system_user(user: User) -> bool: return user.pk is None or user.profile.system_user -def gravatar_url(user: User, size: int = 88) -> str: +def avatar_url(user: User, size: int = 88) -> str: + fxa_account = SocialAccount.objects.filter(user=user, provider="fxa").first() + if fxa_account: + fxa_avatar = fxa_account.extra_data.get("avatar") + if fxa_avatar: + return fxa_avatar + email = md5(user.email.lower().encode("utf-8")).hexdigest() name = quote(user.display_name) @@ -42,7 +49,7 @@ def user_serialize(user: User): """Serialize Project contact""" return { - "avatar": gravatar_url(user), + "avatar": avatar_url(user), "name": user.name_or_email, "url": profile_url(user), } diff --git a/pontoon/base/views.py b/pontoon/base/views.py index ebd94cb6b3..4aa978d6cb 100755 --- a/pontoon/base/views.py +++ b/pontoon/base/views.py @@ -50,10 +50,10 @@ from pontoon.base.services import readonly_exists from pontoon.base.templatetags.helpers import provider_login_url from pontoon.base.user_utils import ( + avatar_url, can_manage_locales, can_translate, can_translate_locales, - gravatar_url, manager_for_locales, profile_url, translated_projects, @@ -492,7 +492,7 @@ def get_translation_history(request): "user": u.name_or_email, "uid": u.pk, "username": u.username, - "user_gravatar_url_small": gravatar_url(u), + "user_gravatar_url_small": avatar_url(u), "user_banner": user_banner(u, locale, project_contact), "date": t.date, "approved_user": t.approved_user.name_or_email @@ -856,7 +856,7 @@ def get_users(request): for u in users: payload.append( { - "gravatar": gravatar_url(u, 44), + "gravatar": avatar_url(u, 44), "name": u.name_or_email, "url": profile_url(u), "username": u.profile.username, @@ -1071,8 +1071,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": gravatar_url(user, 88), - "gravatar_url_big": gravatar_url(user, 176), + "gravatar_url_small": avatar_url(user, 88), + "gravatar_url_big": avatar_url(user, 176), "notifications": serialized_notifications(user), "theme": user.profile.theme, } diff --git a/pontoon/contributors/templates/contributors/contributors.html b/pontoon/contributors/templates/contributors/contributors.html index 117624ad64..1172977cd7 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 eafe9e6c94..0c8ecba89f 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 0bd67cea8a..9d16aa7d2f 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 0dc2e84b1e..d2fb2fd94e 100644 --- a/pontoon/contributors/templates/contributors/widgets/contributor_list.html +++ b/pontoon/contributors/templates/contributors/widgets/contributor_list.html @@ -29,7 +29,7 @@ > From ae140121e9ab7b5c32fbf36ee643c7c6e89bbe22 Mon Sep 17 00:00:00 2001 From: wassafshahzad Date: Wed, 17 Jun 2026 02:02:22 +0200 Subject: [PATCH 2/3] feat: Added fxa property to get fxa account avatar --- pontoon/base/models/user.py | 18 ++++++ pontoon/base/tests/test_user_utils.py | 58 ++++++++++++++++++- pontoon/base/user_utils.py | 10 +--- pontoon/base/views.py | 27 ++++++++- pontoon/contributors/utils.py | 19 ++++-- .../templates/messaging/includes/sent.html | 2 +- 6 files changed, 120 insertions(+), 14 deletions(-) diff --git a/pontoon/base/models/user.py b/pontoon/base/models/user.py index b79f8ce9f2..a0ec7ce0fd 100644 --- a/pontoon/base/models/user.py +++ b/pontoon/base/models/user.py @@ -73,6 +73,23 @@ def display_name_and_email(user: User) -> str: return f"{name} <{user.email}>" +def fxa_avatar(user: User) -> str | None: + if user.pk is None: + return + + if hasattr(user, "_prefetched_fxa_accounts") and isinstance( + user._prefetched_fxa_accounts, list + ): + return ( + user._prefetched_fxa_accounts[0].extra_data.get("avatar") + if user._prefetched_fxa_accounts + else None + ) + + fxa = user.socialaccount_set.filter(provider="fxa").first() + return fxa.extra_data.get("avatar") if fxa else None + + def latest_action(user: User) -> ActionLog | None: """ Return the date of the latest user activity (translation submission or review). @@ -93,6 +110,7 @@ def latest_action(user: User) -> ActionLog | None: AuthUser.add_to_class("latest_action", latest_action) AuthUser.add_to_class("avatar_url", avatar_url) +AuthUser.add_to_class("fxa_avatar", fxa_avatar) AuthUser.add_to_class("translator_for_locales", translator_for_locales) AuthUser.add_to_class("manager_for_locales", manager_for_locales) AuthUser.add_to_class("can_translate_locales", can_translate_locales) diff --git a/pontoon/base/tests/test_user_utils.py b/pontoon/base/tests/test_user_utils.py index ce4ccad981..a482959326 100644 --- a/pontoon/base/tests/test_user_utils.py +++ b/pontoon/base/tests/test_user_utils.py @@ -2,7 +2,7 @@ from allauth.socialaccount.models import SocialAccount -from pontoon.base.models.user import User +from pontoon.base.models.user import User, fxa_avatar from pontoon.base.user_utils import avatar_url, user_banner, user_locale_role, user_role @@ -122,3 +122,59 @@ def test_gravatar_url_falls_back_to_gravatar_when_fxa_has_no_avatar(user_a): ) url = avatar_url(user_a) assert "gravatar.com/avatar/" in url + + +@pytest.mark.django_db +def test_fxa_avatar_returns_none_for_unsaved_user(): + user = User(username="unsaved", email="unsaved@example.com") + assert fxa_avatar(user) is None + + +@pytest.mark.django_db +def test_fxa_avatar_returns_url_from_db(user_a): + SocialAccount.objects.create( + user=user_a, + provider="fxa", + uid="1234", + extra_data={"avatar": "https://profile.accounts.firefox.com/v1/avatar/abc"}, + ) + assert fxa_avatar(user_a) == "https://profile.accounts.firefox.com/v1/avatar/abc" + + +@pytest.mark.django_db +def test_fxa_avatar_returns_none_when_no_fxa_account(user_a): + assert fxa_avatar(user_a) is None + + +@pytest.mark.django_db +def test_fxa_avatar_returns_none_when_fxa_has_no_avatar(user_a): + SocialAccount.objects.create( + user=user_a, + provider="fxa", + uid="1234", + extra_data={}, + ) + assert fxa_avatar(user_a) is None + + +@pytest.mark.django_db +def test_fxa_avatar_uses_prefetched_accounts(user_a): + account = SocialAccount( + user=user_a, + provider="fxa", + uid="1234", + extra_data={ + "avatar": "https://profile.accounts.firefox.com/v1/avatar/prefetched" + }, + ) + user_a._prefetched_fxa_accounts = [account] + assert ( + fxa_avatar(user_a) + == "https://profile.accounts.firefox.com/v1/avatar/prefetched" + ) + + +@pytest.mark.django_db +def test_fxa_avatar_uses_prefetched_accounts_when_empty(user_a): + user_a._prefetched_fxa_accounts = [] + assert fxa_avatar(user_a) is None diff --git a/pontoon/base/user_utils.py b/pontoon/base/user_utils.py index c50fa88378..9803f5ace0 100644 --- a/pontoon/base/user_utils.py +++ b/pontoon/base/user_utils.py @@ -2,7 +2,6 @@ from typing import TYPE_CHECKING, Any, Collection from urllib.parse import quote, urlencode -from allauth.socialaccount.models import SocialAccount from dateutil.relativedelta import relativedelta from guardian.shortcuts import get_objects_for_user @@ -22,14 +21,11 @@ def is_system_user(user: User) -> bool: def avatar_url(user: User, size: int = 88) -> str: - fxa_account = SocialAccount.objects.filter(user=user, provider="fxa").first() - if fxa_account: - fxa_avatar = fxa_account.extra_data.get("avatar") - if fxa_avatar: - return fxa_avatar - email = md5(user.email.lower().encode("utf-8")).hexdigest() + if fxa_avatar := user.fxa_avatar(): + return fxa_avatar + email = md5(user.email.lower().encode("utf-8")).hexdigest() name = quote(user.display_name) background = "333941" color = "FFFFFF" diff --git a/pontoon/base/views.py b/pontoon/base/views.py index 4aa978d6cb..19c7f5c312 100755 --- a/pontoon/base/views.py +++ b/pontoon/base/views.py @@ -6,6 +6,8 @@ from datetime import datetime from urllib.parse import urlparse +from allauth.socialaccount.models import SocialAccount + from django.conf import settings from django.contrib import messages from django.contrib.auth.decorators import login_required @@ -472,7 +474,16 @@ def get_translation_history(request): "timestamp" ), ), - "user", + Prefetch( + "user", + queryset=User.objects.prefetch_related( + Prefetch( + "socialaccount_set", + queryset=SocialAccount.objects.filter(provider="fxa"), + to_attr="_prefetched_fxa_accounts", + ) + ), + ), "approved_user", "rejected_user", "errors", @@ -534,6 +545,13 @@ def get_team_comments(request): comments = ( Comment.objects.filter(entity=entity) .filter(Q(locale=locale) | Q(pinned=True)) + .prefetch_related( + Prefetch( + "author__socialaccount_set", + queryset=SocialAccount.objects.filter(provider="fxa"), + to_attr="_prefetched_fxa_accounts", + ) + ) .order_by("timestamp") ) @@ -840,6 +858,13 @@ def get_users(request): .exclude(email__regex=r"^deleted-user-(\w+)@example.com$") # Prefetch profile for retrieving username .prefetch_related("profile") + .prefetch_related( + Prefetch( + "socialaccount_set", + queryset=SocialAccount.objects.filter(provider="fxa"), + to_attr="_prefetched_fxa_accounts", + ) + ) .annotate( in_locale=Count( "translation", filter=Q(translation__locale__code=locale_code) diff --git a/pontoon/contributors/utils.py b/pontoon/contributors/utils.py index 6a1ef4fe21..6b87543d6c 100644 --- a/pontoon/contributors/utils.py +++ b/pontoon/contributors/utils.py @@ -6,6 +6,7 @@ import jwt +from allauth.socialaccount.models import SocialAccount from dateutil.relativedelta import relativedelta from django.conf import settings @@ -103,10 +104,20 @@ def users_with_translations_counts( for user in loc.translators_group.fetched_translators: translators[user].add(loc.code) - contributors = User.objects.filter( - pk__in=user_stats.keys(), - is_active=True, - ).prefetch_related("profile") + contributors = ( + User.objects.filter( + pk__in=user_stats.keys(), + is_active=True, + ) + .prefetch_related("profile") + .prefetch_related( + Prefetch( + "socialaccount_set", + queryset=SocialAccount.objects.filter(provider="fxa"), + to_attr="_prefetched_fxa_accounts", + ) + ) + ) if None in user_stats.keys(): contributors = list(contributors) diff --git a/pontoon/messaging/templates/messaging/includes/sent.html b/pontoon/messaging/templates/messaging/includes/sent.html index 3d4944f713..3ddf34ccbd 100644 --- a/pontoon/messaging/templates/messaging/includes/sent.html +++ b/pontoon/messaging/templates/messaging/includes/sent.html @@ -15,7 +15,7 @@ rel="noopener noreferrer" > Date: Fri, 19 Jun 2026 18:07:15 +0200 Subject: [PATCH 3/3] fix: Addressed feedback --- pontoon/base/models/user.py | 18 ------------------ pontoon/base/tests/test_user_utils.py | 24 ++++++++++++++++-------- pontoon/base/user_utils.py | 19 ++++++++++++++++++- 3 files changed, 34 insertions(+), 27 deletions(-) diff --git a/pontoon/base/models/user.py b/pontoon/base/models/user.py index a0ec7ce0fd..b79f8ce9f2 100644 --- a/pontoon/base/models/user.py +++ b/pontoon/base/models/user.py @@ -73,23 +73,6 @@ def display_name_and_email(user: User) -> str: return f"{name} <{user.email}>" -def fxa_avatar(user: User) -> str | None: - if user.pk is None: - return - - if hasattr(user, "_prefetched_fxa_accounts") and isinstance( - user._prefetched_fxa_accounts, list - ): - return ( - user._prefetched_fxa_accounts[0].extra_data.get("avatar") - if user._prefetched_fxa_accounts - else None - ) - - fxa = user.socialaccount_set.filter(provider="fxa").first() - return fxa.extra_data.get("avatar") if fxa else None - - def latest_action(user: User) -> ActionLog | None: """ Return the date of the latest user activity (translation submission or review). @@ -110,7 +93,6 @@ def latest_action(user: User) -> ActionLog | None: AuthUser.add_to_class("latest_action", latest_action) AuthUser.add_to_class("avatar_url", avatar_url) -AuthUser.add_to_class("fxa_avatar", fxa_avatar) AuthUser.add_to_class("translator_for_locales", translator_for_locales) AuthUser.add_to_class("manager_for_locales", manager_for_locales) AuthUser.add_to_class("can_translate_locales", can_translate_locales) diff --git a/pontoon/base/tests/test_user_utils.py b/pontoon/base/tests/test_user_utils.py index a482959326..87c80e9bd3 100644 --- a/pontoon/base/tests/test_user_utils.py +++ b/pontoon/base/tests/test_user_utils.py @@ -2,8 +2,14 @@ from allauth.socialaccount.models import SocialAccount -from pontoon.base.models.user import User, fxa_avatar -from pontoon.base.user_utils import avatar_url, user_banner, user_locale_role, user_role +from pontoon.base.models.user import User +from pontoon.base.user_utils import ( + avatar_url, + fxa_avatar_url, + user_banner, + user_locale_role, + user_role, +) @pytest.mark.django_db @@ -127,7 +133,7 @@ def test_gravatar_url_falls_back_to_gravatar_when_fxa_has_no_avatar(user_a): @pytest.mark.django_db def test_fxa_avatar_returns_none_for_unsaved_user(): user = User(username="unsaved", email="unsaved@example.com") - assert fxa_avatar(user) is None + assert fxa_avatar_url(user) is None @pytest.mark.django_db @@ -138,12 +144,14 @@ def test_fxa_avatar_returns_url_from_db(user_a): uid="1234", extra_data={"avatar": "https://profile.accounts.firefox.com/v1/avatar/abc"}, ) - assert fxa_avatar(user_a) == "https://profile.accounts.firefox.com/v1/avatar/abc" + assert ( + fxa_avatar_url(user_a) == "https://profile.accounts.firefox.com/v1/avatar/abc" + ) @pytest.mark.django_db def test_fxa_avatar_returns_none_when_no_fxa_account(user_a): - assert fxa_avatar(user_a) is None + assert fxa_avatar_url(user_a) is None @pytest.mark.django_db @@ -154,7 +162,7 @@ def test_fxa_avatar_returns_none_when_fxa_has_no_avatar(user_a): uid="1234", extra_data={}, ) - assert fxa_avatar(user_a) is None + assert fxa_avatar_url(user_a) is None @pytest.mark.django_db @@ -169,7 +177,7 @@ def test_fxa_avatar_uses_prefetched_accounts(user_a): ) user_a._prefetched_fxa_accounts = [account] assert ( - fxa_avatar(user_a) + fxa_avatar_url(user_a) == "https://profile.accounts.firefox.com/v1/avatar/prefetched" ) @@ -177,4 +185,4 @@ def test_fxa_avatar_uses_prefetched_accounts(user_a): @pytest.mark.django_db def test_fxa_avatar_uses_prefetched_accounts_when_empty(user_a): user_a._prefetched_fxa_accounts = [] - assert fxa_avatar(user_a) is None + assert fxa_avatar_url(user_a) is None diff --git a/pontoon/base/user_utils.py b/pontoon/base/user_utils.py index 9803f5ace0..b583f86d94 100644 --- a/pontoon/base/user_utils.py +++ b/pontoon/base/user_utils.py @@ -20,9 +20,26 @@ def is_system_user(user: User) -> bool: return user.pk is None or user.profile.system_user +def fxa_avatar_url(user: User) -> str | None: + if user.pk is None: + return None + + if hasattr(user, "_prefetched_fxa_accounts") and isinstance( + user._prefetched_fxa_accounts, list + ): + return ( + user._prefetched_fxa_accounts[0].extra_data.get("avatar") + if user._prefetched_fxa_accounts + else None + ) + + fxa = user.socialaccount_set.filter(provider="fxa").first() + return fxa.extra_data.get("avatar") if fxa else None + + def avatar_url(user: User, size: int = 88) -> str: - if fxa_avatar := user.fxa_avatar(): + if fxa_avatar := fxa_avatar_url(user): return fxa_avatar email = md5(user.email.lower().encode("utf-8")).hexdigest()