Compare commits
11 Commits
6bc1bc4ebc
...
fc4f375d8e
Author | SHA1 | Date |
---|---|---|
Renovate | fc4f375d8e | |
Jake Howard | fe43b9c683 | |
Jake Howard | 6cbac34f2d | |
Jake Howard | e5de558958 | |
Jake Howard | 1a9d981c7d | |
Jake Howard | b0f1191d8f | |
Jake Howard | bd4c1a193a | |
Jake Howard | 1934b36ec1 | |
Jake Howard | 23ce49ca8f | |
Jake Howard | 926e62518c | |
Jake Howard | ec609ae562 |
|
@ -1,3 +1,6 @@
|
|||
{
|
||||
"extends": ["stylelint-config-standard-scss", "stylelint-config-prettier-scss"]
|
||||
"extends": ["stylelint-config-standard-scss", "stylelint-config-prettier-scss"],
|
||||
"rules": {
|
||||
"scss/at-extend-no-missing-placeholder": null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ wagtail-lite-youtube-embed==0.1.0
|
|||
# DRF OpenAPI dependencies
|
||||
uritemplate
|
||||
PyYAML
|
||||
inflection
|
||||
|
||||
# Use custom `wagtail-favicon` with performance improvements
|
||||
git+https://github.com/RealOrangeOne/wagtail-favicon@b892165e047b35c46d7244109b9ad9226d32a213
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
body.page-homepage {
|
||||
min-height: 100vh;
|
||||
|
||||
@include tablet {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
main {
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
|
@ -24,11 +20,8 @@ body.page-homepage {
|
|||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
@include mobile {
|
||||
margin: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
@ -78,7 +71,7 @@ body.page-homepage {
|
|||
}
|
||||
|
||||
.content-list {
|
||||
width: 100%;
|
||||
width: 90%;
|
||||
|
||||
.card-image {
|
||||
overflow: hidden;
|
||||
|
@ -90,7 +83,7 @@ body.page-homepage {
|
|||
object-fit: cover;
|
||||
position: absolute;
|
||||
transition: filter 0.25s;
|
||||
filter: brightness(0.9);
|
||||
filter: brightness(0.85);
|
||||
}
|
||||
|
||||
p {
|
||||
|
@ -100,7 +93,7 @@ body.page-homepage {
|
|||
padding: 0.5rem;
|
||||
transition: background-color 0.25s;
|
||||
width: 100%;
|
||||
background-color: rgb(0 0 0 / 50%);
|
||||
background-color: rgb(0 0 0 / 55%);
|
||||
}
|
||||
|
||||
figure {
|
||||
|
@ -114,7 +107,7 @@ body.page-homepage {
|
|||
}
|
||||
|
||||
p {
|
||||
background-color: rgb(0 0 0 / 70%);
|
||||
background-color: rgb(0 0 0 / 75%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.page-blogpostlistpage {
|
||||
.container.listing {
|
||||
.date-header {
|
||||
font-size: $size-2;
|
||||
font-weight: $weight-bold;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block post_content %}
|
||||
<section class="container">
|
||||
<section class="container listing">
|
||||
{% for page in listing_pages %}
|
||||
{% ifchanged %}
|
||||
<h2 id="date-{{ page.date|date:'Y-m' }}" class="date-header">
|
||||
|
|
|
@ -263,7 +263,12 @@ class BaseListingPage(RoutablePageMixin, BaseContentPage):
|
|||
|
||||
def get_context(self, request: HttpRequest) -> dict:
|
||||
context = super().get_context(request)
|
||||
context["listing_pages"] = self.get_paginator_page()
|
||||
listing_pages = self.get_paginator_page()
|
||||
context["listing_pages"] = listing_pages
|
||||
|
||||
# Show listing images if at least 1 page has an image
|
||||
context["show_listing_images"] = any(p.list_image_url for p in listing_pages)
|
||||
|
||||
return context
|
||||
|
||||
@cached_property
|
||||
|
|
|
@ -32,5 +32,38 @@
|
|||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if page.slides_url %}
|
||||
<span class="icon-text">
|
||||
<a href="{{ page.slides_url }}">
|
||||
<span class="icon">
|
||||
<i class="fas fa-lg fa-images"></i>
|
||||
</span>
|
||||
<span>Slides</span>
|
||||
</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
{% if page.video_url %}
|
||||
<span class="icon-text">
|
||||
<a href="{{ page.video_url }}">
|
||||
<span class="icon">
|
||||
<i class="fas fa-lg fa-film"></i>
|
||||
</span>
|
||||
<span>Video</span>
|
||||
</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
{% if page.location_name and page.location_url %}
|
||||
<span class="icon-text">
|
||||
<a href="{{ page.location_url }}">
|
||||
<span class="icon">
|
||||
<i class="fas fa-lg fa-map-marker-alt"></i>
|
||||
</span>
|
||||
<span>{{ page.location_name }}</span>
|
||||
</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endwagtailpagecache %}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{% load wagtailcore_tags wagtail_cache util_tags %}
|
||||
|
||||
{% wagtailpagecache FRAGMENT_CACHE_TTL "listing-item" breadcrumbs %}
|
||||
{% wagtailpagecache FRAGMENT_CACHE_TTL "listing-item" breadcrumbs show_listing_images %}
|
||||
<article class="media listing-item">
|
||||
<div class="columns">
|
||||
<figure class="media-left column is-3 image-column">
|
||||
<figure class="media-left column is-{{ show_listing_images|yesno:'3,1' }} image-column">
|
||||
{% if page.list_image_url %}
|
||||
<a href="{% pageurl page %}" class="image" title="{{ page.title }}">
|
||||
<img src="{{ page.list_image_url }}" alt="{{ page.hero_image_alt }}" loading="lazy" decoding="async" referrerpolicy="no-referrer" />
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
{% load wagtailcore_tags %}
|
||||
|
||||
{% spaceless %}
|
||||
{{ obj.content_html | truncatewords_html:75 | safe }}
|
||||
|
||||
<p>
|
||||
<a href="{% fullpageurl obj %}">Continue Reading…</a>
|
||||
</p>
|
||||
{% endspaceless %}
|
|
@ -64,6 +64,7 @@ class KeybaseView(TemplateView):
|
|||
class AllPagesFeed(Feed):
|
||||
feed_type = CustomFeed
|
||||
link = "/"
|
||||
description_template = "feed-description.html"
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.style_tag = f'<?xml-stylesheet href="{static("contrib/pretty-feed-v3.xsl")}" type="text/xsl"?>'.encode()
|
||||
|
@ -123,9 +124,6 @@ class AllPagesFeed(Feed):
|
|||
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_categories(self, item: BasePage) -> Optional[list[str]]:
|
||||
if isinstance(item, BlogPostPage):
|
||||
return item.tags_list.values_list("slug", flat=True)
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<a href="{% pageurl page %}">
|
||||
<figure class="image is-16by9">
|
||||
<img src="{{ page.list_image_url }}" alt="{{ page.hero_image_alt }}" loading="lazy" decoding="async" referrerpolicy="no-referrer" />
|
||||
<p class="has-text-weight-light">{{ page.title }}</p>
|
||||
<p>{{ page.title }}</p>
|
||||
</figure>
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -10,4 +10,5 @@ urlpatterns = [
|
|||
path("tags/<slug:slug>/", views.TagView.as_view()),
|
||||
path("tags/", views.TagsView.as_view()),
|
||||
path("categories/", views.TagsView.as_view()),
|
||||
path("index.json", views.PageLinksView.as_view()),
|
||||
]
|
||||
|
|
|
@ -12,6 +12,12 @@ class AllPagesFeedView(RedirectView):
|
|||
permanent = True
|
||||
|
||||
|
||||
@method_decorator(cache_control(max_age=60 * 60), name="dispatch")
|
||||
class PageLinksView(RedirectView):
|
||||
pattern_name = "api:page-links"
|
||||
permanent = True
|
||||
|
||||
|
||||
@method_decorator(cache_control(max_age=60 * 60), name="dispatch")
|
||||
class TagView(RedirectView):
|
||||
permanent = True
|
||||
|
|
|
@ -42,6 +42,7 @@ INSTALLED_APPS = [
|
|||
"website.utils",
|
||||
"website.well_known",
|
||||
"website.legacy",
|
||||
"website.talks",
|
||||
"website.contrib.code_block",
|
||||
"website.contrib.mermaid_block",
|
||||
"website.contrib.unsplash",
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
from datetime import timedelta
|
||||
|
||||
from website.common.factories import BaseContentFactory, BaseListingFactory
|
||||
|
||||
from . import models
|
||||
|
||||
|
||||
class TalksListPageFactory(BaseListingFactory):
|
||||
class Meta:
|
||||
model = models.TalksListPage
|
||||
|
||||
|
||||
class TalkPageFactory(BaseContentFactory):
|
||||
duration = timedelta(minutes=30)
|
||||
|
||||
class Meta:
|
||||
model = models.TalkPage
|
|
@ -0,0 +1,354 @@
|
|||
# Generated by Django 5.0.1 on 2024-03-01 17:44
|
||||
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import wagtail.blocks
|
||||
import wagtail.contrib.routable_page.models
|
||||
import wagtail.contrib.typed_table_block.blocks
|
||||
import wagtail.embeds.blocks
|
||||
import wagtail.fields
|
||||
import wagtail.images.blocks
|
||||
import wagtailmetadata.models
|
||||
from django.db import migrations, models
|
||||
|
||||
import website.contrib.code_block.blocks
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("images", "0002_alter_customimage_file_alter_customrendition_file"),
|
||||
("unsplash", "0001_initial"),
|
||||
("wagtailcore", "0089_log_entry_data_json_null_to_object"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="TalkPage",
|
||||
fields=[
|
||||
(
|
||||
"page_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="wagtailcore.page",
|
||||
),
|
||||
),
|
||||
("subtitle", wagtail.fields.RichTextField(blank=True)),
|
||||
(
|
||||
"body",
|
||||
wagtail.fields.StreamField(
|
||||
[
|
||||
("embed", wagtail.embeds.blocks.EmbedBlock()),
|
||||
("rich_text", wagtail.blocks.RichTextBlock()),
|
||||
(
|
||||
"lorem",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"paragraphs",
|
||||
wagtail.blocks.IntegerBlock(min_value=1),
|
||||
)
|
||||
]
|
||||
),
|
||||
),
|
||||
("html", wagtail.blocks.RawHTMLBlock()),
|
||||
(
|
||||
"image",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"image",
|
||||
wagtail.images.blocks.ImageChooserBlock(),
|
||||
),
|
||||
(
|
||||
"caption",
|
||||
wagtail.blocks.RichTextBlock(
|
||||
editor="plain", required=False
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"code",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"filename",
|
||||
wagtail.blocks.CharBlock(
|
||||
max_length=128, required=False
|
||||
),
|
||||
),
|
||||
(
|
||||
"language",
|
||||
wagtail.blocks.ChoiceBlock(
|
||||
choices=website.contrib.code_block.blocks.get_language_choices
|
||||
),
|
||||
),
|
||||
("source", wagtail.blocks.TextBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"tangent",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"name",
|
||||
wagtail.blocks.CharBlock(max_length=64),
|
||||
),
|
||||
(
|
||||
"content",
|
||||
wagtail.blocks.RichTextBlock(
|
||||
editor="simple"
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"mermaid",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("source", wagtail.blocks.TextBlock()),
|
||||
(
|
||||
"caption",
|
||||
wagtail.blocks.RichTextBlock(
|
||||
editor="plain", required=False
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"table",
|
||||
wagtail.contrib.typed_table_block.blocks.TypedTableBlock(
|
||||
[
|
||||
(
|
||||
"rich_text",
|
||||
wagtail.blocks.RichTextBlock(
|
||||
editor="plain"
|
||||
),
|
||||
),
|
||||
("numeric", wagtail.blocks.FloatBlock()),
|
||||
("text", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"iframe",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("url", wagtail.blocks.URLBlock()),
|
||||
(
|
||||
"caption",
|
||||
wagtail.blocks.RichTextBlock(
|
||||
editor="plain", required=False
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
],
|
||||
blank=True,
|
||||
use_json_field=True,
|
||||
),
|
||||
),
|
||||
("date", models.DateField(default=django.utils.timezone.now)),
|
||||
("duration", models.DurationField()),
|
||||
("slides_url", models.URLField(blank=True)),
|
||||
("video_url", models.URLField(blank=True)),
|
||||
("location_name", models.CharField(blank=True, max_length=64)),
|
||||
("location_url", models.URLField(blank=True)),
|
||||
(
|
||||
"hero_image",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="images.customimage",
|
||||
),
|
||||
),
|
||||
(
|
||||
"hero_unsplash_photo",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="unsplash.unsplashphoto",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
bases=("wagtailcore.page", wagtailmetadata.models.MetadataMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="TalksListPage",
|
||||
fields=[
|
||||
(
|
||||
"page_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="wagtailcore.page",
|
||||
),
|
||||
),
|
||||
(
|
||||
"body",
|
||||
wagtail.fields.StreamField(
|
||||
[
|
||||
("embed", wagtail.embeds.blocks.EmbedBlock()),
|
||||
("rich_text", wagtail.blocks.RichTextBlock()),
|
||||
(
|
||||
"lorem",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"paragraphs",
|
||||
wagtail.blocks.IntegerBlock(min_value=1),
|
||||
)
|
||||
]
|
||||
),
|
||||
),
|
||||
("html", wagtail.blocks.RawHTMLBlock()),
|
||||
(
|
||||
"image",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"image",
|
||||
wagtail.images.blocks.ImageChooserBlock(),
|
||||
),
|
||||
(
|
||||
"caption",
|
||||
wagtail.blocks.RichTextBlock(
|
||||
editor="plain", required=False
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"code",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"filename",
|
||||
wagtail.blocks.CharBlock(
|
||||
max_length=128, required=False
|
||||
),
|
||||
),
|
||||
(
|
||||
"language",
|
||||
wagtail.blocks.ChoiceBlock(
|
||||
choices=website.contrib.code_block.blocks.get_language_choices
|
||||
),
|
||||
),
|
||||
("source", wagtail.blocks.TextBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"tangent",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"name",
|
||||
wagtail.blocks.CharBlock(max_length=64),
|
||||
),
|
||||
(
|
||||
"content",
|
||||
wagtail.blocks.RichTextBlock(
|
||||
editor="simple"
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"mermaid",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("source", wagtail.blocks.TextBlock()),
|
||||
(
|
||||
"caption",
|
||||
wagtail.blocks.RichTextBlock(
|
||||
editor="plain", required=False
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"table",
|
||||
wagtail.contrib.typed_table_block.blocks.TypedTableBlock(
|
||||
[
|
||||
(
|
||||
"rich_text",
|
||||
wagtail.blocks.RichTextBlock(
|
||||
editor="plain"
|
||||
),
|
||||
),
|
||||
("numeric", wagtail.blocks.FloatBlock()),
|
||||
("text", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"iframe",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("url", wagtail.blocks.URLBlock()),
|
||||
(
|
||||
"caption",
|
||||
wagtail.blocks.RichTextBlock(
|
||||
editor="plain", required=False
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
],
|
||||
blank=True,
|
||||
use_json_field=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"hero_image",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="images.customimage",
|
||||
),
|
||||
),
|
||||
(
|
||||
"hero_unsplash_photo",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="unsplash.unsplashphoto",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
bases=(
|
||||
wagtail.contrib.routable_page.models.RoutablePageMixin,
|
||||
"wagtailcore.page",
|
||||
wagtailmetadata.models.MetadataMixin,
|
||||
),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,62 @@
|
|||
from datetime import timedelta
|
||||
from typing import Any
|
||||
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
from wagtail.admin.panels import FieldPanel, MultiFieldPanel
|
||||
|
||||
from website.common.models import BaseContentPage, BaseListingPage
|
||||
|
||||
|
||||
class TalksListPage(BaseListingPage):
|
||||
max_count = 1
|
||||
subpage_types = ["talks.TalkPage"]
|
||||
|
||||
|
||||
class TalkPage(BaseContentPage):
|
||||
subpage_types: list[Any] = []
|
||||
parent_page_types = [TalksListPage]
|
||||
|
||||
date = models.DateField(default=timezone.now)
|
||||
|
||||
duration = models.DurationField()
|
||||
|
||||
slides_url = models.URLField(blank=True)
|
||||
video_url = models.URLField(blank=True)
|
||||
|
||||
location_name = models.CharField(max_length=64, blank=True)
|
||||
location_url = models.URLField(blank=True)
|
||||
|
||||
content_panels = BaseContentPage.content_panels + [
|
||||
MultiFieldPanel(
|
||||
[
|
||||
FieldPanel("slides_url"),
|
||||
FieldPanel("video_url"),
|
||||
],
|
||||
heading="Media",
|
||||
),
|
||||
MultiFieldPanel(
|
||||
[
|
||||
FieldPanel("location_name"),
|
||||
FieldPanel("location_url"),
|
||||
],
|
||||
heading="Location",
|
||||
),
|
||||
FieldPanel("duration"),
|
||||
]
|
||||
|
||||
promote_panels = BaseContentPage.promote_panels + [
|
||||
FieldPanel("date"),
|
||||
]
|
||||
|
||||
@property
|
||||
def show_table_of_contents(self) -> bool:
|
||||
return False
|
||||
|
||||
@property
|
||||
def reading_time(self) -> timedelta:
|
||||
return self.duration
|
||||
|
||||
@property
|
||||
def word_count(self) -> int:
|
||||
return 0
|
|
@ -0,0 +1,11 @@
|
|||
{% extends "common/content_page.html" %}
|
||||
|
||||
{% load wagtailembeds_tags %}
|
||||
|
||||
{% block pre_content %}
|
||||
{% if page.video_url %}
|
||||
<section class="container mb-5 content">
|
||||
<div class="block-embed">{% embed page.video_url %}</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,26 @@
|
|||
{% extends "common/listing_page.html" %}
|
||||
|
||||
{% load wagtailroutablepage_tags %}
|
||||
|
||||
{% block post_content %}
|
||||
<section class="container listing">
|
||||
{% for page in listing_pages %}
|
||||
{% ifchanged %}
|
||||
<h2 id="date-{{ page.date.year }}" class="date-header">
|
||||
<time datetime="{{ page.date.year }}" title="{{ page.date.year }}">
|
||||
{{ page.date.year }}
|
||||
</time>
|
||||
</h2>
|
||||
{% endifchanged %}
|
||||
|
||||
{% include "common/listing-item.html" %}
|
||||
{% endfor %}
|
||||
</section>
|
||||
|
||||
{% if listing_pages.has_other_pages %}
|
||||
<section class="container">
|
||||
<hr class="my-5" />
|
||||
{% include "common/pagination.html" with page=listing_pages %}
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,47 @@
|
|||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from website.home.models import HomePage
|
||||
|
||||
from .factories import TalkPageFactory, TalksListPageFactory
|
||||
|
||||
|
||||
class TalkPageTestCase(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls) -> None:
|
||||
cls.home_page = HomePage.objects.get()
|
||||
cls.list_page = TalksListPageFactory(parent=cls.home_page)
|
||||
cls.page = TalkPageFactory(parent=cls.list_page)
|
||||
|
||||
def test_accessible(self) -> None:
|
||||
response = self.client.get(self.page.url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_queries(self) -> None:
|
||||
with self.assertNumQueries(34):
|
||||
self.client.get(self.page.url)
|
||||
|
||||
|
||||
class TalksListPageTestCase(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls) -> None:
|
||||
cls.home_page = HomePage.objects.get()
|
||||
cls.page = TalksListPageFactory(parent=cls.home_page)
|
||||
|
||||
TalkPageFactory(parent=cls.page)
|
||||
TalkPageFactory(parent=cls.page)
|
||||
|
||||
def test_accessible(self) -> None:
|
||||
response = self.client.get(self.page.url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.context["listing_pages"]), 2)
|
||||
|
||||
def test_queries(self) -> None:
|
||||
with self.assertNumQueries(35):
|
||||
self.client.get(self.page.url)
|
||||
|
||||
def test_feed_accessible(self) -> None:
|
||||
response = self.client.get(self.page.url + self.page.reverse_subpage("feed"))
|
||||
self.assertRedirects(
|
||||
response, reverse("feed"), status_code=301, fetch_redirect_response=True
|
||||
)
|
|
@ -1,4 +1,5 @@
|
|||
from datetime import timedelta
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from django.conf import settings
|
||||
from django.http.request import HttpRequest
|
||||
|
@ -56,10 +57,13 @@ def activitypub_proxy(request: HttpRequest) -> HttpResponse:
|
|||
if not settings.ACTIVITYPUB_HOST:
|
||||
raise Http404
|
||||
|
||||
activitypub_url = urljoin(
|
||||
"https://" + settings.ACTIVITYPUB_HOST,
|
||||
request.path,
|
||||
allow_fragments=True,
|
||||
)
|
||||
|
||||
try:
|
||||
return proxy_view(
|
||||
request,
|
||||
f"https://{settings.ACTIVITYPUB_HOST}{request.path}",
|
||||
)
|
||||
return proxy_view(request, activitypub_url)
|
||||
except RequestException:
|
||||
return HttpResponse(status_code=502)
|
||||
return HttpResponse(status=502)
|
||||
|
|
Loading…
Reference in New Issue