From f378f48e148f12e09a0f429cf84f2b2b91a017f4 Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Thu, 5 Nov 2020 16:44:36 +0000 Subject: [PATCH] Prevent errors on get_url_parts when a non-content-language locale is active (#6521) Fixes #6510 and #6511 Catch the LookupError from get_supported_content_language_variant so that if the active language is not one that is recognised as a content language, the URL prefix will be left unchanged from the page's reported locale. Additionally, skip the `translation.override` when WAGTAIL_I18N_ENABLED is false; this accounts for the fact that existing sites may have used alternative / custom i18n implementations involving i18n_patterns, and on these sites we can't rely on the page's locale field to be meaningful. That way, we restore the pre-2.11 behaviour: if the existing i18n code was using some kind of wrapper around the get_url_parts call, then that will work again; and if not, the URL will reflect the current active locale, which is no worse than before. --- CHANGELOG.txt | 1 + docs/releases/2.11.1.rst | 1 + wagtail/admin/tests/api/test_pages.py | 16 ++++++++++++++++ wagtail/core/models.py | 24 ++++++++++++++++++------ wagtail/core/tests/test_page_model.py | 6 ++++++ 5 files changed, 42 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 2971c0b10e..4d37ef8e24 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -12,6 +12,7 @@ Changelog * Fix: Ensure that cached `wagtail_site_root_paths` structures from older Wagtail versions are invalidated (Sævar Öfjörð Magnússon) * Fix: Avoid circular import between wagtail.admin.auth and custom user models (Matt Westcott) + * Fix: Prevent error on resolving page URLs when a locale outside of `WAGTAIL_CONTENT_LANGUAGES` is active (Matt Westcott) 2.11 LTS (02.11.2020) diff --git a/docs/releases/2.11.1.rst b/docs/releases/2.11.1.rst index 3276d75ade..647e5bf9cf 100644 --- a/docs/releases/2.11.1.rst +++ b/docs/releases/2.11.1.rst @@ -15,3 +15,4 @@ Bug fixes * Ensure that cached ``wagtail_site_root_paths`` structures from older Wagtail versions are invalidated (Sævar Öfjörð Magnússon) * Avoid circular import between wagtail.admin.auth and custom user models (Matt Westcott) + * Prevent error on resolving page URLs when a locale outside of ``WAGTAIL_CONTENT_LANGUAGES`` is active (Matt Westcott) diff --git a/wagtail/admin/tests/api/test_pages.py b/wagtail/admin/tests/api/test_pages.py index b994b620cb..03af041d5e 100644 --- a/wagtail/admin/tests/api/test_pages.py +++ b/wagtail/admin/tests/api/test_pages.py @@ -2,6 +2,7 @@ import collections import datetime import json +from django.contrib.auth import get_user_model from django.urls import reverse from django.utils import timezone @@ -9,6 +10,7 @@ from wagtail.api.v2.tests.test_pages import TestPageDetail, TestPageListing from wagtail.core.models import Locale, Page from wagtail.tests.demosite import models from wagtail.tests.testapp.models import SimplePage, StreamPage +from wagtail.users.models import UserProfile from .utils import AdminAPITestCase @@ -101,6 +103,20 @@ class TestAdminPageListing(AdminAPITestCase, TestPageListing): content = json.loads(response.content.decode('UTF-8')) self.assertEqual(content['meta']['total_count'], new_total_count) + def test_get_in_non_content_language(self): + # set logged-in user's admin UI language to Swedish + user = get_user_model().objects.get(email='test@email.com') + UserProfile.objects.update_or_create(user=user, defaults={'preferred_language': 'se'}) + + response = self.get_response() + + self.assertEqual(response.status_code, 200) + self.assertEqual(response['Content-type'], 'application/json') + + # Will crash if the JSON is invalid + content = json.loads(response.content.decode('UTF-8')) + self.assertIn('meta', content) + # FIELDS # Not applicable to the admin API diff --git a/wagtail/core/models.py b/wagtail/core/models.py index 17a5f2d48c..d60419a469 100644 --- a/wagtail/core/models.py +++ b/wagtail/core/models.py @@ -1585,16 +1585,28 @@ class Page(AbstractPage, index.Indexed, ClusterableModel, metaclass=PageBase): else: site_id, root_path, root_url, language_code = possible_sites[0] - # If the active language code is a variant of the page's language, then - # use that instead - # This is used when LANGUAGES contain more languages than WAGTAIL_CONTENT_LANGUAGES - if get_supported_content_language_variant(translation.get_language()) == language_code: - language_code = translation.get_language() + use_wagtail_i18n = getattr(settings, 'WAGTAIL_I18N_ENABLED', False) + + if use_wagtail_i18n: + # If the active language code is a variant of the page's language, then + # use that instead + # This is used when LANGUAGES contain more languages than WAGTAIL_CONTENT_LANGUAGES + try: + if get_supported_content_language_variant(translation.get_language()) == language_code: + language_code = translation.get_language() + except LookupError: + # active language code is not a recognised content language, so leave + # page's language code unchanged + pass # The page may not be routable because wagtail_serve is not registered # This may be the case if Wagtail is used headless try: - with translation.override(language_code): + if use_wagtail_i18n: + with translation.override(language_code): + page_path = reverse( + 'wagtail_serve', args=(self.url_path[len(root_path):],)) + else: page_path = reverse( 'wagtail_serve', args=(self.url_path[len(root_path):],)) except NoReverseMatch: diff --git a/wagtail/core/tests/test_page_model.py b/wagtail/core/tests/test_page_model.py index f9abc76bf9..35b0681ade 100644 --- a/wagtail/core/tests/test_page_model.py +++ b/wagtail/core/tests/test_page_model.py @@ -499,6 +499,12 @@ class TestRoutingWithI18N(TestRouting): with translation.override("en-us"): self.test_urls() + def test_urls_with_language_not_in_wagtail_content_languages(self): + # If the active locale doesn't map to anything in WAGTAIL_CONTENT_LANGUAGES, + # URL prefixes should remain the same as the page's reported locale + with translation.override("se"): + self.test_urls() + def test_urls_with_different_language_tree(self): default_site = Site.objects.get(is_default_site=True) homepage = Page.objects.get(url_path='/home/')