Unify RSS feeds

One per section is a little much. Just use a single feed and be done with. Might help discoverability a little.
This commit is contained in:
Jake Howard 2023-12-22 17:35:18 +00:00
parent aa5107c89e
commit e5ae66567a
Signed by: jake
GPG key ID: 57AFB45680EDD477
8 changed files with 27 additions and 83 deletions

View file

@ -1,4 +1,5 @@
from django.test import TestCase from django.test import TestCase
from django.urls import reverse
from website.home.models import HomePage from website.home.models import HomePage
@ -73,17 +74,13 @@ class BlogPostListPageTestCase(TestCase):
response = self.client.get(self.page.url) response = self.client.get(self.page.url)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context["listing_pages"]), 2) self.assertEqual(len(response.context["listing_pages"]), 2)
self.assertContains(response, self.page.reverse_subpage("feed"))
def test_queries(self) -> None: def test_queries(self) -> None:
with self.assertNumQueries(44): with self.assertNumQueries(44):
self.client.get(self.page.url) self.client.get(self.page.url)
def test_feed_accessible(self) -> None: def test_feed_accessible(self) -> None:
with self.assertNumQueries(14): response = self.client.get(self.page.url + self.page.reverse_subpage("feed"))
response = self.client.get( self.assertRedirects(
self.page.url + self.page.reverse_subpage("feed") response, reverse("feed"), status_code=301, fetch_redirect_response=True
) )
self.assertEqual(response.status_code, 200)
self.assertEqual(response["Content-Type"], "application/xml")
self.assertContains(response, "xml-stylesheet")

View file

@ -1,19 +1,17 @@
from datetime import timedelta from datetime import timedelta
from typing import Any, Optional, Type from typing import Any, Optional
from urllib.parse import urlencode from urllib.parse import urlencode
from django.contrib.humanize.templatetags.humanize import NaturalTimeFormatter from django.contrib.humanize.templatetags.humanize import NaturalTimeFormatter
from django.contrib.syndication.views import Feed
from django.core.paginator import EmptyPage, Paginator from django.core.paginator import EmptyPage, Paginator
from django.core.paginator import Page as PaginatorPage from django.core.paginator import Page as PaginatorPage
from django.db import models from django.db import models
from django.http.request import HttpRequest from django.http.request import HttpRequest
from django.http.response import Http404, HttpResponse, HttpResponseBadRequest from django.http.response import Http404, HttpResponse, HttpResponseBadRequest
from django.shortcuts import redirect
from django.utils import timezone from django.utils import timezone
from django.utils.decorators import method_decorator
from django.utils.functional import cached_property, classproperty from django.utils.functional import cached_property, classproperty
from django.utils.text import slugify from django.utils.text import slugify
from django.views.decorators.cache import cache_page
from wagtail.admin.panels import FieldPanel, MultiFieldPanel from wagtail.admin.panels import FieldPanel, MultiFieldPanel
from wagtail.contrib.routable_page.models import RoutablePageMixin, route from wagtail.contrib.routable_page.models import RoutablePageMixin, route
from wagtail.contrib.settings.models import BaseGenericSetting, register_setting from wagtail.contrib.settings.models import BaseGenericSetting, register_setting
@ -257,12 +255,6 @@ class BaseListingPage(RoutablePageMixin, BaseContentPage):
def show_reading_time(self) -> bool: def show_reading_time(self) -> bool:
return False return False
@property
def feed_class(self) -> Type[Feed]:
from .views import ContentPageFeed
return ContentPageFeed
@route(r"^$") @route(r"^$")
def index_route(self, request: HttpRequest) -> HttpResponse: def index_route(self, request: HttpRequest) -> HttpResponse:
self.serializer = PaginationSerializer(data=request.GET) self.serializer = PaginationSerializer(data=request.GET)
@ -283,13 +275,8 @@ class BaseListingPage(RoutablePageMixin, BaseContentPage):
return url + "?" + urlencode(query_data) return url + "?" + urlencode(query_data)
@route(r"^feed/$") @route(r"^feed/$")
@method_decorator(cache_page(60 * 30))
def feed(self, request: HttpRequest) -> HttpResponse: def feed(self, request: HttpRequest) -> HttpResponse:
return self.feed_class( return redirect("feed", permanent=True)
self.get_listing_pages(),
self.get_full_url(request),
self.html_title_tag,
)(request)
class ListingPage(BaseListingPage): class ListingPage(BaseListingPage):

View file

@ -1,15 +1,8 @@
{% extends "common/content_page.html" %} {% extends "common/content_page.html" %}
{% load wagtailroutablepage_tags wagtailadmin_tags %} {% load wagtailadmin_tags %}
{% block extra_head %}
{{ block.super }}
<link rel="alternate" type="application/rss+xml" href="{% routablepageurl page 'feed' %}" />
{% endblock %}
{% block hero_buttons %} {% block hero_buttons %}
<a class="button is-radiusless" href="{% routablepageurl page 'feed' %}" title="View feed"><i class="fas fa-rss" aria-hidden="true"></i></a>
{% if listing_pages.has_previous %} {% if listing_pages.has_previous %}
<a class="button is-radiusless" href="{% querystring page=listing_pages.previous_page_number %}" title="Previous page"><i class="fas fa-arrow-left" aria-hidden="true"></i></a> <a class="button is-radiusless" href="{% querystring page=listing_pages.previous_page_number %}" title="Previous page"><i class="fas fa-arrow-left" aria-hidden="true"></i></a>
{% endif %} {% endif %}

