renamed category to topic, updated all references in code. (#8812)

* renamed blogpagecategory to blogpagetopic, updated all references and routes to match.

* renamed migrations

* squashed migrations

* updated migrations

* updated bpc to blog_page_topic to be more descriptive

* updated migrations

* updated cards to show topic

* updated more templates to show categories

* updated classname from category to topic

* updated migrations

* feedback from PR
This commit is contained in:
Daniel Miranda 2022-06-01 12:13:25 -07:00 коммит произвёл GitHub
Родитель 6dd8563880
Коммит 7f95567329
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
17 изменённых файлов: 239 добавлений и 141 удалений

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -15,7 +15,7 @@ from factory import (
from networkapi.wagtailpages.models import (
BlogAuthors,
BlogPage,
BlogPageCategory,
BlogPageTopic,
BlogIndexPage,
Profile,
)
@ -44,9 +44,9 @@ blog_body_streamfield_fields = [
]
def add_category(post):
categories = BlogPageCategory.objects.all()
post.category.add(choice(categories))
def add_topic(post):
topic_choices = BlogPageTopic.objects.all()
post.topics.add(choice(topic_choices))
post.save()
@ -109,7 +109,7 @@ def generate(seed):
post = BlogPageFactory.create(parent=blog_namespace, title=title)
add_tags(post)
add_category(post)
add_topic(post)
add_authors(post)
for i in range(6):
@ -122,7 +122,7 @@ def generate(seed):
post = BlogPageFactory.create(parent=blog_namespace, title=title)
add_tags(post)
add_category(post)
add_topic(post)
add_authors(post)
for post in BlogPage.objects.all():

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -48,8 +48,8 @@ from .pagemodels.blog.blog import (
BlogPage,
)
from .pagemodels.blog.blog_category import (
BlogPageCategory
from .pagemodels.blog.blog_topic import (
BlogPageTopic
)
from .pagemodels.blog.blog_index import (
@ -121,7 +121,7 @@ __all__ = [
BlogAuthors,
BlogIndexPage,
BlogPage,
BlogPageCategory,
BlogPageTopic,
BuyersGuidePage,
CampaignIndexPage,
CampaignPage,

Просмотреть файл

@ -36,7 +36,7 @@ from ...utils import (
)
from networkapi.wagtailpages.models import Profile
from .blog_category import BlogPageCategory
from .blog_topic import BlogPageTopic
from .blog_index import BlogIndexPage
base_fields = [
@ -118,11 +118,11 @@ class BlogPage(FoundationMetadataPageMixin, Page):
block_counts={'typeform': {'max_num': 1}},
)
category = ParentalManyToManyField(
BlogPageCategory,
help_text='Which blog categories is this blog page associated with?',
topics = ParentalManyToManyField(
BlogPageTopic,
help_text='Which blog topics is this blog page associated with?',
blank=True,
verbose_name='Categories',
verbose_name='Topics',
)
tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
@ -164,7 +164,7 @@ class BlogPage(FoundationMetadataPageMixin, Page):
],
heading='Author(s)'
),
FieldPanel('category'),
FieldPanel('topics'),
MultiFieldPanel(
[
FieldPanel("hero_video"),

Просмотреть файл

@ -18,7 +18,7 @@ from networkapi.wagtailpages.utils import (
from sentry_sdk import capture_exception, push_scope
from ..index import IndexPage
from .blog_category import BlogPageCategory
from .blog_topic import BlogPageTopic
class FeaturedBlogPages(WagtailOrderable, models.Model):
@ -47,7 +47,7 @@ class FeaturedBlogPages(WagtailOrderable, models.Model):
class BlogIndexPage(IndexPage):
"""
The blog index is specifically for blog pages,
with additional logic to explore categories.
with additional logic to explore topics.
"""
subpage_types = [
@ -88,43 +88,44 @@ class BlogIndexPage(IndexPage):
def filter_entries(self, entries, context):
entries = super().filter_entries(entries, context)
if context['filtered'] == 'category':
entries = self.filter_entries_for_category(entries, context)
if context['filtered'] == 'topic':
entries = self.filter_entries_for_topic(entries, context)
context['total_entries'] = len(entries)
return entries
def filter_entries_for_category(self, entries, context):
category = self.filtered.get('category')
def filter_entries_for_topic(self, entries, context):
# The following code first updates page share metadata when filtered by category.
topic = self.filtered.get('topic')
# The following code first updates page share metadata when filtered by topic.
# First, updating metadata that is not localized
#
# make sure we bypass "x results for Y"
context['no_filter_ui'] = True
# and that we don't show the primary tag/category
# and that we don't show the primary tag/topic
context['hide_classifiers'] = True
# store the base category name
context['terms'] = [category.name, ]
# store the base topic name
context['terms'] = [topic.name, ]
# then explicitly set all the metadata that can be localized, making
# sure to use the localized category for those fields:
# sure to use the localized topic for those fields:
locale = get_locale_from_request(context['request'])
try:
localized_category = category.get_translation(locale)
localized_topic = topic.get_translation(locale)
except ObjectDoesNotExist:
localized_category = category
localized_topic = topic
context['index_intro'] = localized_category.intro
context['index_title'] = titlecase(f'{localized_category.name} {self.title}')
context['index_intro'] = localized_topic.intro
context['index_title'] = titlecase(f'{localized_topic.name} {self.title}')
if localized_category.title:
context['index_title'] = localized_category.title
if localized_topic.title:
context['index_title'] = localized_topic.title
# update seo fields
self.set_seo_fields_from_category(localized_category)
self.set_seo_fields_from_topic(localized_topic)
# This code is not efficient, but its purpose is to get us logs
# that we can use to figure out what's going wrong more than
@ -133,20 +134,20 @@ class BlogIndexPage(IndexPage):
# See https://github.com/mozilla/foundation.mozilla.org/issues/6255
#
in_category = []
in_topics = []
try:
for entry in entries.specific():
if hasattr(entry, 'category'):
entry_categories = entry.category.all()
if hasattr(entry, 'topics'):
entry_topics = entry.topics.all()
try:
if category in entry_categories:
in_category.append(entry)
if topic in entry_topics:
in_topics.append(entry)
except Exception as e:
if settings.SENTRY_ENVIRONMENT is not None:
push_scope().set_extra(
'reason',
f'entry_categories has an iteration problem; {str(entry_categories)}'
f'entry_topics has an iteration problem; {str(entry_topics)}'
)
capture_exception(e)
@ -155,92 +156,82 @@ class BlogIndexPage(IndexPage):
push_scope().set_extra('reason', 'entries.specific threw')
capture_exception(e)
entries = in_category
# Original code is as follows:
#
# entries = [
# entry
# for
# entry in entries.specific()
# if
# hasattr(entry, 'category')
# and
# category in entry.category.all()
# ]
entries = in_topics
return entries
def set_seo_fields_from_category(self, category):
if category.title:
setattr(self, 'seo_title', category.title)
elif category.name:
setattr(self, 'seo_title', category.name)
def set_seo_fields_from_topic(self, topic):
if topic.title:
setattr(self, 'seo_title', topic.title)
elif topic.name:
setattr(self, 'seo_title', topic.name)
# If description not set, default to category's "intro" text.
# If description not set, default to topic's "intro" text.
# If "intro" is not set, use the foundation's default meta description.
if category.share_description:
setattr(self, 'search_description', category.share_description)
elif category.intro:
setattr(self, 'search_description', category.intro)
if topic.share_description:
setattr(self, 'search_description', topic.share_description)
elif topic.intro:
setattr(self, 'search_description', topic.intro)
# If the category has a search image set, update page metadata.
if category.share_image:
setattr(self, 'search_image_id', category.share_image_id)
# If the topic has a search image set, update page metadata.
if topic.share_image:
setattr(self, 'search_image_id', topic.share_image_id)
# helper function to resolve category slugs to actual objects
def get_category_object_for_slug(self, category_slug):
# helper function to resolve topic slugs to actual objects
def get_topic_object_for_slug(self, topic_slug):
(DEFAULT_LOCALE, DEFAULT_LOCALE_ID) = get_default_locale()
english_categories = BlogPageCategory.objects.filter(
english_topics = BlogPageTopic.objects.filter(
locale_id=DEFAULT_LOCALE_ID
)
# We can't use .filter for @property fields,
# so we have to run through all categories =(
for bpc in english_categories:
if bpc.slug == category_slug:
category_object = bpc
# so we have to run through all topics =(
for blog_page_topic in english_topics:
if blog_page_topic.slug == topic_slug:
topic_object = blog_page_topic
break
else:
category_object = None
topic_object = None
return category_object
return topic_object
# helper function for /category/... subroutes
def extract_category_information(self, category_slug):
category_object = self.get_category_object_for_slug(category_slug)
# helper function for /topic/... subroutes
def extract_topic_information(self, topic_slug):
if category_object is None:
topic_object = self.get_topic_object_for_slug(topic_slug)
if topic_object is None:
raise ObjectDoesNotExist
self.filtered = {
'type': 'category',
'category': category_object
'type': 'topic',
'topic': topic_object
}
@route(r'^category/(?P<category>.+)/entries/')
def generate_category_entries_set_html(self, request, category, *args, **kwargs):
@route(r'^topic/(?P<topic>.+)/entries/')
def generate_topic_entries_set_html(self, request, topic, *args, **kwargs):
"""
JSON endpoint for getting a set of (pre-rendered) category entries
JSON endpoint for getting a set of (pre-rendered) topic entries
"""
try:
self.extract_category_information(category)
self.extract_topic_information(topic)
except ObjectDoesNotExist:
return redirect(self.full_url)
return self.generate_entries_set_html(request, *args, **kwargs)
@route(r'^category/(?P<category>.+)/')
def entries_by_category(self, request, category, *args, **kwargs):
@route(r'^topic/(?P<topic>.+)/')
def entries_by_topic(self, request, topic, *args, **kwargs):
"""
If this page was called with `/category/...` as suffix, extract
the category to filter prior to rendering this page. Only one
category can be specified (unlike tags)
If this page was called with `/topic/...` as suffix, extract
the topic to filter prior to rendering this page. Only one
topic can be specified (unlike tags)
"""
try:
self.extract_category_information(category)
self.extract_topic_information(topic)
except ObjectDoesNotExist:
return redirect(self.full_url)

Просмотреть файл

@ -10,14 +10,14 @@ from networkapi.wagtailpages.pagemodels.customblocks.base_rich_text_options impo
@register_snippet
class BlogPageCategory(TranslatableMixin, models.Model):
class BlogPageTopic(TranslatableMixin, models.Model):
name = models.CharField(
max_length=50
)
title = models.TextField(
blank=True,
help_text='Optional title that will apear on the page and when category page is shared. '
help_text='Optional title that will apear on the page and when topic page is shared. '
'If not set, will default to "name" text.'
)
@ -27,7 +27,7 @@ class BlogPageCategory(TranslatableMixin, models.Model):
)
share_description = models.TextField(
blank=True,
help_text='Optional description that will apear when category page is shared. '
help_text='Optional description that will apear when topic page is shared. '
'If not set, will default to "intro" text.'
)
share_image = models.ForeignKey(
@ -36,7 +36,7 @@ class BlogPageCategory(TranslatableMixin, models.Model):
blank=True,
on_delete=models.SET_NULL,
verbose_name='Share Image',
help_text='Optional image that will apear when category page is shared.',
help_text='Optional image that will apear when topic page is shared.',
)
panels = [
@ -47,7 +47,7 @@ class BlogPageCategory(TranslatableMixin, models.Model):
ImageChooserPanel("share_image"),
]
def get_categories():
def get_topics():
"""
WARNING: this function is referenced by two migrations:
@ -79,5 +79,5 @@ class BlogPageCategory(TranslatableMixin, models.Model):
return self.name
class Meta(TranslatableMixin.Meta):
verbose_name = "Blog Page Category"
verbose_name_plural = "Blog Page Categories"
verbose_name = "Blog Page Topic"
verbose_name_plural = "Blog Page Topics"

Просмотреть файл

@ -4,7 +4,7 @@ from django.template.defaultfilters import slugify
from wagtail.core import blocks
from networkapi.wagtailpages.utils import get_locale_from_request
from ..blog.blog_category import BlogPageCategory
from ..blog.blog_topic import BlogPageTopic
class RecentBlogEntries(blocks.StructBlock):
@ -18,11 +18,11 @@ class RecentBlogEntries(blocks.StructBlock):
help_text='Test this filter at foundation.mozilla.org/blog/tags/',
)
category_filter = blocks.ChoiceBlock(
label='Filter by Category',
topic_filter = blocks.ChoiceBlock(
label='Filter by Topic',
required=False,
choices=BlogPageCategory.get_categories(),
help_text='Test this filter at foundation.mozilla.org/blog/category/',
choices=BlogPageTopic.get_topics(),
help_text='Test this filter at foundation.mozilla.org/blog/topic/',
)
top_divider = blocks.BooleanBlock(
@ -35,7 +35,7 @@ class RecentBlogEntries(blocks.StructBlock):
help_text='Optional divider below content block.',
)
# TODO: add in validation so that if there are no tags or category
# TODO: add in validation so that if there are no tags or topic
# filled in we don't allow the page to be saved, with a wagtail
# error indication what's wrong.
@ -47,7 +47,7 @@ class RecentBlogEntries(blocks.StructBlock):
blog_page = BlogIndexPage.objects.get(title__iexact="blog", locale=locale)
tag = value.get("tag_filter", False)
category = value.get("category_filter", False)
topic = value.get("topic_filter", False)
# default filter and query
type = "tags"
@ -55,26 +55,26 @@ class RecentBlogEntries(blocks.StructBlock):
entries = []
# If only tag_filter is chosen we want to load entries by tag and update the url accordingly
if tag and not category:
if tag and not topic:
tag = slugify(tag)
query = tag
blog_page.extract_tag_information(tag)
entries = blog_page.get_entries(context)
'''
If category_filter is chosen at all, we want to load entries by category and
If topic_filter is chosen at all, we want to load entries by topic and
update the url accordingly. Once we add validation, we'll be able to remove
the prioritization of category and instead notify the user that they must/can
the prioritization of topic and instead notify the user that they must/can
only choose one filter option.
'''
if category and category != "All":
type = "category"
query = slugify(category)
if topic and topic != "All":
type = "topic"
query = slugify(topic)
try:
# verify this category exists, and set up a filter for it
category_object = BlogPageCategory.objects.get(name=category)
blog_page.extract_category_information(category_object.slug)
except BlogPageCategory.DoesNotExist:
# verify this topic exists, and set up a filter for it
topic_object = BlogPageTopic.objects.get(name=topic)
blog_page.extract_topic_information(topic_object.slug)
except BlogPageTopic.DoesNotExist:
# do nothing
pass

Просмотреть файл

@ -223,7 +223,7 @@ class IndexPage(FoundationMetadataPageMixin, RoutablePageMixin, Page):
hide_classifiers = False
if hasattr(self, 'filtered'):
if self.filtered.get('type') == 'category':
if self.filtered.get('type') == 'topic':
hide_classifiers = True
html = loader.render_to_string(

Просмотреть файл

@ -8,9 +8,9 @@
{% if page.last_published_at %}
<meta property="article:modified" content="{{ page.last_published_at|date:"DATE_FORMAT" }}"/>
{% endif %}
{% with category=page.specific.category.first %}
{% if category %}
<meta property="article:section" content="{{ category }}" />
{% with topic=page.specific.topics.first %}
{% if topic %}
<meta property="article:section" content="{{ topic }}" />
{% endif %}
{% endwith %}
{% endblock %}

Просмотреть файл

@ -15,10 +15,10 @@
{% if show_full_info %}
| {% if blog_page.first_published_at %}{{ blog_page.first_published_at|date:"DATE_FORMAT" }}
{% else %}not published yet{% endif %}
{% with category=page.specific.category.first %}
{% if category and blog_index %}
{% localized_version category as localized_category %}
| <a class="category tw-text-inherit" href="{{blog_index.localized.url}}category/{{category.slug}}">{{ localized_category }}</a>
{% with topic=page.specific.topics.first %}
{% if topic and blog_index %}
{% localized_version topic as localized_topic %}
| <a class="topic tw-text-inherit" href="{{blog_index.localized.url}}topic/{{topic.slug}}">{{ localized_topic }}</a>
{% endif %}
{% endwith %}
{% endif %}

Просмотреть файл

@ -13,12 +13,12 @@ each blog page's dominant tag)
{% block tags %}
{% if hide_classifiers != True %}
{% with category=page.specific.category.first %}
{% if category %}
{% localized_version category as localized_category %}
{% with topic=page.specific.topics.first %}
{% if topic %}
{% localized_version topic as localized_topic %}
{% get_root_or_page as parent_page %}
{# If we have a "root" context variable, we know this card is generated on an index page (or index page subroute) #}
<a class="tw-h6-heading d-block mt-3 mb-0" href="{% localizedroutablepageurl parent_page "entries_by_category" category.slug %}">{{ localized_category }}</a>
<a class="tw-h6-heading d-block mt-3 mb-0" href="{% localizedroutablepageurl parent_page "entries_by_topic" topic.slug %}">{{ localized_topic }}</a>
{% endif %}
{% endwith %}
{% endif %}

Просмотреть файл

@ -14,9 +14,9 @@
<div class="tw-flex tw-flex-col tw-flex-1 tw-border-b-4 tw-border-gray-05 -tw-mt-5 tw-bg-white tw-relative tw-mx-2 medium:tw-mx-5 tw-p-4">
<p class="tw-h6-heading tw-mb-2">
{% for category in blog.localized.category.all %}
{% localized_version category as localized_category %}
<a href="/blog/category/{{category.slug}}">{{ localized_category }}</a>{% if not forloop.last %}, {% endif %}
{% for topic in blog.localized.topics.all %}
{% localized_version topic as localized_topic %}
<a href="/blog/topic/{{topic.slug}}">{{ localized_topic }}</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
</p>
<h5 class="tw-mb-2 tw-h3-heading">

Просмотреть файл

@ -32,10 +32,10 @@
<img src="{% image_url first.get_meta_image "fill-1000x500" %}" alt="{{ first.title }}">
</div>
<div class="feature-content align-self-center">
{% if first.category.count %}
{% with category=first.category.first %}
{% localized_version category as localized_category %}
<a class="tw-h6-heading d-block mb-2" href="/{{ lang_code }}/blog/category/{{ category.slug }}">{{ localized_category }}</a>
{% if first.topics.count %}
{% with topic=first.topics.first %}
{% localized_version topic as localized_topic %}
<a class="tw-h6-heading d-block mb-2" href="/{{ lang_code }}/blog/topic/{{ topic.slug }}">{{ localized_topic }}</a>
{% endwith %}
{% else %}
<div class="tw-h6-heading d-md-block d-none mb-2">&nbsp;</div>
@ -64,10 +64,10 @@
<div class="bg-white">
<img src="{% image_url localized.get_meta_image "fill-700x394" %}" alt="" class="embed-responsive-item">
<div class="p-4">
{% if localized.category.count %}
{% with category=localized.category.first %}
{% localized_version category as localized_category %}
<a class="tw-h6-heading d-block mb-1" href="/{{ lang_code }}/blog/category/{{ category.slug }}">{{ localized_category }}</a>
{% if localized.topics.count %}
{% with topic=localized.topics.first %}
{% localized_version topic as localized_topic %}
<a class="tw-h6-heading d-block mb-1" href="/{{ lang_code }}/blog/topic/{{ topic.slug }}">{{ localized_topic }}</a>
{% endwith %}
{% else %}
<div class="tw-h6-heading d-md-block d-none mb-1">&nbsp;</div>

Просмотреть файл

@ -16,7 +16,7 @@
}
}
a.category {
a.topic {
@include hover-focus-active {
color: $dark-blue;
}

Просмотреть файл

@ -4,8 +4,8 @@ http://localhost:8000/en/who-we-are
http://localhost:8000/en/blog
http://localhost:8000/en/blog/tags/iot
http://localhost:8000/en/blog/tags/randomnonsensetagthatdoesntexist
http://localhost:8000/en/blog/category/mozilla-festival
http://localhost:8000/en/blog/category/randomnonsensecateogrythatdoesntexist
http://localhost:8000/en/blog/topic/mozilla-festival
http://localhost:8000/en/blog/topic/randomnonsensecateogrythatdoesntexist
http://localhost:8000/en/blog/initial-test-blog-post-with-fixed-title
http://localhost:8000/en/opportunity/single-page-opportunity/
http://localhost:8000/en/opportunity/multi-page-opportunity/

Просмотреть файл

@ -7,7 +7,7 @@ module.exports = {
"Blog index (filtered on tag)": "/blog/tags/iot",
"Blog index (non-existent tag)":
"/blog/tags/randomnonsensetagthatdoesntexist",
"Blog index (filtered on category)": "/blog/category/mozilla-festival",
"Blog index (filtered on topic)": "/blog/topic/mozilla-festival",
"Fixed blog post": "/blog/initial-test-blog-post-with-fixed-title",
"Campaign index": "/campaigns",
"Single-page campaign": "/campaigns/single-page",