From eac88b5ef74af5ca3b205fbd106b73a12cad9e91 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Fri, 26 Jan 2024 11:44:02 +0100 Subject: [PATCH] Widget: Fix language on first iframe request --- src/pretix/api/auth/session.py | 4 ++- src/pretix/base/middleware.py | 38 ++++++++++++++++++---------- src/pretix/presale/views/__init__.py | 23 ++++++++++++++--- src/tests/api/test_auth.py | 2 +- src/tests/presale/test_widget.py | 6 ++++- 5 files changed, 52 insertions(+), 21 deletions(-) diff --git a/src/pretix/api/auth/session.py b/src/pretix/api/auth/session.py index cf55f87cf4..431741963f 100644 --- a/src/pretix/api/auth/session.py +++ b/src/pretix/api/auth/session.py @@ -20,7 +20,9 @@ # . # from rest_framework import exceptions -from rest_framework.authentication import SessionAuthentication as BaseSessionAuthentication +from rest_framework.authentication import ( + SessionAuthentication as BaseSessionAuthentication, +) from pretix.multidomain.middlewares import CsrfViewMiddleware diff --git a/src/pretix/base/middleware.py b/src/pretix/base/middleware.py index 7338a818a7..6b96d68cb7 100644 --- a/src/pretix/base/middleware.py +++ b/src/pretix/base/middleware.py @@ -44,6 +44,25 @@ from pretix.multidomain.urlreverse import ( _supported = None +def get_supported_language(requested_language, allowed_languages, default_language): + language = requested_language + if language not in allowed_languages: + firstpart = language.split('-')[0] + if firstpart in allowed_languages: + language = firstpart + else: + language = default_language + for lang in allowed_languages: + if lang.startswith(firstpart + '-'): + language = lang + break + if language not in allowed_languages: + # This seems redundant, but can happen in the rare edge case that settings.locale is (wrongfully) + # not part of settings.locales + language = allowed_languages[0] + return language + + class LocaleMiddleware(MiddlewareMixin): """ @@ -65,20 +84,11 @@ class LocaleMiddleware(MiddlewareMixin): settings_holder = None if settings_holder: - if language not in settings_holder.settings.locales: - firstpart = language.split('-')[0] - if firstpart in settings_holder.settings.locales: - language = firstpart - else: - language = settings_holder.settings.locale - for lang in settings_holder.settings.locales: - if lang.startswith(firstpart + '-'): - language = lang - break - if language not in settings_holder.settings.locales: - # This seems redundant, but can happen in the rare edge case that settings.locale is (wrongfully) - # not part of settings.locales - language = settings_holder.settings.locales[0] + language = get_supported_language( + language, + settings_holder.settings.locales, + settings_holder.settings.locale, + ) if '-' not in language and settings_holder.settings.region: language += '-' + settings_holder.settings.region else: diff --git a/src/pretix/presale/views/__init__.py b/src/pretix/presale/views/__init__.py index 9f36426a9e..70543c2ec8 100644 --- a/src/pretix/presale/views/__init__.py +++ b/src/pretix/presale/views/__init__.py @@ -41,12 +41,14 @@ from itertools import groupby from django.conf import settings from django.contrib import messages from django.db.models import Exists, OuterRef, Prefetch, Sum +from django.utils import translation from django.utils.functional import cached_property from django.utils.timezone import now from django.utils.translation import gettext_lazy as _ from django_scopes import scopes_disabled -from pretix.base.i18n import language +from pretix.base.i18n import get_language_without_region +from pretix.base.middleware import get_supported_language from pretix.base.models import ( CartPosition, Customer, InvoiceAddress, ItemAddOn, Question, QuestionAnswer, QuestionOption, TaxRule, @@ -455,11 +457,24 @@ def iframe_entry_view_wrapper(view_func): locale = request.GET.get('locale') if locale and locale in [lc for lc, ll in settings.LANGUAGES]: - region = None + lng = locale if hasattr(request, 'event'): + lng = get_supported_language( + lng, + request.event.settings.locales, + request.event.settings.locale, + ) + region = request.event.settings.region - with language(locale, region): - resp = view_func(request, *args, **kwargs) + if '-' not in lng and region: + lng += '-' + region.lower() + + # with language() is not good enough here – we really need to take the role of LocaleMiddleware and modify + # global state, because template rendering might be happening lazily. + translation.activate(lng) + request.LANGUAGE_CODE = get_language_without_region() + resp = view_func(request, *args, **kwargs) + max_age = 10 * 365 * 24 * 60 * 60 set_cookie_without_samesite( request, diff --git a/src/tests/api/test_auth.py b/src/tests/api/test_auth.py index 311d14014a..52f5f77377 100644 --- a/src/tests/api/test_auth.py +++ b/src/tests/api/test_auth.py @@ -24,9 +24,9 @@ import time import pytest from bs4 import BeautifulSoup from django.test import Client +from tests.base import extract_form_fields from pretix.base.models import Organizer -from tests.base import extract_form_fields @pytest.mark.django_db diff --git a/src/tests/presale/test_widget.py b/src/tests/presale/test_widget.py index c0a36620f3..987f8deb07 100644 --- a/src/tests/presale/test_widget.py +++ b/src/tests/presale/test_widget.py @@ -58,9 +58,13 @@ class WidgetCartTest(CartTestMixin, TestCase): ) def test_iframe_entry_view_wrapper(self): - self.client.get('/%s/%s/?iframe=1&locale=de' % (self.orga.slug, self.event.slug)) + self.event.settings.locales = ["de", "en"] + r = self.client.get('/%s/%s/?iframe=1&locale=de' % (self.orga.slug, self.event.slug), headers={ + "Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8", + }) assert 'iframe_session' in self.client.session assert self.client.cookies[settings.LANGUAGE_COOKIE_NAME].value == "de" + assert r.headers["Content-Language"] == "de" def test_allow_frame_if_namespaced(self): response = self.client.get('/%s/%s/' % (self.orga.slug, self.event.slug))