from datetime import date
from django.core.validators import RegexValidator
from django.db import models
from django.http import Http404
from wagtail.admin.panels import FieldPanel
from wagtail.fields import RichTextField, StreamField
from wagtail.models import Page
from wagtail.snippets.blocks import SnippetChooserBlock
from ppa.pages.models import BodyContentBlock, PagePreviewDescriptionMixin, Person
[docs]
class EditorialIndexPage(Page):
"""Editorial index page; list recent editorial articles."""
intro = RichTextField(blank=True)
content_panels = Page.content_panels + [FieldPanel("intro", classname="full")]
# can only be created under home page; can only have
# editorial pages as subpages
parent_page_types = ["pages.HomePage"]
subpage_types = ["editorial.EditorialPage"]
[docs]
def get_context(self, request):
"""Add published editorial posts to template context, most recent first"""
context = super().get_context(request)
# Add extra variables and return the updated context
context["posts"] = (
EditorialPage.objects.child_of(self).live().order_by("-first_published_at")
)
return context
[docs]
def route(self, request, path_components):
"""Customize editorial page routing to serve editorial pages
by year/month/slug."""
# NOTE: might be able to use RoutablePageMixin for this,
# but could not get that to work
if path_components:
# if not enough path components are specified, raise a 404
if len(path_components) < 3:
raise Http404
# (could eventually handle year/month to display posts by
# date, but not yet implemented)
# currently only handle year/month/post-slug/
if len(path_components) >= 3:
# request is for a child of this page
# not using a regex route, so check character count
# - want a four-digit year and a two-digit month
if len(path_components[0]) != 4 or len(path_components[1]) != 2:
raise Http404
try:
year = int(path_components[0])
month = int(path_components[1])
except ValueError:
# if year or month are not numeric, then 404
raise Http404
child_slug = path_components[2]
remaining_components = path_components[3:]
# find a matching child or 404
try:
subpage = self.get_children().get(
first_published_at__year=year,
first_published_at__month=month,
slug=child_slug,
)
except Page.DoesNotExist:
raise Http404
# delegate further routing to child page
return subpage.specific.route(request, remaining_components)
else:
# handle normally (display current page)
return super().route(request, path_components)
validate_doi = RegexValidator(
regex=r"^10[.][0-9]{4,}", message="DOI in short form, starting with 10."
)
[docs]
class EditorialPage(Page, PagePreviewDescriptionMixin):
"""Editorial page, for scholarly, educational, or other essay-like
content related to the site"""
# preliminary streamfield; we may need other options for content
# (maybe a footnotes block?)
body = StreamField(
BodyContentBlock,
use_json_field=True,
)
authors = StreamField(
[("author", SnippetChooserBlock(Person))],
blank=True,
help_text="Select or create people snippets to add as authors.",
use_json_field=True,
)
editors = StreamField(
[("editor", SnippetChooserBlock(Person))],
blank=True,
help_text="Select or create people snippets to add as editors.",
use_json_field=True,
)
doi = models.CharField(
"DOI",
blank=True,
max_length=255,
help_text="Digital Object Identifier (DOI) if registered, in short form",
validators=[validate_doi],
)
pdf = models.URLField(
"PDF URL",
blank=True,
max_length=255,
help_text="URL for a PDF of this article, if available",
)
content_panels = Page.content_panels + [
FieldPanel("description"),
FieldPanel("authors"),
FieldPanel("editors"),
FieldPanel("doi"),
FieldPanel("pdf"),
FieldPanel("body"),
]
# can only be under editorial, cannot have subpages
parent_page_types = ["editorial.EditorialIndexPage"]
subpage_types = []
[docs]
def set_url_path(self, parent):
"""
Generate the url_path field based on this page's slug, first publication date,
and the specified parent page. Adapted from default logic to include
publication date.
(Parent is passed in for previewing unsaved pages)
"""
# use current date for preview if first published is not set
post_date = self.first_published_at or date.today()
if parent:
self.url_path = "{}{}/{}/".format(
parent.url_path, post_date.strftime("%Y/%m"), self.slug
)
else:
# a page without a parent is the tree root,
# which always has a url_path of '/'
self.url_path = "/"
return self.url_path