from django.http import HttpResponse from django.utils.translation import gettext_lazy as _ from django.views.generic import DetailView, ListView from django.views.generic.edit import CreateView, UpdateView from django.urls import reverse from honeypot.decorators import check_honeypot from aircox.conf import settings from ..filters import PageFilters from ..forms import CommentForm from ..models import Comment, Category from .base import BaseView from .mixins import AttachedToMixin, FiltersMixin, ParentMixin __all__ = [ "attached_views", "attach", "BasePageListView", "BasePageDetailView", "PageDetailView", "PageListView", "PageUpdateView", ] attached_views = {} """Register views by StaticPage.Target.""" def attach(cls): """Add decorated view class to `attached_views`""" attached_views[cls.attach_to_value] = cls return cls class CanEditMixin: """Add context 'can_edit' set to True when object is editable by user.""" def can_edit(self, object): """Return True if user can edit current page.""" return False def get_context_data(self, **kwargs): return super().get_context_data(can_edit=self.can_edit(self.object), **kwargs) class BasePageMixin: category = None def get_queryset(self): qs = super().get_queryset().select_subclasses().select_related("cover") if self.request.user.is_authenticated: return qs return qs.published() def get_category(self, page, **kwargs): if page: if getattr(page, "category_id", None): return page.category if getattr(page, "parent_id", None): return self.get_category(page.parent_subclass) if slug := self.kwargs.get("category_slug"): return Category.objects.get(slug=slug) return None def get_context_data(self, *args, **kwargs): kwargs.setdefault("category", self.category) return super().get_context_data(*args, **kwargs) class BasePageListView(AttachedToMixin, BasePageMixin, ParentMixin, BaseView, ListView): """Base view class for BasePage list.""" template_name = "aircox/basepage_list.html" paginate_by = 30 has_headline = True def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs) def get(self, *args, **kwargs): self.category = self.get_category(self.parent) return super().get(*args, **kwargs) def get_queryset(self): query = super().get_queryset() if self.category: query = query.filter(category=self.category) return query def get_context_data(self, **kwargs): kwargs.setdefault("has_headline", self.has_headline) context = super().get_context_data(**kwargs) parent = context.get("parent") if not context.get("page"): if not context.get("title"): model = self.model._meta.verbose_name_plural title = _("{model}") context["title"] = title.format(model=model, parent=parent) if not context.get("cover") and parent and parent.cover: context["cover"] = parent.cover.url return context class BasePageDetailView(BasePageMixin, BaseView, DetailView): """Base view class for BasePage.""" template_name = "aircox/public.html" context_object_name = "page" def get_context_data(self, **kwargs): if self.object.cover: kwargs.setdefault("cover", self.object.cover.url) if self.object.title: kwargs.setdefault("title", self.object.display_title) return super().get_context_data(**kwargs) def get_object(self): if getattr(self, "object", None): return self.object obj = super().get_object() self.category = self.get_category(obj) return obj def get_page(self): return self.object class PageListView(FiltersMixin, BasePageListView): """Page list view.""" filterset_class = PageFilters template_name = None categories = None filters = None def get_template_names(self): return super().get_template_names() + ["aircox/page_list.html"] def get_filterset(self, data, query): # FIXME: not the most efficient, cause join then split (django filters) data["category__id__in"] = ",".join(data.getlist("category__id__in")) return super().get_filterset(data, query) def get_queryset(self): qs = super().get_queryset().select_related("category").order_by("-pub_date") cat_ids = self.model.objects.published().values_list("category_id", flat=True) self.categories = Category.objects.filter(id__in=cat_ids) return qs @classmethod def get_secondary_nav(cls): cat_ids = cls.model.objects.published().values_list("category_id", flat=True) categories = Category.objects.filter(id__in=cat_ids) return tuple( (category.title, reverse(cls.model.list_url_name, kwargs={"category_slug": category.slug})) for category in categories ) def get_context_data(self, **kwargs): kwargs["categories"] = self.categories return super().get_context_data(**kwargs) class PageDetailView(CanEditMixin, BasePageDetailView): """Base view class for pages.""" template_name = None context_object_name = "page" def get_template_names(self): return super().get_template_names() + ["aircox/page_detail.html"] def get_queryset(self): return super().get_queryset().select_related("category") def get_context_data(self, **kwargs): if "comment_form" not in kwargs: kwargs["comment_form"] = self.get_comment_form() kwargs["comments"] = Comment.objects.filter(page=self.object).order_by("-date") if parent_subclass := getattr(self.object, "parent_subclass", None): kwargs["parent"] = parent_subclass if "related_objects" not in kwargs: related = self.get_related_queryset() if related: related = related[: self.related_count] kwargs["related_objects"] = related return super().get_context_data(**kwargs) def get_comment_form(self): if settings.ALLOW_COMMENTS and self.object.allow_comments: return CommentForm() return None @classmethod def as_view(cls, *args, **kwargs): view = super(PageDetailView, cls).as_view(*args, **kwargs) return check_honeypot(view, field_name="website") def post(self, request, *args, **kwargs): self.object = self.get_object() if not self.object.allow_comments: return HttpResponse(_("comments are not allowed"), status=503) form = CommentForm(request.POST) comment = form.save(commit=False) comment.page = self.object comment.save() return self.get(request, *args, **kwargs) class PageCreateView(CanEditMixin, BaseView, CreateView): def get_page(self): return self.object def get_success_url(self): return self.request.path class PageUpdateView(CanEditMixin, BaseView, UpdateView): def get_page(self): return self.object def get_success_url(self): return self.request.path