Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pontoon/base/models/comment.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions pontoon/base/models/translation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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,
}
Expand Down
4 changes: 2 additions & 2 deletions pontoon/base/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion pontoon/base/templates/allauth/layouts/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
{% if user.is_authenticated %}
<img
class="rounded"
src="{{ user.gravatar_url }}"
src="{{ user.avatar_url }}"
width="44"
height="44"
/>
Expand Down
2 changes: 1 addition & 1 deletion pontoon/base/templates/widgets/latest_activity.html
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '' %}
Expand Down
4 changes: 2 additions & 2 deletions pontoon/base/templates/widgets/profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{% if user.is_authenticated %}
<img
class="rounded"
src="{{ user.gravatar_url(88) }}"
src="{{ user.avatar_url(88) }}"
width="44"
height="44"
/>
Expand All @@ -21,7 +21,7 @@
>
<img
class="rounded"
src="{{ user.gravatar_url(176) }}"
src="{{ user.avatar_url(176) }}"
width="88"
height="88"
/>
Expand Down
6 changes: 3 additions & 3 deletions pontoon/base/tests/models/test_comment.py
Original file line number Diff line number Diff line change
@@ -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


Expand All @@ -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,
Expand All @@ -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,
Expand Down
97 changes: 96 additions & 1 deletion pontoon/base/tests/test_user_utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
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,
fxa_avatar_url,
user_banner,
user_locale_role,
user_role,
)


@pytest.mark.django_db
Expand Down Expand Up @@ -91,3 +99,90 @@ 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


@pytest.mark.django_db
def test_fxa_avatar_returns_none_for_unsaved_user():
user = User(username="unsaved", email="unsaved@example.com")
assert fxa_avatar_url(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_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_url(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_url(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_url(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_url(user_a) is None
26 changes: 23 additions & 3 deletions pontoon/base/user_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,29 @@ 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:
email = md5(user.email.lower().encode("utf-8")).hexdigest()
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 := fxa_avatar_url(user):
return fxa_avatar

email = md5(user.email.lower().encode("utf-8")).hexdigest()
name = quote(user.display_name)
background = "333941"
color = "FFFFFF"
Expand All @@ -42,7 +62,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),
}
Expand Down
37 changes: 31 additions & 6 deletions pontoon/base/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -50,10 +52,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,
Expand Down Expand Up @@ -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",
Expand All @@ -492,7 +503,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
Expand Down Expand Up @@ -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")
)

Expand Down Expand Up @@ -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)
Expand All @@ -856,7 +881,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,
Expand Down Expand Up @@ -1071,8 +1096,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,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ <h1>
>
<img
class="rounded"
src="{{ top_contributor.gravatar_url(252) }}"
src="{{ top_contributor.avatar_url(252) }}"
height="126"
width="126"
/>
Expand Down
2 changes: 1 addition & 1 deletion pontoon/contributors/templates/contributors/profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
{% endif %}
<img
class="rounded"
src="{{ contributor.gravatar_url(512) }}"
src="{{ contributor.avatar_url(512) }}"
width="256"
height="256"
/>
Expand Down
2 changes: 1 addition & 1 deletion pontoon/contributors/templates/contributors/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<div class="desc">Update profile picture</div>
<img
class="rounded"
src="{{ user.gravatar_url(400) }}"
src="{{ user.avatar_url(400) }}"
width="200"
height="200"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
>
<img
class="rounded"
src="{{ contributor.gravatar_url() }}"
src="{{ contributor.avatar_url() }}"
width="44"
height="44"
/>
Expand Down
Loading
Loading