website/website/common/views.py

171 lines
5.4 KiB
Python
Raw Normal View History

from datetime import datetime, time
2023-06-26 14:27:04 +01:00
from typing import Any, Optional
2022-08-19 09:36:03 +01:00
from django.contrib.syndication.views import Feed
from django.http.request import HttpRequest
2022-08-19 09:36:03 +01:00
from django.http.response import HttpResponse
from django.templatetags.static import static
2022-08-19 14:35:38 +01:00
from django.urls import reverse
2022-10-02 18:53:51 +01:00
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_control, cache_page
2022-08-19 09:36:03 +01:00
from django.views.defaults import ERROR_404_TEMPLATE_NAME
2023-07-23 14:44:35 +01:00
from django.views.generic import RedirectView, TemplateView
from wagtail.models import Page
from wagtail.query import PageQuerySet
2023-07-23 14:44:35 +01:00
from wagtail_favicon.models import FaviconSettings
from wagtail_favicon.utils import get_rendition_url
2022-08-19 09:36:03 +01:00
from website.common.utils import get_site_title
2023-05-05 12:39:39 +01:00
from website.contrib.singleton_page.utils import SingletonPageCache
2022-08-19 09:36:03 +01:00
from website.home.models import HomePage
2023-05-05 12:39:39 +01:00
from website.search.models import SearchPage
2022-08-19 09:36:03 +01:00
from .feed_generators import CustomFeed
from .models import BasePage
2022-08-19 09:36:03 +01:00
class Error404View(TemplateView):
template_name = ERROR_404_TEMPLATE_NAME
def render_to_response(self, context: dict, **response_kwargs: Any) -> HttpResponse:
2022-09-04 18:17:06 +01:00
resolver_match = self.request.resolver_match
if not resolver_match or resolver_match.url_name != "404":
response_kwargs["status"] = 404
2022-08-19 09:36:03 +01:00
return super().render_to_response(context, **response_kwargs)
def get_context_data(self, **kwargs: dict) -> dict:
context = super().get_context_data(**kwargs)
2022-08-28 16:51:27 +01:00
context["homepage"] = HomePage.objects.get()
2023-05-05 12:39:39 +01:00
context["search_url"] = SingletonPageCache.get_url(SearchPage, self.request)
2022-08-19 09:36:03 +01:00
return context
page_not_found = Error404View.as_view()
2022-08-19 14:35:38 +01:00
2022-10-02 18:53:51 +01:00
@method_decorator(cache_control(max_age=60 * 60), name="dispatch")
2022-08-19 14:35:38 +01:00
class RobotsView(TemplateView):
template_name = "robots.txt"
content_type = "text/plain"
def get_context_data(self, **kwargs: dict) -> dict:
context = super().get_context_data(**kwargs)
context["sitemap"] = self.request.build_absolute_uri(reverse("sitemap"))
return context
2022-10-02 18:53:51 +01:00
@method_decorator(cache_control(max_age=60 * 60), name="dispatch")
2022-08-25 14:11:47 +01:00
class KeybaseView(TemplateView):
template_name = "keybase.txt"
content_type = "text/plain"
class AllPagesFeed(Feed):
feed_type = CustomFeed
link = "/"
def __init__(self) -> None:
self.style_tag = f'<?xml-stylesheet href="{static("contrib/pretty-feed-v3.xsl")}" type="text/xsl"?>'.encode()
super().__init__()
2022-10-02 18:53:51 +01:00
@method_decorator(cache_page(60 * 60))
def __call__(
self, request: HttpRequest, *args: list, **kwargs: dict
) -> HttpResponse:
self.request = request
response = super().__call__(request, *args, **kwargs)
# Override Content-Type to allow styles
response.headers["content-type"] = "application/xml"
# Inject styles
opening_xml = response.content.find(b"?>") + 2
response.content = (
response.content[:opening_xml]
+ b"\n"
+ self.style_tag
+ response.content[opening_xml:]
)
return response
def feed_extra_kwargs(self, obj):
return {**super().feed_extra_kwargs(obj), "request": self.request}
def title(self) -> str:
return f"All Pages Feed :: {get_site_title()}"
def items(self) -> PageQuerySet:
return (
Page.objects.live()
.public()
.exclude(depth__lte=2)
.specific()
.order_by("-last_published_at")
)
def item_guid(self, item: BasePage) -> str:
return item.get_full_url(request=self.request)
def item_title(self, item: BasePage) -> str:
return item.title
def item_link(self, item: BasePage) -> str:
2023-09-04 20:03:17 +01:00
return item.get_full_url(request=self.request) + "?utm_medium=rss"
def item_pubdate(self, item: BasePage) -> datetime:
if item_date := item.date:
return datetime.combine(item_date, time())
return item.first_published_at
def item_updateddate(self, item: BasePage) -> datetime:
return item.last_published_at
def item_description(self, item: BasePage) -> str:
return getattr(item, "summary", None) or item.title
def item_enclosure_url(self, item: BasePage) -> Optional[str]:
2023-09-04 21:39:14 +01:00
if not hasattr(item, "get_meta_image_url"):
return ""
2023-09-04 21:39:14 +01:00
image_url = item.get_meta_image_url(self.request)
2023-06-26 14:27:04 +01:00
2023-09-04 21:39:14 +01:00
if image_url and image_url.startswith("/"):
return self.request.build_absolute_uri(image_url)
2023-06-26 14:27:04 +01:00
2023-09-04 21:39:14 +01:00
return image_url
def item_enclosure_mime_type(self, item: BasePage) -> str:
if not hasattr(item, "get_meta_image_mime"):
return ""
return item.get_meta_image_mime() or ""
2023-06-26 14:27:04 +01:00
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__()
2023-06-26 14:27:04 +01:00
def title(self) -> str:
return self._title
def items(self) -> PageQuerySet:
return self.posts
2023-07-23 14:44:35 +01:00
@method_decorator(cache_control(max_age=60 * 60), name="dispatch")
class FaviconView(RedirectView):
def get_redirect_url(self) -> str:
favicon_settings = FaviconSettings.for_request(self.request)
size = FaviconSettings.icon_sizes[0]
# Force image to PNG
return get_rendition_url(
favicon_settings.base_favicon_image, f"fill-{size}|format-png"
)