2022-06-19 19:24:29 +01:00
|
|
|
from typing import Any
|
|
|
|
|
2022-08-26 23:13:06 +01:00
|
|
|
from django.core.paginator import EmptyPage
|
|
|
|
from django.core.paginator import Page as PaginatorPage
|
|
|
|
from django.core.paginator import Paginator
|
2022-06-19 20:55:37 +01:00
|
|
|
from django.db import models
|
2022-07-10 18:42:16 +01:00
|
|
|
from django.db.models.functions import TruncMonth
|
2022-06-19 19:24:29 +01:00
|
|
|
from django.http.request import HttpRequest
|
2022-08-26 23:13:06 +01:00
|
|
|
from django.http.response import Http404, HttpResponse, HttpResponseBadRequest
|
2022-06-19 21:03:05 +01:00
|
|
|
from django.utils import timezone
|
2022-06-26 17:00:44 +01:00
|
|
|
from django.utils.functional import cached_property
|
2022-07-16 10:29:01 +01:00
|
|
|
from modelcluster.fields import ParentalManyToManyField
|
2022-06-19 20:55:37 +01:00
|
|
|
from wagtail.admin.panels import FieldPanel
|
2022-07-25 21:58:06 +01:00
|
|
|
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
|
2022-07-10 18:42:16 +01:00
|
|
|
from wagtail.query import PageQuerySet
|
2022-06-19 19:24:29 +01:00
|
|
|
|
2022-08-16 21:32:46 +01:00
|
|
|
from website.common.models import BaseContentPage
|
2022-08-26 23:13:06 +01:00
|
|
|
from website.common.serializers import PaginationSerializer
|
2022-06-26 17:00:44 +01:00
|
|
|
from website.common.utils import TocEntry
|
2022-06-19 19:24:29 +01:00
|
|
|
|
|
|
|
|
2022-08-20 14:19:07 +01:00
|
|
|
class BlogPostListPage(RoutablePageMixin, BaseContentPage):
|
2022-06-19 19:24:29 +01:00
|
|
|
max_count = 1
|
2022-07-16 15:45:57 +01:00
|
|
|
subpage_types = [
|
|
|
|
"blog.BlogPostPage",
|
|
|
|
"blog.BlogPostTagListPage",
|
2022-08-20 14:19:07 +01:00
|
|
|
"blog.BlogPostCollectionListPage",
|
|
|
|
"blog.BlogPostCollectionPage",
|
2022-07-16 15:45:57 +01:00
|
|
|
]
|
2022-06-19 19:24:29 +01:00
|
|
|
|
2022-06-26 17:00:44 +01:00
|
|
|
@cached_property
|
2022-08-20 11:22:50 +01:00
|
|
|
def show_reading_time(self) -> bool:
|
|
|
|
return False
|
2022-06-26 17:00:44 +01:00
|
|
|
|
|
|
|
@cached_property
|
|
|
|
def table_of_contents(self) -> list[TocEntry]:
|
2022-08-26 23:13:06 +01:00
|
|
|
post_months = sorted(
|
|
|
|
{
|
|
|
|
dt.strftime("%Y-%m")
|
|
|
|
for dt in self.paginator_page.object_list.annotate(
|
|
|
|
post_month=TruncMonth("date", output_field=models.DateField())
|
|
|
|
).values_list("post_month", flat=True)
|
|
|
|
}
|
|
|
|
)
|
2022-07-10 18:42:16 +01:00
|
|
|
|
|
|
|
return [TocEntry(post_month, post_month, 0, []) for post_month in post_months]
|
|
|
|
|
2022-07-16 10:29:01 +01:00
|
|
|
def get_blog_posts(self) -> PageQuerySet:
|
2022-07-28 20:15:20 +01:00
|
|
|
return BlogPostPage.objects.descendant_of(self).live()
|
2022-06-26 17:00:44 +01:00
|
|
|
|
2022-08-26 23:13:06 +01:00
|
|
|
@cached_property
|
|
|
|
def paginator_page(self) -> PaginatorPage:
|
|
|
|
pages = (
|
2022-07-16 10:29:01 +01:00
|
|
|
self.get_blog_posts()
|
2022-06-26 12:55:08 +01:00
|
|
|
.select_related("hero_image")
|
2022-07-12 22:45:50 +01:00
|
|
|
.select_related("hero_unsplash_photo")
|
2022-06-26 12:55:08 +01:00
|
|
|
.prefetch_related("tags")
|
2022-08-26 23:13:06 +01:00
|
|
|
.order_by("-date", "title")
|
2022-06-19 19:24:29 +01:00
|
|
|
)
|
2022-08-26 23:13:06 +01:00
|
|
|
paginator = Paginator(pages, per_page=1)
|
|
|
|
try:
|
|
|
|
return paginator.page(self.serializer.validated_data["page"])
|
|
|
|
except EmptyPage:
|
|
|
|
raise Http404
|
|
|
|
|
|
|
|
def get_context(self, request: HttpRequest) -> dict:
|
|
|
|
context = super().get_context(request)
|
|
|
|
context["pages"] = self.paginator_page
|
2022-06-19 19:24:29 +01:00
|
|
|
return context
|
|
|
|
|
2022-08-26 23:13:06 +01:00
|
|
|
@route(r"^$")
|
|
|
|
def index_route(self, request: HttpRequest) -> HttpResponse:
|
|
|
|
self.serializer = PaginationSerializer(data=request.GET)
|
|
|
|
if not self.serializer.is_valid():
|
|
|
|
return HttpResponseBadRequest()
|
|
|
|
return super().index_route(request)
|
|
|
|
|
2022-07-25 21:58:06 +01:00
|
|
|
@route(r"^feed/$")
|
|
|
|
def feed(self, request: HttpRequest) -> HttpResponse:
|
|
|
|
from .views import BlogPostPageFeed
|
|
|
|
|
|
|
|
return BlogPostPageFeed(
|
|
|
|
self.get_blog_posts().order_by("-date"), self.get_url(), self.title
|
|
|
|
)(request)
|
|
|
|
|
2022-06-19 19:24:29 +01:00
|
|
|
|
2022-08-16 21:32:46 +01:00
|
|
|
class BlogPostPage(BaseContentPage):
|
2022-06-19 19:24:29 +01:00
|
|
|
subpage_types: list[Any] = []
|
2022-08-20 14:19:07 +01:00
|
|
|
parent_page_types = [BlogPostListPage, "blog.BlogPostCollectionPage"]
|
2022-06-19 20:55:37 +01:00
|
|
|
|
2022-07-16 10:29:01 +01:00
|
|
|
tags = ParentalManyToManyField("blog.BlogPostTagPage", blank=True)
|
2022-06-19 21:03:05 +01:00
|
|
|
date = models.DateField(default=timezone.now)
|
2022-06-19 20:55:37 +01:00
|
|
|
|
2022-08-16 21:32:46 +01:00
|
|
|
content_panels = BaseContentPage.content_panels + [
|
|
|
|
FieldPanel("date"),
|
|
|
|
FieldPanel("tags"),
|
|
|
|
]
|
2022-07-16 10:29:01 +01:00
|
|
|
|
|
|
|
|
2022-08-16 21:32:46 +01:00
|
|
|
class BlogPostTagListPage(BaseContentPage):
|
2022-07-16 10:29:01 +01:00
|
|
|
max_count = 1
|
2022-08-20 14:19:07 +01:00
|
|
|
parent_page_types = [BlogPostListPage]
|
2022-07-16 10:29:01 +01:00
|
|
|
subpage_types = ["blog.BlogPostTagPage"]
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
def table_of_contents(self) -> list[TocEntry]:
|
|
|
|
return [TocEntry(page.title, page.slug, 0, []) for page in self.get_tags()]
|
|
|
|
|
|
|
|
def get_tags(self) -> PageQuerySet:
|
2022-08-25 22:27:28 +01:00
|
|
|
return (
|
|
|
|
self.get_children()
|
|
|
|
.specific()
|
|
|
|
.live()
|
|
|
|
.order_by("title")
|
|
|
|
.select_related("hero_image")
|
|
|
|
.select_related("hero_unsplash_photo")
|
|
|
|
)
|
2022-07-16 10:29:01 +01:00
|
|
|
|
|
|
|
def get_context(self, request: HttpRequest) -> dict:
|
|
|
|
context = super().get_context(request)
|
|
|
|
context["tags"] = self.get_children().specific().live().order_by("title")
|
|
|
|
return context
|
|
|
|
|
|
|
|
|
2022-08-16 21:32:46 +01:00
|
|
|
class BlogPostTagPage(RoutablePageMixin, BaseContentPage):
|
2022-07-16 10:29:01 +01:00
|
|
|
subpage_types: list[Any] = []
|
|
|
|
parent_page_types = [BlogPostTagListPage]
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
def table_of_contents(self) -> list[TocEntry]:
|
|
|
|
return [
|
|
|
|
TocEntry(page.title, page.slug, 0, []) for page in self.get_blog_posts()
|
|
|
|
]
|
|
|
|
|
|
|
|
def get_blog_posts(self) -> PageQuerySet:
|
2022-08-20 14:19:07 +01:00
|
|
|
blog_list_page = BlogPostListPage.objects.all().live().get()
|
2022-08-25 22:27:28 +01:00
|
|
|
return (
|
|
|
|
blog_list_page.get_blog_posts()
|
|
|
|
.filter(tags=self)
|
|
|
|
.order_by("-date")
|
|
|
|
.select_related("hero_image")
|
|
|
|
.select_related("hero_unsplash_photo")
|
|
|
|
)
|
2022-07-16 10:29:01 +01:00
|
|
|
|
|
|
|
def get_context(self, request: HttpRequest) -> dict:
|
|
|
|
context = super().get_context(request)
|
|
|
|
context["pages"] = self.get_blog_posts()
|
|
|
|
return context
|
2022-07-16 15:45:57 +01:00
|
|
|
|
2022-07-25 21:58:06 +01:00
|
|
|
@route(r"^feed/$")
|
|
|
|
def feed(self, request: HttpRequest) -> HttpResponse:
|
|
|
|
from .views import BlogPostPageFeed
|
|
|
|
|
|
|
|
return BlogPostPageFeed(
|
|
|
|
self.get_blog_posts().order_by("-date"), self.get_url(), self.title
|
|
|
|
)(request)
|
|
|
|
|
2022-07-16 15:45:57 +01:00
|
|
|
|
2022-08-20 14:19:07 +01:00
|
|
|
class BlogPostCollectionListPage(BaseContentPage):
|
2022-07-16 15:45:57 +01:00
|
|
|
subpage_types: list[Any] = []
|
2022-08-20 14:19:07 +01:00
|
|
|
parent_page_types = [BlogPostListPage]
|
2022-07-16 15:45:57 +01:00
|
|
|
max_count = 1
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
def table_of_contents(self) -> list[TocEntry]:
|
|
|
|
return [
|
|
|
|
TocEntry(page.title, page.slug, 0, []) for page in self.get_collections()
|
|
|
|
]
|
|
|
|
|
|
|
|
def get_collections(self) -> PageQuerySet:
|
2022-08-20 14:19:07 +01:00
|
|
|
blog_list_page = BlogPostListPage.objects.all().live().get()
|
|
|
|
return BlogPostCollectionPage.objects.child_of(blog_list_page).live()
|
2022-07-16 15:45:57 +01:00
|
|
|
|
|
|
|
def get_context(self, request: HttpRequest) -> dict:
|
|
|
|
context = super().get_context(request)
|
|
|
|
context["collections"] = self.get_collections()
|
|
|
|
return context
|
|
|
|
|
|
|
|
|
2022-08-20 14:19:07 +01:00
|
|
|
class BlogPostCollectionPage(BaseContentPage):
|
|
|
|
parent_page_types = [BlogPostListPage]
|
2022-07-16 15:45:57 +01:00
|
|
|
subpage_types = [BlogPostPage]
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
def table_of_contents(self) -> list[TocEntry]:
|
|
|
|
return [
|
|
|
|
TocEntry(page.title, page.slug, 0, []) for page in self.get_blog_posts()
|
|
|
|
]
|
|
|
|
|
|
|
|
def get_blog_posts(self) -> PageQuerySet:
|
2022-08-25 22:27:28 +01:00
|
|
|
return (
|
|
|
|
BlogPostPage.objects.child_of(self)
|
|
|
|
.order_by("-date")
|
|
|
|
.select_related("hero_image")
|
|
|
|
.select_related("hero_unsplash_photo")
|
|
|
|
)
|
2022-07-16 15:45:57 +01:00
|
|
|
|
|
|
|
def get_context(self, request: HttpRequest) -> dict:
|
|
|
|
context = super().get_context(request)
|
|
|
|
context["pages"] = self.get_blog_posts()
|
|
|
|
return context
|