View file

@ -1,5 +1,6 @@
from django.template.loader import get_template from django.template.loader import get_template
from django.test import SimpleTestCase, TestCase from django.test import SimpleTestCase, TestCase
from django.urls import reverse
from website.common.factories import ContentPageFactory, ListingPageFactory from website.common.factories import ContentPageFactory, ListingPageFactory
from website.common.models import BaseListingPage, BasePage from website.common.models import BaseListingPage, BasePage
@ -58,14 +59,11 @@ class ListingPageTestCase(TestCase):
self.assertEqual(len(response.context["listing_pages"]), 2) self.assertEqual(len(response.context["listing_pages"]), 2)
self.assertContains(response, self.page.reverse_subpage("feed")) self.assertContains(response, self.page.reverse_subpage("feed"))
def test_feed_accessible(self) -> None: def test_feed_redirects(self) -> None:
with self.assertNumQueries(13): response = self.client.get(self.page.url + self.page.reverse_subpage("feed"))
response = self.client.get( self.assertRedirects(
self.page.url + self.page.reverse_subpage("feed") response, reverse("feed"), status_code=301, fetch_redirect_response=True
) )
self.assertEqual(response.status_code, 200)
self.assertEqual(response["Content-Type"], "application/xml")
self.assertContains(response, "xml-stylesheet")
def test_meta_url(self) -> None: def test_meta_url(self) -> None:
response = self.client.get(self.page.url) response = self.client.get(self.page.url)

View file

@ -93,7 +93,7 @@ class AllPagesFeed(Feed):
return {**super().feed_extra_kwargs(obj), "request": self.request} return {**super().feed_extra_kwargs(obj), "request": self.request}
def title(self) -> str: def title(self) -> str:
return f"All Pages Feed :: {get_site_title()}" return f"Feed :: {get_site_title()}"
def items(self) -> PageQuerySet: def items(self) -> PageQuerySet:
return ( return (
@ -149,20 +149,6 @@ class AllPagesFeed(Feed):
item_enclosure_length = 0 item_enclosure_length = 0
class ContentPageFeed(AllPagesFeed):
def __init__(self, posts: PageQuerySet, link: str, title: str):
self.posts = posts
self.link = link
self._title = title
super().__init__()
def title(self) -> str:
return self._title
def items(self) -> PageQuerySet:
return self.posts
@method_decorator(cache_control(max_age=60 * 60), name="dispatch") @method_decorator(cache_control(max_age=60 * 60), name="dispatch")
class FaviconView(RedirectView): class FaviconView(RedirectView):
def get_redirect_url(self) -> str: def get_redirect_url(self) -> str:

View file

@ -1,24 +1,16 @@
from django.test import TestCase from django.test import TestCase
from django.urls import reverse from django.urls import reverse
from website.blog.factories import BlogPostListPageFactory
from website.home.models import HomePage
class PostsFeedViewTestCase(TestCase):
@classmethod
def setUpTestData(cls) -> None:
cls.home_page = HomePage.objects.get()
cls.page = BlogPostListPageFactory(parent=cls.home_page)
def test_redirects(self) -> None:
response = self.client.get("/posts/index.xml")
self.assertRedirects(
response, self.page.url + self.page.reverse_subpage("feed"), status_code=301
)
class AllPagesFeedViewTestCase(TestCase): class AllPagesFeedViewTestCase(TestCase):
def test_redirects(self) -> None: def test_redirects(self) -> None:
response = self.client.get("/index.xml") response = self.client.get("/index.xml")
self.assertRedirects(response, reverse("feed"), status_code=301) self.assertRedirects(
response, reverse("feed"), status_code=301, fetch_redirect_response=True
)
def test_redirects_posts(self) -> None:
response = self.client.get("/posts/index.xml")
self.assertRedirects(
response, reverse("feed"), status_code=301, fetch_redirect_response=True
)

View file

@ -5,7 +5,7 @@ from . import views
app_name = "legacy" app_name = "legacy"
urlpatterns = [ urlpatterns = [
path("posts/index.xml", views.PostsFeedView.as_view()), path("posts/index.xml", views.AllPagesFeedView.as_view()),
path("index.xml", views.AllPagesFeedView.as_view()), path("index.xml", views.AllPagesFeedView.as_view()),
path("tags/<slug:slug>/", views.TagView.as_view()), path("tags/<slug:slug>/", views.TagView.as_view()),
path("tags/", views.TagsView.as_view()), path("tags/", views.TagsView.as_view()),

View file

@ -3,16 +3,7 @@ from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_control from django.views.decorators.cache import cache_control
from django.views.generic import RedirectView from django.views.generic import RedirectView
from website.blog.models import BlogPostListPage, BlogPostTagListPage, BlogPostTagPage from website.blog.models import BlogPostTagListPage, BlogPostTagPage
@method_decorator(cache_control(max_age=60 * 60), name="dispatch")
class PostsFeedView(RedirectView):
permanent = True
def get_redirect_url(self) -> str:
post_list = get_object_or_404(BlogPostListPage)
return post_list.url + post_list.reverse_subpage("feed")
@method_decorator(cache_control(max_age=60 * 60), name="dispatch") @method_decorator(cache_control(max_age=60 * 60), name="dispatch")