Skip to content

Instantly share code, notes, and snippets.

View cnk's full-sized avatar

Cynthia Kiser cnk

View GitHub Profile
@cnk
cnk / documents.py
Created November 11, 2025 22:22
Documents in subfolders in s3
class AbstractPermissionedDocument(AbstractDocument):
"""
We are now using a patched version of Wagtails document permissions via collection permissions
and using WAGTAILDOCS_SERVE_METHOD = 'serve_view' so users never see the S3 url for documents.
Note, however, that they'll still see the base filename in the URLs that Wagtail
serves.
We monkey patch the serve() method to set cache control headers to private. The code
for this is in multitenant.wagtail_patches.views.misc.py
"""
@cnk
cnk / models.py
Created September 18, 2025 21:08
# Field in the page model
publication_date = models.DateTimeField(
blank=True,
null=True,
help_text="This field will be automatically filled in once this news article is published. "
"After that, you may edit it. This date is used to sort articles and is displayed on the teaser."
)
@cnk
cnk / models.py
Created November 26, 2024 17:27
Overriding RoutablePage to add caching headers
class BasePage(RobotsTxtMixin, Page):
"""
BasePage exists to provide the default functionality that we want all (or nearly all) of our Page models to have.
Currently, that includes the following:
* RobotsTxtMixin adds the ability to place the Page into our robots.txt file, to hide it from search engines.
* The serve() method in this class adds Cache-Control headers to every Page when it gets served by Wagtail.
The values for the Cache-Control headers are a work in progress, and for now are basically either "don't cache
locally or on Cloudflare" OR "please cache on Cloudflare".
@cnk
cnk / blocks.py
Created September 22, 2024 19:02
StructBlock enforcing minimum size for images
class MixedMediaBlock(blocks.StructBlock):
"""
A Block which can display an Image and/or a Video.
"""
def __init__(self, local_blocks=None, **kwargs): # noqa
super().__init__(**kwargs)
self.min_width = kwargs.get('min_width', None)
self.min_height = kwargs.get('min_height', None)
@cnk
cnk / calendar.py
Created July 10, 2024 20:58
Calendar views
class MasterCalendarPage(RoutablePageMixin, Page):
## Fields - including one that sets the default time period: day, week, month
## Helpers
def _build_date_filtered_queryset(self, site, start_date, end_date):
queryset = self.base_queryset(site)
if start_date:
# If the user selected a start date, exclude all events that ended before then.
queryset = queryset.exclude(end_date__lte=start_date)
class RedirectPage(Page):
destination = models.CharField(
'Destination URL',
max_length=512,
help_text="If you want to redirect to an arbitrary URL, input it here. If redirecting off-site, the URL must "
"start with https://. If you want to redirect to a page on your site, use the Page field, instead.",
blank=True,
)
page = models.ForeignKey(
'wagtailcore.Page', # noqa
@cnk
cnk / wagtail_hooks.py
Created March 27, 2024 16:05
Registering custom menu item groups where the "is shown" needs to be evaluated for each request
class CalendarViewSetGroup(SnippetViewSetGroup):
"""
This class defines the Calendar menu, which is only displayed on www and on default sites from other servers.
"""
items = [EventSeries2ViewSet, EventSponsor2ViewSet, EventTagViewSet, EventSeason2ViewSet, AcademicTermViewSet]
menu_icon = 'calendar-alt'
menu_label = 'Calendar'
menu_name = 'calendar'
# This puts the Calendar menu just below News.
menu_order = 110
@cnk
cnk / news.py
Created October 19, 2023 00:32
class NewsPage(Page):
# Other fields
writer = models.CharField(max_length=255, blank=True, default=get_current_user_full_name)
content_panels = Page.content_panels + [
FieldPanel('writer'),
]
# ===================
# Utility Functions
@cnk
cnk / patches.py
Created October 10, 2023 23:23
Overriding the ETAG decorator for wagtail document's serve method
################################################################################################################
# Replace the wagtaildocs serve() view to change the cache-control header that it returns.
# This prevents any cache besides the user's own browser from storing any potentially confidential document.
################################################################################################################
multitenant_document_serve = etag(document_etag)(cache_control(max_age=3600, private=True)(serve.__wrapped__))
patched_wagtail_urlpatterns = [
# This overrides the wagtaildocs_serve view.
re_path(r'^documents/(\d+)/(.*)$', multitenant_document_serve),
from wagtail.contrib.routable_page.models import RoutablePageMixin
from wagtail.models import Page, PageViewRestriction
from robots_txt.models import RobotsTxtMixin
from ..utils import URLMixin
# Typical cache durations, defined in seconds.
DEFAULT_PAGE_CACHE_TIME = 60 * 5 # 5 minutes
TWENTY_FOUR_HOURS = 60 * 60 * 24