import django_filters from taggit.models import Tag from wagtail import hooks from wagtail.admin.filters import WagtailFilterSet from wagtail.admin.ui.components import Component from wagtail.admin.ui.tables import Column, DateColumn from wagtail.admin.ui.tables.pages import BulkActionsColumn, PageStatusColumn, PageTitleColumn from wagtail.admin.viewsets.pages import PageListingViewSet from wagtail.snippets.models import register_snippet from wagtail.snippets.views.snippets import SnippetViewSet from apps.authors.models import Author from apps.blog.models import ArticlePage, Category, TagMetadata STATUS_CHOICES = [ ("live", "Published"), ("draft", "Draft"), ("scheduled", "Scheduled"), ] class TagMetadataViewSet(SnippetViewSet): model = TagMetadata icon = "tag" list_display = ["tag", "colour"] register_snippet(TagMetadataViewSet) class CategoryViewSet(SnippetViewSet): model = Category icon = "folder-open-inverse" list_display = ["name", "slug", "show_in_nav", "sort_order"] list_filter = ["show_in_nav"] ordering = ["sort_order", "name"] register_snippet(CategoryViewSet) # ── Articles page listing ──────────────────────────────────────────────────── class StatusFilter(django_filters.ChoiceFilter): def filter(self, qs, value): # noqa: A003 if value == "live": return qs.filter(live=True) if value == "draft": return qs.filter(live=False, go_live_at__isnull=True) if value == "scheduled": return qs.filter(live=False, go_live_at__isnull=False) return qs class ArticleFilterSet(WagtailFilterSet): category = django_filters.ModelChoiceFilter( queryset=Category.objects.all(), empty_label="All categories", ) author = django_filters.ModelChoiceFilter( queryset=Author.objects.all(), empty_label="All authors", ) status = StatusFilter( choices=STATUS_CHOICES, empty_label="All statuses", ) tag = django_filters.ModelChoiceFilter( field_name="tags", queryset=Tag.objects.all(), empty_label="All tags", ) class Meta: model = ArticlePage fields = [] class ArticlePageListingViewSet(PageListingViewSet): model = ArticlePage icon = "doc-full" menu_label = "Articles" menu_order = 200 add_to_admin_menu = True name = "articles" columns = [ BulkActionsColumn("bulk_actions"), PageTitleColumn("title", classname="title"), Column("author", label="Author", sort_key="author__name"), Column("category", label="Category"), DateColumn("published_date", label="Published", sort_key="published_date"), PageStatusColumn("status", sort_key="live"), ] filterset_class = ArticleFilterSet default_ordering = "-published_date" @hooks.register("register_admin_viewset") def register_article_listing(): return ArticlePageListingViewSet("articles") # ── Dashboard panel ────────────────────────────────────────────────────────── class ArticlesSummaryPanel(Component): name = "articles_summary" template_name = "blog/panels/articles_summary.html" order = 110 def get_context_data(self, parent_context): context = super().get_context_data(parent_context) context["drafts"] = ( ArticlePage.objects.not_live() .order_by("-latest_revision_created_at")[:5] ) context["scheduled"] = ( ArticlePage.objects.filter(go_live_at__isnull=False, live=False) .order_by("go_live_at")[:5] ) context["recent"] = ( ArticlePage.objects.live() .order_by("-published_date")[:5] ) return context @hooks.register("construct_homepage_panels") def add_articles_summary_panel(request, panels): panels.append(ArticlesSummaryPanel())