diff --git a/network-api/networkapi/settings.py b/network-api/networkapi/settings.py index 99f22f7d684..1596d99081d 100644 --- a/network-api/networkapi/settings.py +++ b/network-api/networkapi/settings.py @@ -339,6 +339,7 @@ 'blog_tags': 'networkapi.wagtailpages.templatetags.blog_tags', 'card_tags': 'networkapi.wagtailpages.templatetags.card_tags', 'class_tags': 'networkapi.wagtailpages.templatetags.class_tags', + 'debug_tags': 'networkapi.wagtailpages.templatetags.debug_tags', 'homepage_tags': 'networkapi.wagtailpages.templatetags.homepage_tags', 'localization': 'networkapi.wagtailpages.templatetags.localization', 'mini_site_tags': 'networkapi.wagtailpages.templatetags.mini_site_tags', diff --git a/network-api/networkapi/wagtailpages/pagemodels/mixin/snippets.py b/network-api/networkapi/wagtailpages/pagemodels/mixin/snippets.py new file mode 100644 index 00000000000..432da0302fb --- /dev/null +++ b/network-api/networkapi/wagtailpages/pagemodels/mixin/snippets.py @@ -0,0 +1,17 @@ +from django.conf import settings +from wagtail.core.models import Locale + +DEFAULT_LOCALE = Locale.objects.get(language_code=settings.LANGUAGE_CODE) + + +class LocalizedSnippet(): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + @property + def original(self): + try: + return self.get_translation(DEFAULT_LOCALE) + except AttributeError: + return self + diff --git a/network-api/networkapi/wagtailpages/pagemodels/products.py b/network-api/networkapi/wagtailpages/pagemodels/products.py index 07a7e9605ce..1d6d1ab7405 100644 --- a/network-api/networkapi/wagtailpages/pagemodels/products.py +++ b/network-api/networkapi/wagtailpages/pagemodels/products.py @@ -25,7 +25,6 @@ from wagtail.snippets.models import register_snippet from wagtail_localize.fields import SynchronizedField, TranslatableField - from wagtail_airtable.mixins import AirtableMixin from networkapi.wagtailpages.fields import ExtendedBoolean, ExtendedYesNoField @@ -36,7 +35,7 @@ # TODO: Move this util function from networkapi.buyersguide.utils import get_category_og_image_upload_path - +from .mixin.snippets import LocalizedSnippet TRACK_RECORD_CHOICES = [ ('Great', 'Great'), @@ -45,6 +44,31 @@ ('Bad', 'Bad') ] +DEFAULT_LOCALE_ID = Locale.objects.get(language_code=settings.LANGUAGE_CODE).id + + +def get_language_code_from_request(request): + """ + Accepts a request. Returns a language code (string) if there is one. Falls back to English. + """ + default_language_code = settings.LANGUAGE_CODE + if hasattr(request, 'LANGUAGE_CODE'): + default_language_code = request.LANGUAGE_CODE + return default_language_code + +def get_categories_for_locale(language_code): + """ + Make sure that we check both the "whatever the current locale" category + for whether or not it's hidden, but also the original English version. + """ + return [ + cat + for cat in BuyersGuideProductCategory.objects.filter( + hidden=False, + locale=Locale.objects.get(language_code=language_code) + ) + if not cat.original.hidden + ] def get_product_subset(cutoff_date, authenticated, key, products, language_code='en'): """ @@ -53,10 +77,7 @@ def get_product_subset(cutoff_date, authenticated, key, products, language_code= to the system or not (authenticated users get to see all products, including draft products) """ - products = products.filter( - review_date__gte=cutoff_date, - locale=Locale.objects.get(language_code=language_code) - ) + products = products.filter(review_date__gte=cutoff_date) if not authenticated: products = products.live() products = sort_average(products) @@ -71,7 +92,7 @@ def sort_average(products): @register_snippet -class BuyersGuideProductCategory(TranslatableMixin, models.Model): +class BuyersGuideProductCategory(TranslatableMixin, LocalizedSnippet, models.Model): """ A simple category class for use with Buyers Guide products, registered as snippet so that we can moderate them if and @@ -121,11 +142,6 @@ class BuyersGuideProductCategory(TranslatableMixin, models.Model): def published_product_page_count(self): return ProductPage.objects.filter(product_categories__category=self).live().count() - @property - def published_product_count(self): - # TODO: REMOVE: LEGACY FUNCTION - return ProductPage.objects.filter(product_category=self, draft=False).count() - def __str__(self): return self.name @@ -805,7 +821,8 @@ def get_or_create_votes(self): def get_context(self, request, *args, **kwargs): context = super().get_context(request, *args, **kwargs) context['product'] = self - context['categories'] = BuyersGuideProductCategory.objects.filter(hidden=False) + language_code = get_language_code_from_request(request) + context['categories'] = get_categories_for_locale(language_code) context['mediaUrl'] = settings.MEDIA_URL context['use_commento'] = settings.USE_COMMENTO context['pageTitle'] = f'{self.title} | ' + gettext("Privacy & security guide") + ' | Mozilla Foundation' @@ -1291,7 +1308,9 @@ class Meta: class ExcludedCategories(TranslatableMixin, Orderable): - """This allows us to select one or more blog authors from Snippets.""" + """ + This allows us to filter categories from showing up on the PNI site + """ page = ParentalKey("wagtailpages.BuyersGuidePage", related_name="excluded_categories") category = models.ForeignKey( @@ -1380,13 +1399,6 @@ def get_banner(self): SynchronizedField('search_image'), ] - def get_language_code(self, request): - """Accepts a request. Returns a language code (string) if there is one. Falls back to English.""" - default_language_code = settings.LANGUAGE_CODE - if hasattr(request, 'LANGUAGE_CODE'): - default_language_code = request.LANGUAGE_CODE - return default_language_code - @route(r'^about/$', name='how-to-use-view') def about_page(self, request): context = self.get_context(request) @@ -1460,14 +1472,28 @@ def product_view(self, request, slug): @route(r'^categories/(?P[\w\W]+)/', name='category-view') def categories_page(self, request, slug): context = self.get_context(request, bypass_products=True) - language_code = self.get_language_code(request) + language_code = get_language_code_from_request(request) + locale_id = Locale.objects.get(language_code=language_code).id slug = slugify(slug) - # If getting by slug fails, also try to get it by name. + # because we may be working with localized content, and the slug + # will always be our english slug, we need to find the english + # category first, and then find its corresponding localized version try: - category = BuyersGuideProductCategory.objects.get(slug=slug) + original_category = BuyersGuideProductCategory.objects.get(slug=slug, locale_id=DEFAULT_LOCALE_ID) except BuyersGuideProductCategory.DoesNotExist: - category = get_object_or_404(BuyersGuideProductCategory, name__iexact=slug) + original_category = get_object_or_404(BuyersGuideProductCategory, name__iexact=slug) + + if locale_id != DEFAULT_LOCALE_ID: + try: + category = BuyersGuideProductCategory.objects.get( + translation_key=original_category.translation_key, + locale_id=DEFAULT_LOCALE_ID, + ) + except BuyersGuideProductCategory.DoesNotExist: + category = original_category + else: + category = original_category authenticated = request.user.is_authenticated key = f'cat_product_dicts_{slug}_auth' if authenticated else f'cat_product_dicts_{slug}_live' @@ -1480,12 +1506,13 @@ def categories_page(self, request, slug): self.cutoff_date, authenticated, key, - ProductPage.objects.filter(product_categories__category__in=[category]) + ProductPage.objects.filter(product_categories__category__in=[original_category]) .exclude(product_categories__category__id__in=exclude_cat_ids), language_code=language_code ) - context['category'] = category.slug + context['category'] = slug + context['current_category'] = category context['products'] = products context['pageTitle'] = f'{category} | ' + gettext("Privacy & security guide") + ' | Mozilla Foundation' context['template_cache_key_fragment'] = f'{category.slug}_{request.LANGUAGE_CODE}' @@ -1525,7 +1552,7 @@ def get_sitemap_urls(self, request): def get_context(self, request, *args, **kwargs): context = super().get_context(request, *args, **kwargs) - language_code = self.get_language_code(request) + language_code = get_language_code_from_request(request) authenticated = request.user.is_authenticated key = 'home_product_dicts_authed' if authenticated else 'home_product_dicts_live' @@ -1542,11 +1569,7 @@ def get_context(self, request, *args, **kwargs): language_code=language_code ) - categories = BuyersGuideProductCategory.objects.filter( - hidden=False, - locale=Locale.objects.get(language_code=language_code) - ) - context['categories'] = categories + context['categories'] = get_categories_for_locale(language_code) context['products'] = products context['web_monetization_pointer'] = settings.WEB_MONETIZATION_POINTER pni_home_page = BuyersGuidePage.objects.first() diff --git a/network-api/networkapi/wagtailpages/templates/buyersguide/bg_base.html b/network-api/networkapi/wagtailpages/templates/buyersguide/bg_base.html index 9ec63f1262a..0db9399eea9 100644 --- a/network-api/networkapi/wagtailpages/templates/buyersguide/bg_base.html +++ b/network-api/networkapi/wagtailpages/templates/buyersguide/bg_base.html @@ -1,6 +1,6 @@ {% extends "pages/base.html" %} -{% load bg_nav_tags localization i18n static wagtailroutablepage_tags wagtailmetadata_tags %} +{% load bg_nav_tags localization i18n static wagtailroutablepage_tags wagtailmetadata_tags debug_tags %} {% get_current_language as lang_code %} @@ -85,15 +85,18 @@
{% if pagetype == "product" or pagetype == "about" %} - {% trans "All" %} + {% trans "All" %} {% else %} - {% trans "All" %} + {% trans "All" %} {% endif %} + {% for cat in categories %} - {% if cat.published_product_page_count > 0 %} - {% routablepageurl home_page 'category-view' cat.slug as cat_url %} - {{ cat.name }} + {% with original=cat.original %} + {% if original.published_product_page_count > 0 %} + {% localizedroutablepageurl home_page 'category-view' lang_code original.slug as cat_url %} + {{ cat.name }} {% endif %} + {% endwith %} {% endfor %}
diff --git a/network-api/networkapi/wagtailpages/templates/buyersguide/fragments/category_nav_links.html b/network-api/networkapi/wagtailpages/templates/buyersguide/fragments/category_nav_links.html index 998d0fc80a0..a431d72735f 100644 --- a/network-api/networkapi/wagtailpages/templates/buyersguide/fragments/category_nav_links.html +++ b/network-api/networkapi/wagtailpages/templates/buyersguide/fragments/category_nav_links.html @@ -5,13 +5,15 @@ {% endcomment %} {% for cat in sorted_categories %} + {% with original=cat.original %} {% if cat.hidden == False and cat.published_product_page_count > 0 %} - {% url 'category-view' cat.slug as cat_url %} + {% localizedroutablepageurl home_page 'category-view' lang_code original.slug as cat_url %} {{ cat.name }} {% endif %} + {% endwith %} {% endfor %} diff --git a/network-api/networkapi/wagtailpages/templates/buyersguide/product_page.html b/network-api/networkapi/wagtailpages/templates/buyersguide/product_page.html index 1c4aa6a196d..5f300e30cac 100644 --- a/network-api/networkapi/wagtailpages/templates/buyersguide/product_page.html +++ b/network-api/networkapi/wagtailpages/templates/buyersguide/product_page.html @@ -1,6 +1,8 @@ {% extends "buyersguide/bg_base.html" %} -{% load bg_selector_tags env l10n i18n static wagtailimages_tags %} +{% load bg_selector_tags env l10n i18n localization static wagtailimages_tags %} + +{% get_current_language as lang_code %} {% block head_extra %} @@ -297,7 +299,7 @@

{% trans "Related products" %}

{% for related_product_page in product.related_product_pages.all %} {% with related_product=related_product_page.related_product.localized %}