from typing import Any, Dict, List, Type from django.contrib.contenttypes.models import ContentType from django.db.models import QuerySet from django.forms import BaseModelForm, modelform_factory from django.http import FileResponse, Http404, HttpRequest from django.shortcuts import get_object_or_404 from django.urls import reverse_lazy from django.utils.decorators import method_decorator from django.utils.translation import gettext as _ from django.views import View from django.views.decorators.cache import never_cache from django.views.generic.detail import SingleObjectMixin from django.views.generic.list import ListView from django_tables2 import SingleTableView from guardian.shortcuts import get_objects_for_user from oauth2_provider.views.mixins import ScopedResourceMixin from reversion.views import RevisionMixin from rules.contrib.views import PermissionRequiredMixin from aleksis.core.mixins import AdvancedCreateView, AdvancedDeleteView, AdvancedEditView from aleksis.core.util.auth_helpers import ClientProtectedResourceMixin from .forms import PosterGroupForm, PosterUploadForm from .models import LiveDocument, Poster, PosterGroup from .tables import LiveDocumentTable class PosterGroupListView(PermissionRequiredMixin, ListView): """Show a list of all poster groups.""" template_name = "resint/group/list.html" model = PosterGroup permission_required = "resint.view_postergroups_rule" def get_queryset(self) -> QuerySet: qs = super().get_queryset() if self.request.user.has_perm("resint.view_postergroup"): return qs return get_objects_for_user(self.request.user, "resint.view_postergroup", qs) class PosterGroupCreateView(PermissionRequiredMixin, AdvancedCreateView): """Create a new poster group.""" model = PosterGroup success_url = reverse_lazy("poster_group_list") template_name = "resint/group/create.html" success_message = _("The poster group has been saved.") form_class = PosterGroupForm permission_required = "resint.create_postergroup_rule" class PosterGroupEditView(PermissionRequiredMixin, AdvancedEditView): """Edit an existing poster group.""" model = PosterGroup success_url = reverse_lazy("poster_group_list") template_name = "resint/group/edit.html" success_message = _("The poster group has been saved.") form_class = PosterGroupForm permission_required = "resint.edit_postergroup_rule" class PosterGroupDeleteView(PermissionRequiredMixin, AdvancedDeleteView): """Delete a poster group.""" model = PosterGroup success_url = reverse_lazy("poster_group_list") success_message = _("The poster group has been deleted.") template_name = "core/pages/delete.html" permission_required = "resint.delete_postergroup_rule" class PosterListView(PermissionRequiredMixin, ListView): """Show a list of all uploaded posters.""" template_name = "resint/poster/list.html" model = Poster permission_required = "resint.view_posters_rule" def get_queryset(self) -> QuerySet: qs = Poster.objects.all().order_by("-year", "-week") if self.request.user.has_perm("resint.view_poster"): return qs allowed_groups = get_objects_for_user( self.request.user, "resint.view_poster_of_group", PosterGroup ) posters = get_objects_for_user(self.request.user, "resint.view_poster", qs) return qs.filter(group__in=allowed_groups) | posters def get_context_data(self, **kwargs: Any) -> Dict[str, Any]: context = super().get_context_data(**kwargs) context["poster_groups"] = PosterGroup.objects.all().order_by("name") return context class RequestMixin: def get_form_kwargs(self) -> Dict[str, Any]: kwargs = super().get_form_kwargs() kwargs["request"] = self.request return kwargs class PosterUploadView(RequestMixin, PermissionRequiredMixin, AdvancedCreateView): """Upload a new poster.""" model = Poster success_url = reverse_lazy("poster_index") template_name = "resint/poster/upload.html" success_message = _("The poster has been uploaded.") form_class = PosterUploadForm permission_required = "resint.upload_poster_rule" class PosterEditView(RequestMixin, PermissionRequiredMixin, AdvancedEditView): """Edit an uploaded poster.""" model = Poster success_url = reverse_lazy("poster_index") template_name = "resint/poster/edit.html" success_message = _("The poster has been changed.") form_class = PosterUploadForm permission_required = "resint.edit_poster_rule" class PosterDeleteView(PermissionRequiredMixin, AdvancedDeleteView): """Delete an uploaded poster.""" model = Poster success_url = reverse_lazy("poster_index") success_message = _("The poster has been deleted.") template_name = "core/pages/delete.html" permission_required = "resint.delete_poster_rule" class PosterCurrentView(PermissionRequiredMixin, SingleObjectMixin, View): """Show the poster which is currently valid.""" model = PosterGroup permission_required = "resint.view_poster_pdf" def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> FileResponse: group = self.get_object() current_poster = group.current_poster file = current_poster.pdf if current_poster else group.default_pdf return FileResponse(file, content_type="application/pdf") class LiveDocumentListView(PermissionRequiredMixin, SingleTableView): """Table of all live documents.""" model = LiveDocument table_class = LiveDocumentTable permission_required = "resint.view_livedocuments_rule" template_name = "resint/live_document/list.html" def get_context_data(self, **kwargs: Any) -> dict[str, Any]: context = super().get_context_data(**kwargs) context["document_types"] = [ (ContentType.objects.get_for_model(m, False), m) for m in LiveDocument.__subclasses__() ] return context @method_decorator(never_cache, name="dispatch") class LiveDocumentCreateView(PermissionRequiredMixin, AdvancedCreateView): """Create view for live documents.""" def get_model(self, request, *args, **kwargs): app_label = kwargs.get("app") model = kwargs.get("model") ct = get_object_or_404(ContentType, app_label=app_label, model=model) return ct.model_class() def get_context_data(self, **kwargs: Any) -> dict[str, Any]: context = super().get_context_data(**kwargs) context["model"] = self.model return context def get(self, request, *args, **kwargs): self.model = self.get_model(request, *args, **kwargs) return super().get(request, *args, **kwargs) def post(self, request, *args, **kwargs): self.model = self.get_model(request, *args, **kwargs) return super().post(request, *args, **kwargs) fields = "__all__" model = LiveDocument permission_required = "resint.add_livedocument_rule" template_name = "resint/live_document/create.html" success_url = reverse_lazy("live_documents") success_message = _("The live document has been created.") @method_decorator(never_cache, name="dispatch") class LiveDocumentEditView(PermissionRequiredMixin, AdvancedEditView): """Edit view for live documents.""" def get_form_class(self) -> Type[BaseModelForm]: return modelform_factory(self.object.__class__, fields=self.fields) model = LiveDocument fields = "__all__" permission_required = "resint.edit_livedocument_rule" template_name = "resint/live_document/edit.html" success_url = reverse_lazy("live_documents") success_message = _("The live document has been saved.") @method_decorator(never_cache, name="dispatch") class LiveDocumentDeleteView(PermissionRequiredMixin, RevisionMixin, AdvancedDeleteView): """Delete view for live documents.""" model = LiveDocument permission_required = "resint.delete_livedocument_rule" template_name = "core/pages/delete.html" success_url = reverse_lazy("live_documents") success_message = _("The live document has been deleted.") class LiveDocumentShowBaseView(SingleObjectMixin, View): """Base view for showing live documents.""" model = LiveDocument def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> FileResponse: live_document = self.get_object() file = live_document.get_current_file() if not file: raise Http404 return FileResponse(file, content_type="application/pdf") class LiveDocumentShowView(PermissionRequiredMixin, LiveDocumentShowBaseView): """Show the current version of the live document.""" permission_required = "resint.view_livedocument_rule" class LiveDocumentShowAPIView( ScopedResourceMixin, ClientProtectedResourceMixin, LiveDocumentShowBaseView ): """Show the current version of the live document in API.""" def get_scopes(self, *args, **kwargs) -> List[str]: """Return the scope needed to access the PDF file.""" return [self.get_object().scope]