dnscms: use wagtail pageviewsets everywhere

This commit is contained in:
2026-05-26 01:41:39 +02:00
parent 38229c97f0
commit 089970a5cd
13 changed files with 266 additions and 145 deletions
+21 -10
View File
@@ -1,7 +1,7 @@
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from wagtail.admin.ui.tables import Column, DateColumn from wagtail.admin.ui.tables import Column, DateColumn
from wagtail.admin.ui.tables.pages import PageStatusColumn, PageTitleColumn from wagtail.admin.ui.tables.pages import PageStatusColumn, PageTitleColumn
from wagtail.admin.viewsets.pages import PageListingViewSet from wagtail.admin.viewsets.pages import PageListingViewSet, PageViewSet
from associations.models import AssociationPage from associations.models import AssociationPage
from dnscms.admin import ListingRedirectChooseParentView from dnscms.admin import ListingRedirectChooseParentView
@@ -16,15 +16,11 @@ class AssociationChooseParentView(ListingRedirectChooseParentView):
listing_url_name = "associations:index" listing_url_name = "associations:index"
class AssociationPageListingViewSet(PageListingViewSet): class AssociationListingMixin:
model = AssociationPage """Shared model + columns for the standalone listing and the page explorer."""
choose_parent_view_class = AssociationChooseParentView
icon = "group"
menu_label = _("Associations")
menu_order = 2
add_to_admin_menu = True
ordering = "title"
model = AssociationPage
icon = "group"
columns = [ columns = [
PageTitleColumn("title", label=_("Title"), sort_key="title", classname="title"), PageTitleColumn("title", label=_("Title"), sort_key="title", classname="title"),
AssociationTypeColumn( AssociationTypeColumn(
@@ -43,4 +39,19 @@ class AssociationPageListingViewSet(PageListingViewSet):
] ]
association_page_listing_viewset = AssociationPageListingViewSet("associations") class AssociationSidebarViewSet(AssociationListingMixin, PageListingViewSet):
"""Standalone 'Associations' sidebar entry, reached independently of the page tree."""
choose_parent_view_class = AssociationChooseParentView
menu_label = _("Associations")
menu_order = 2
add_to_admin_menu = True
ordering = "title"
class AssociationExplorerViewSet(AssociationListingMixin, PageViewSet):
"""Applies the same columns when navigating into AssociationIndex via the page explorer."""
association_sidebar_viewset = AssociationSidebarViewSet("associations")
association_explorer_viewset = AssociationExplorerViewSet()
+8 -3
View File
@@ -1,6 +1,6 @@
from wagtail import hooks from wagtail import hooks
from .admin import association_page_listing_viewset from .admin import association_sidebar_viewset, association_explorer_viewset
from .views import association_chooser_viewset from .views import association_chooser_viewset
@@ -10,5 +10,10 @@ def register_viewset():
@hooks.register("register_admin_viewset") @hooks.register("register_admin_viewset")
def register_association_page_listing_viewset(): def register_association_sidebar_viewset():
return association_page_listing_viewset return association_sidebar_viewset
@hooks.register("register_admin_viewset")
def register_association_explorer_viewset():
return association_explorer_viewset
+36 -13
View File
@@ -3,8 +3,8 @@ from django.utils.translation import gettext, gettext_lazy as _
from django.utils.translation import ngettext from django.utils.translation import ngettext
from wagtail.admin.ui.tables import Column, DateColumn from wagtail.admin.ui.tables import Column, DateColumn
from wagtail.admin.ui.tables.pages import PageStatusColumn, PageTitleColumn from wagtail.admin.ui.tables.pages import PageStatusColumn, PageTitleColumn
from wagtail.admin.views.pages.listing import IndexView from wagtail.admin.views.pages.listing import ExplorableIndexView, IndexView
from wagtail.admin.viewsets.pages import PageListingViewSet from wagtail.admin.viewsets.pages import PageListingViewSet, PageViewSet
from dnscms.admin import ListingRedirectChooseParentView from dnscms.admin import ListingRedirectChooseParentView
from events.models import EventPage from events.models import EventPage
@@ -32,7 +32,9 @@ class OrganizersColumn(Column):
return f"{names[0]} (+{len(names) - 1})" return f"{names[0]} (+{len(names) - 1})"
class EventPageIndexView(IndexView): class EventPagePrefetchMixin:
"""Prefetch the relations the event columns read, so the listing avoids N+1."""
def annotate_queryset(self, pages): def annotate_queryset(self, pages):
pages = super().annotate_queryset(pages) pages = super().annotate_queryset(pages)
return pages.prefetch_related( return pages.prefetch_related(
@@ -41,20 +43,23 @@ class EventPageIndexView(IndexView):
) )
class EventPageIndexView(EventPagePrefetchMixin, IndexView):
pass
class EventPageExplorableIndexView(EventPagePrefetchMixin, ExplorableIndexView):
pass
class EventChooseParentView(ListingRedirectChooseParentView): class EventChooseParentView(ListingRedirectChooseParentView):
listing_url_name = "events:index" listing_url_name = "events:index"
class EventPageListingViewSet(PageListingViewSet): class EventListingMixin:
model = EventPage """Shared model + columns for the standalone listing and the page explorer."""
index_view_class = EventPageIndexView
choose_parent_view_class = EventChooseParentView
icon = "date"
menu_label = _("Events")
menu_order = 1
add_to_admin_menu = True
ordering = "-latest_revision_created_at"
model = EventPage
icon = "date"
columns = [ columns = [
PageTitleColumn("title", label=_("Title"), sort_key="title", classname="title"), PageTitleColumn("title", label=_("Title"), sort_key="title", classname="title"),
EventDateColumn("event_date", label=_("Date"), width="13%"), EventDateColumn("event_date", label=_("Date"), width="13%"),
@@ -69,4 +74,22 @@ class EventPageListingViewSet(PageListingViewSet):
] ]
event_page_listing_viewset = EventPageListingViewSet("events") class EventSidebarViewSet(EventListingMixin, PageListingViewSet):
"""Standalone 'Events' sidebar entry, reached independently of the page tree."""
index_view_class = EventPageIndexView
choose_parent_view_class = EventChooseParentView
menu_label = _("Events")
menu_order = 1
add_to_admin_menu = True
ordering = "-latest_revision_created_at"
class EventExplorerViewSet(EventListingMixin, PageViewSet):
"""Applies the same columns when navigating into EventIndex via the page explorer."""
index_view_class = EventPageExplorableIndexView
event_sidebar_viewset = EventSidebarViewSet("events")
event_explorer_viewset = EventExplorerViewSet()
+8 -3
View File
@@ -1,6 +1,6 @@
from wagtail import hooks from wagtail import hooks
from .admin import event_page_listing_viewset from .admin import event_sidebar_viewset, event_explorer_viewset
from .views import event_organizer_chooser_viewset from .views import event_organizer_chooser_viewset
@@ -10,5 +10,10 @@ def register_viewset():
@hooks.register("register_admin_viewset") @hooks.register("register_admin_viewset")
def register_event_page_listing_viewset(): def register_event_sidebar_viewset():
return event_page_listing_viewset return event_sidebar_viewset
@hooks.register("register_admin_viewset")
def register_event_explorer_viewset():
return event_explorer_viewset
Binary file not shown.
+45 -99
View File
@@ -7,187 +7,143 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: dnscms\n" "Project-Id-Version: dnscms\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-19 21:55+0200\n" "POT-Creation-Date: 2026-05-26 01:39+0200\n"
"Language: nb\n" "Language: nb\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: associations/admin.py:23
msgid "Associations"
msgstr "Foreninger"
#: associations/admin.py:29 events/admin.py:59 news/admin.py:24
msgid "Title" msgid "Title"
msgstr "Tittel" msgstr "Tittel"
#: associations/admin.py:32 associations/models.py:79
msgid "Type" msgid "Type"
msgstr "Type" msgstr "Type"
#: associations/admin.py:38 events/admin.py:64 news/admin.py:27
msgid "Updated" msgid "Updated"
msgstr "Oppdatert" msgstr "Oppdatert"
#: associations/admin.py:42 events/admin.py:68 news/admin.py:31
msgid "Status" msgid "Status"
msgstr "Status" msgstr "Status"
#: associations/models.py:30 associations/models.py:76 events/models.py:327 msgid "Associations"
#: news/models.py:23 news/models.py:69 msgstr "Foreninger"
msgid "Lead" msgid "Lead"
msgstr "Ingress" msgstr "Ingress"
#: associations/models.py:31 associations/models.py:77
msgid "Content" msgid "Content"
msgstr "Innhold" msgstr "Innhold"
#: associations/models.py:42
msgid "association index" msgid "association index"
msgstr "foreningsoversikt" msgstr "foreningsoversikt"
#: associations/models.py:43
msgid "association indexes" msgid "association indexes"
msgstr "foreningsoversikter" msgstr "foreningsoversikter"
#: associations/models.py:52
msgid "Association" msgid "Association"
msgstr "Forening" msgstr "Forening"
#: associations/models.py:53
msgid "Committee" msgid "Committee"
msgstr "Utvalg" msgstr "Utvalg"
#: associations/models.py:73 news/models.py:60
msgid "Excerpt" msgid "Excerpt"
msgstr "Utdrag" msgstr "Utdrag"
#: associations/models.py:74
msgid "A very short summary of the content below. Used in listing views." msgid "A very short summary of the content below. Used in listing views."
msgstr "" msgstr ""
"En veldig kort oppsummering av innholdet nedenfor. Brukes i listevisninger." "En veldig kort oppsummering av innholdet nedenfor. Brukes i listevisninger."
#: associations/models.py:80 events/models.py:189
msgid "Website" msgid "Website"
msgstr "Nettsted" msgstr "Nettsted"
#: associations/models.py:98
msgid "association" msgid "association"
msgstr "forening" msgstr "forening"
#: associations/models.py:99
msgid "associations" msgid "associations"
msgstr "foreninger" msgstr "foreninger"
#: associations/views.py:8
msgid "Choose an association" msgid "Choose an association"
msgstr "Velg en forening" msgstr "Velg en forening"
#: associations/views.py:9
msgid "Choose another association" msgid "Choose another association"
msgstr "Velg en annen forening" msgstr "Velg en annen forening"
#: associations/views.py:10
msgid "Edit this association" msgid "Edit this association"
msgstr "Rediger denne foreningen" msgstr "Rediger denne foreningen"
#: events/admin.py:20
msgid "%Y-%m-%d at %H:%M" msgid "%Y-%m-%d at %H:%M"
msgstr "%Y-%m-%d kl %H:%M" msgstr "%Y-%m-%d kl %H:%M"
#: events/admin.py:22
#, python-format #, python-format
msgid "%(count)d occurrence" msgid "%(count)d occurrence"
msgid_plural "%(count)d occurrences" msgid_plural "%(count)d occurrences"
msgstr[0] "%(count)d forekomst" msgstr[0] "%(count)d forekomst"
msgstr[1] "%(count)d forekomster" msgstr[1] "%(count)d forekomster"
#: events/admin.py:53
msgid "Events"
msgstr "Arrangementer"
#: events/admin.py:60
msgid "Date" msgid "Date"
msgstr "Dato" msgstr "Dato"
#: events/admin.py:61 events/models.py:331
msgid "Organizers" msgid "Organizers"
msgstr "Arrangører" msgstr "Arrangører"
#: events/models.py:73 events/models.py:156 msgid "Events"
msgstr "Arrangementer"
msgid "slug" msgid "slug"
msgstr "permalenke" msgstr "permalenke"
#: events/models.py:75
msgid "The name of the category as it will appear in URLs." msgid "The name of the category as it will appear in URLs."
msgstr "Navnet på kategorien slik det vil vises i URL-er." msgstr "Navnet på kategorien slik det vil vises i URL-er."
#: events/models.py:79
msgid "Should this category be available as a filter in the event programme?" msgid "Should this category be available as a filter in the event programme?"
msgstr "Skal denne kategorien være mulig å filtrere på i programmet?" msgstr "Skal denne kategorien være mulig å filtrere på i programmet?"
#: events/models.py:83 events/models.py:266
msgid "None" msgid "None"
msgstr "Ingen" msgstr "Ingen"
#: events/models.py:91
msgid "Default pig for events of this kind." msgid "Default pig for events of this kind."
msgstr "Standardgris for arrangementer av denne typen." msgstr "Standardgris for arrangementer av denne typen."
#: events/models.py:98 events/models.py:341
msgid "Pig" msgid "Pig"
msgstr "Gris" msgstr "Gris"
#: events/models.py:109
msgid "event category" msgid "event category"
msgstr "arrangementskategori" msgstr "arrangementskategori"
#: events/models.py:110
msgid "event categories" msgid "event categories"
msgstr "arrangementskategorier" msgstr "arrangementskategorier"
#: events/models.py:138
msgid "organizer" msgid "organizer"
msgstr "arrangør" msgstr "arrangør"
#: events/models.py:139
msgid "organizers" msgid "organizers"
msgstr "arrangører" msgstr "arrangører"
#: events/models.py:158
msgid "The name of the organizer as it will appear in URLs." msgid "The name of the organizer as it will appear in URLs."
msgstr "Navnet på arrangøren slik det vil vises i URL-er." msgstr "Navnet på arrangøren slik det vil vises i URL-er."
#: events/models.py:167
msgid "If a DNS association or committee is behind it, choose it here." msgid "If a DNS association or committee is behind it, choose it here."
msgstr "Om en samfundsforening eller -utvalg står bak, velg det her." msgstr "Om en samfundsforening eller -utvalg står bak, velg det her."
#: events/models.py:173
msgid "Link to the external organizer's website" msgid "Link to the external organizer's website"
msgstr "Lenke til nettstedet til ekstern arrangør" msgstr "Lenke til nettstedet til ekstern arrangør"
#: events/models.py:182
msgid "Internal organizer" msgid "Internal organizer"
msgstr "Intern arrangør" msgstr "Intern arrangør"
#: events/models.py:185
msgid "External organizer" msgid "External organizer"
msgstr "Ekstern arrangør" msgstr "Ekstern arrangør"
#: events/models.py:190
msgid "Leave this empty if the organizer exists in the list above." msgid "Leave this empty if the organizer exists in the list above."
msgstr "La denne stå tom om arrangøren finnes i lista over." msgstr "La denne stå tom om arrangøren finnes i lista over."
#: events/models.py:204
msgid "event organizer" msgid "event organizer"
msgstr "arrangør" msgstr "arrangør"
#: events/models.py:205
msgid "event organizers" msgid "event organizers"
msgstr "arrangører" msgstr "arrangører"
#: events/models.py:239
msgid "" msgid ""
"Choose an image for use in the programme and other surfaces. Should be a " "Choose an image for use in the programme and other surfaces. Should be a "
"photo or an illustration without too much text don't reuse a Facebook " "photo or an illustration without too much text don't reuse a Facebook "
@@ -197,7 +153,6 @@ msgstr ""
"bilde eller en illustrasjon uten for mye tekst ikke gjenbruk et Facebook-" "bilde eller en illustrasjon uten for mye tekst ikke gjenbruk et Facebook-"
"cover ukritisk!" "cover ukritisk!"
#: events/models.py:249
msgid "" msgid ""
"A short text that appears right below the title. Feel free to leave it empty " "A short text that appears right below the title. Feel free to leave it empty "
"if you fit most of it in the main title." "if you fit most of it in the main title."
@@ -205,11 +160,9 @@ msgstr ""
"En kort tekst som kommer rett under tittelen. La denne gjerne stå tom om du " "En kort tekst som kommer rett under tittelen. La denne gjerne stå tom om du "
"fikk plass til det meste i hovedtittelen." "fikk plass til det meste i hovedtittelen."
#: events/models.py:267
msgid "Automatic" msgid "Automatic"
msgstr "Automatisk" msgstr "Automatisk"
#: events/models.py:276
msgid "" msgid ""
"The pig that hangs out on the event page. Automatic causes one to be chosen " "The pig that hangs out on the event page. Automatic causes one to be chosen "
"based on the event's category." "based on the event's category."
@@ -217,36 +170,28 @@ msgstr ""
"Grisen som henger på arrangementssiden. Automatisk fører til at en velges " "Grisen som henger på arrangementssiden. Automatisk fører til at en velges "
"basert på arrangementets kategori." "basert på arrangementets kategori."
#: events/models.py:284
msgid "Direct link to ticket purchase, e.g. TicketCo, Billetto or Ticketmaster" msgid "Direct link to ticket purchase, e.g. TicketCo, Billetto or Ticketmaster"
msgstr "" msgstr ""
"Lenke direkte til billettkjøp, f.eks. TicketCo, Billetto eller Ticketmaster" "Lenke direkte til billettkjøp, f.eks. TicketCo, Billetto eller Ticketmaster"
#: events/models.py:289
msgid "Direct link to the event on Facebook" msgid "Direct link to the event on Facebook"
msgstr "Lenke direkte til arrangementet på Facebook" msgstr "Lenke direkte til arrangementet på Facebook"
#: events/models.py:298
msgid "Free" msgid "Free"
msgstr "Gratis" msgstr "Gratis"
#: events/models.py:298
msgid "Is this event free for everyone?" msgid "Is this event free for everyone?"
msgstr "Er dette arrangementet gratis for alle?" msgstr "Er dette arrangementet gratis for alle?"
#: events/models.py:303
msgid "Regular price" msgid "Regular price"
msgstr "Ordinær pris" msgstr "Ordinær pris"
#: events/models.py:304
msgid "Price for students" msgid "Price for students"
msgstr "Pris for studenter" msgstr "Pris for studenter"
#: events/models.py:305
msgid "Price for DNS members" msgid "Price for DNS members"
msgstr "Pris for medlemmer av DNS" msgstr "Pris for medlemmer av DNS"
#: events/models.py:312
msgid "" msgid ""
"Write <strong>0</strong> for free. An empty field hides the price category. " "Write <strong>0</strong> for free. An empty field hides the price category. "
"If possible, write digits only." "If possible, write digits only."
@@ -254,57 +199,44 @@ msgstr ""
"Skriv <strong>0</strong> om gratis. Tomt felt skjuler priskategorien. Om " "Skriv <strong>0</strong> om gratis. Tomt felt skjuler priskategorien. Om "
"mulig, skriv kun tall." "mulig, skriv kun tall."
#: events/models.py:321
msgid "Ticket purchase link" msgid "Ticket purchase link"
msgstr "Billettkjøpslenke" msgstr "Billettkjøpslenke"
#: events/models.py:325
msgid "Subtitle" msgid "Subtitle"
msgstr "Undertittel" msgstr "Undertittel"
#: events/models.py:334
msgid "Who is behind the event?" msgid "Who is behind the event?"
msgstr "Hvem står bak arrangementet?" msgstr "Hvem står bak arrangementet?"
#: events/models.py:337
msgid "Organizer" msgid "Organizer"
msgstr "Arrangør" msgstr "Arrangør"
#: events/models.py:344
msgid "Facebook link" msgid "Facebook link"
msgstr "Facebook-lenke" msgstr "Facebook-lenke"
#: events/models.py:345
msgid "Direct link to the event on Facebook." msgid "Direct link to the event on Facebook."
msgstr "Lenke direkte til arrangementet på Facebook." msgstr "Lenke direkte til arrangementet på Facebook."
#: events/models.py:347
msgid "Pricing and tickets" msgid "Pricing and tickets"
msgstr "Priser og billettkjøp" msgstr "Priser og billettkjøp"
#: events/models.py:349
msgid "Date, time and venue" msgid "Date, time and venue"
msgstr "Dato, tid og lokale" msgstr "Dato, tid og lokale"
#: events/models.py:353
msgid "If the event spans several days, add each day as a separate occurrence." msgid "If the event spans several days, add each day as a separate occurrence."
msgstr "" msgstr ""
"Om arrangementet går over flere dager, legg inn hver dag som en egen " "Om arrangementet går over flere dager, legg inn hver dag som en egen "
"forekomst." "forekomst."
#: events/models.py:356
msgid "Occurrence" msgid "Occurrence"
msgstr "Forekomst" msgstr "Forekomst"
#: events/models.py:399
msgid "event" msgid "event"
msgstr "arrangement" msgstr "arrangement"
#: events/models.py:400
msgid "events" msgid "events"
msgstr "arrangementer" msgstr "arrangementer"
#: events/models.py:560
msgid "" msgid ""
"Use this <em>if none of the venues that can be selected on the left</em> " "Use this <em>if none of the venues that can be selected on the left</em> "
"fit. E.g. <em>Frederikkeplassen</em> or <em>Sirkusteltet</em>." "fit. E.g. <em>Frederikkeplassen</em> or <em>Sirkusteltet</em>."
@@ -312,75 +244,60 @@ msgstr ""
"Bruk denne <em>om ingen av lokalene som kan velges til venstre</em> passer. " "Bruk denne <em>om ingen av lokalene som kan velges til venstre</em> passer. "
"F.eks. <em>Frederikkeplassen</em> eller <em>Sirkusteltet</em>." "F.eks. <em>Frederikkeplassen</em> eller <em>Sirkusteltet</em>."
#: events/models.py:569
msgid "Start" msgid "Start"
msgstr "Start" msgstr "Start"
#: events/models.py:570
msgid "End" msgid "End"
msgstr "Slutt" msgstr "Slutt"
#: events/models.py:575
msgid "Venue" msgid "Venue"
msgstr "Lokale" msgstr "Lokale"
#: events/models.py:576
msgid "Venue as free text" msgid "Venue as free text"
msgstr "Lokale som fritekst" msgstr "Lokale som fritekst"
#: events/models.py:593
msgid "You can't both pick a venue and write something in this field." msgid "You can't both pick a venue and write something in this field."
msgstr "Du kan ikke både velge et lokale og skrive noe i dette feltet." msgstr "Du kan ikke både velge et lokale og skrive noe i dette feltet."
#: events/models.py:598
msgid "Venue is required." msgid "Venue is required."
msgstr "Lokale er påkrevd." msgstr "Lokale er påkrevd."
#: events/models.py:604
msgid "occurrence" msgid "occurrence"
msgstr "forekomst" msgstr "forekomst"
#: events/models.py:605
msgid "occurrences" msgid "occurrences"
msgstr "forekomster" msgstr "forekomster"
#: events/views.py:9
msgid "Choose organizers" msgid "Choose organizers"
msgstr "Velg arrangører" msgstr "Velg arrangører"
#: events/views.py:10
msgid "Choose an organizer" msgid "Choose an organizer"
msgstr "Velg en arrangør" msgstr "Velg en arrangør"
#: events/views.py:11
msgid "Choose another organizer" msgid "Choose another organizer"
msgstr "Velg en annen arrangør" msgstr "Velg en annen arrangør"
#: events/views.py:12
msgid "Edit this organizer" msgid "Edit this organizer"
msgstr "Rediger denne arrangøren" msgstr "Rediger denne arrangøren"
#: images/models.py:40
msgid "image" msgid "image"
msgstr "bilde" msgstr "bilde"
#: images/models.py:41
msgid "images" msgid "images"
msgstr "bilder" msgstr "bilder"
#: news/admin.py:18 msgid "First published"
msgstr "Først publisert"
msgid "News" msgid "News"
msgstr "Nyheter" msgstr "Nyheter"
#: news/models.py:33
msgid "news index" msgid "news index"
msgstr "nyhetsoversikt" msgstr "nyhetsoversikt"
#: news/models.py:34
msgid "news indexes" msgid "news indexes"
msgstr "nyhetsoversikter" msgstr "nyhetsoversikter"
#: news/models.py:52
msgid "" msgid ""
"Choose an image for use on the front page and other surfaces. Should be a " "Choose an image for use on the front page and other surfaces. Should be a "
"photo or an illustration without too much text." "photo or an illustration without too much text."
@@ -388,15 +305,13 @@ msgstr ""
"Velg et bilde til bruk på forsiden og andre visningsflater. Bør være et " "Velg et bilde til bruk på forsiden og andre visningsflater. Bør være et "
"bilde eller en illustrasjon uten for mye tekst." "bilde eller en illustrasjon uten for mye tekst."
#: news/models.py:62
msgid "" msgid ""
"A very short summary of the article's content. Used on the front page and in " "A very short summary of the article's content. Used on the front page and in "
"the article listing." "the article listing."
msgstr "" msgstr ""
"En veldig kort oppsummering av innholdet i artikkelen. Brukes på forsiden " "En veldig kort oppsummering av innholdet i artikkelen. Brukes på forsiden og "
"og i artikkeloversikten." "i artikkeloversikten."
#: news/models.py:71
msgid "" msgid ""
"A brief, introductory paragraph that summarizes the main content of the " "A brief, introductory paragraph that summarizes the main content of the "
"article." "article."
@@ -404,10 +319,41 @@ msgstr ""
"Et kortfattet, innledende avsnitt som oppsummerer hovedinnholdet i " "Et kortfattet, innledende avsnitt som oppsummerer hovedinnholdet i "
"artikkelen." "artikkelen."
#: news/models.py:92
msgid "news article" msgid "news article"
msgstr "nyhetsartikkel" msgstr "nyhetsartikkel"
#: news/models.py:93
msgid "news articles" msgid "news articles"
msgstr "nyhetsartikler" msgstr "nyhetsartikler"
msgid "Rentals page"
msgstr "Utleieside"
msgid "Venue overview"
msgstr "Lokaleoversikt"
msgid "Venues"
msgstr "Lokaler"
msgid "venue index"
msgstr "lokaleoversikt"
msgid "venue indexes"
msgstr "lokaleoversikter"
msgid "rentals page"
msgstr "utleieside"
msgid "rentals pages"
msgstr "utleiesider"
msgid "venue"
msgstr "lokale"
msgid "venues"
msgstr "lokaler"
#~ msgid "Bookable"
#~ msgstr "Til utleie"
#~ msgid "Listed"
#~ msgstr "I oversikt"
+27 -10
View File
@@ -1,7 +1,7 @@
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from wagtail.admin.ui.tables import DateColumn from wagtail.admin.ui.tables import DateColumn
from wagtail.admin.ui.tables.pages import PageStatusColumn, PageTitleColumn from wagtail.admin.ui.tables.pages import PageStatusColumn, PageTitleColumn
from wagtail.admin.viewsets.pages import PageListingViewSet from wagtail.admin.viewsets.pages import PageListingViewSet, PageViewSet
from dnscms.admin import ListingRedirectChooseParentView from dnscms.admin import ListingRedirectChooseParentView
from news.models import NewsPage from news.models import NewsPage
@@ -11,17 +11,19 @@ class NewsChooseParentView(ListingRedirectChooseParentView):
listing_url_name = "news:index" listing_url_name = "news:index"
class NewsPageListingViewSet(PageListingViewSet): class NewsListingMixin:
model = NewsPage """Shared model + columns for the standalone listing and the page explorer."""
choose_parent_view_class = NewsChooseParentView
icon = "info-circle"
menu_label = _("News")
menu_order = 3
add_to_admin_menu = True
ordering = "-latest_revision_created_at"
model = NewsPage
icon = "info-circle"
columns = [ columns = [
PageTitleColumn("title", label=_("Title"), sort_key="title", classname="title"), PageTitleColumn("title", label=_("Title"), sort_key="title", classname="title"),
DateColumn(
"first_published_at",
label=_("First published"),
sort_key="first_published_at",
width="10%",
),
DateColumn( DateColumn(
"latest_revision_created_at", "latest_revision_created_at",
label=_("Updated"), label=_("Updated"),
@@ -32,4 +34,19 @@ class NewsPageListingViewSet(PageListingViewSet):
] ]
news_page_listing_viewset = NewsPageListingViewSet("news") class NewsSidebarViewSet(NewsListingMixin, PageListingViewSet):
"""Standalone 'News' sidebar entry, reached independently of the page tree."""
choose_parent_view_class = NewsChooseParentView
menu_label = _("News")
menu_order = 3
add_to_admin_menu = True
ordering = "-latest_revision_created_at"
class NewsExplorerViewSet(NewsListingMixin, PageViewSet):
"""Applies the same columns when navigating into NewsIndex via the page explorer."""
news_sidebar_viewset = NewsSidebarViewSet("news")
news_explorer_viewset = NewsExplorerViewSet()
+8 -3
View File
@@ -1,8 +1,13 @@
from wagtail import hooks from wagtail import hooks
from .admin import news_page_listing_viewset from .admin import news_sidebar_viewset, news_explorer_viewset
@hooks.register("register_admin_viewset") @hooks.register("register_admin_viewset")
def register_news_page_listing_viewset(): def register_news_sidebar_viewset():
return news_page_listing_viewset return news_sidebar_viewset
@hooks.register("register_admin_viewset")
def register_news_explorer_viewset():
return news_explorer_viewset
+4 -4
View File
@@ -1,4 +1,4 @@
from news.admin import NewsPageListingViewSet from news.admin import NewsSidebarViewSet
from news.models import NewsPage from news.models import NewsPage
from tests.conftest import NewsPageFactory from tests.conftest import NewsPageFactory
@@ -11,9 +11,9 @@ def test_news_page_persists_via_factory(news_index):
assert reloaded.excerpt == "Short summary" assert reloaded.excerpt == "Short summary"
def test_news_listing_viewset_wired_to_newspage(): def test_news_sidebar_viewset_wired_to_newspage():
assert NewsPageListingViewSet.model is NewsPage assert NewsSidebarViewSet.model is NewsPage
assert NewsPageListingViewSet.add_to_admin_menu is True assert NewsSidebarViewSet.add_to_admin_menu is True
def test_graphql_news_index_query(news_index, graphql_post): def test_graphql_news_index_query(news_index, graphql_post):
+58
View File
@@ -0,0 +1,58 @@
from django.utils.translation import gettext_lazy as _
from wagtail.admin.ui.tables import BooleanColumn, DateColumn
from wagtail.admin.ui.tables.pages import PageStatusColumn, PageTitleColumn
from wagtail.admin.viewsets.pages import PageListingViewSet, PageViewSet
from dnscms.admin import ListingRedirectChooseParentView
from venues.models import VenuePage
class VenueChooseParentView(ListingRedirectChooseParentView):
listing_url_name = "venues:index"
class VenueListingMixin:
"""Shared model + columns for the standalone listing and the page explorer."""
model = VenuePage
icon = "home"
columns = [
PageTitleColumn("title", label=_("Title"), sort_key="title", classname="title"),
BooleanColumn(
"show_as_bookable",
label=_("Rentals page"),
sort_key="show_as_bookable",
width="10%",
),
BooleanColumn(
"show_in_overview",
label=_("Venue overview"),
sort_key="show_in_overview",
width="10%",
),
DateColumn(
"latest_revision_created_at",
label=_("Updated"),
sort_key="latest_revision_created_at",
width="10%",
),
PageStatusColumn("status", label=_("Status"), sort_key="live", width="10%"),
]
class VenueSidebarViewSet(VenueListingMixin, PageListingViewSet):
"""Standalone 'Venues' sidebar entry, reached independently of the page tree."""
choose_parent_view_class = VenueChooseParentView
menu_label = _("Venues")
menu_order = 4
add_to_admin_menu = True
ordering = "title"
class VenueExplorerViewSet(VenueListingMixin, PageViewSet):
"""Applies the same columns when navigating into VenueIndex via the page explorer."""
venue_sidebar_viewset = VenueSidebarViewSet("venues")
venue_explorer_viewset = VenueExplorerViewSet()
@@ -0,0 +1,25 @@
# Generated by Django 6.0.5 on 2026-05-25 23:40
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('venues', '0024_venuepage_show_in_overview_and_more'),
]
operations = [
migrations.AlterModelOptions(
name='venueindex',
options={'verbose_name': 'venue index', 'verbose_name_plural': 'venue indexes'},
),
migrations.AlterModelOptions(
name='venuepage',
options={'verbose_name': 'venue', 'verbose_name_plural': 'venues'},
),
migrations.AlterModelOptions(
name='venuerentalindex',
options={'verbose_name': 'rentals page', 'verbose_name_plural': 'rentals pages'},
),
]
+13
View File
@@ -1,4 +1,5 @@
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _
from grapple.helpers import register_singular_query_field from grapple.helpers import register_singular_query_field
from grapple.models import ( from grapple.models import (
GraphQLBoolean, GraphQLBoolean,
@@ -38,6 +39,10 @@ class VenueIndex(HeadlessMixin, Page):
graphql_fields = [GraphQLRichText("lead"), GraphQLStreamfield("body")] graphql_fields = [GraphQLRichText("lead"), GraphQLStreamfield("body")]
class Meta:
verbose_name = _("venue index")
verbose_name_plural = _("venue indexes")
@register_singular_query_field("venueRentalIndex") @register_singular_query_field("venueRentalIndex")
class VenueRentalIndex(HeadlessMixin, Page): class VenueRentalIndex(HeadlessMixin, Page):
@@ -55,6 +60,10 @@ class VenueRentalIndex(HeadlessMixin, Page):
graphql_fields = [GraphQLRichText("lead"), GraphQLStreamfield("body")] graphql_fields = [GraphQLRichText("lead"), GraphQLStreamfield("body")]
class Meta:
verbose_name = _("rentals page")
verbose_name_plural = _("rentals pages")
class VenuePage(HeadlessMixin, WPImportedPageMixin, Page): class VenuePage(HeadlessMixin, WPImportedPageMixin, Page):
# no children # no children
@@ -184,3 +193,7 @@ class VenuePage(HeadlessMixin, WPImportedPageMixin, Page):
search_fields = Page.search_fields + [ search_fields = Page.search_fields + [
index.SearchField("body"), index.SearchField("body"),
] ]
class Meta:
verbose_name = _("venue")
verbose_name_plural = _("venues")
+13
View File
@@ -0,0 +1,13 @@
from wagtail import hooks
from .admin import venue_explorer_viewset, venue_sidebar_viewset
@hooks.register("register_admin_viewset")
def register_venue_sidebar_viewset():
return venue_sidebar_viewset
@hooks.register("register_admin_viewset")
def register_venue_explorer_viewset():
return venue_explorer_viewset