1from typing import TYPE_CHECKING
2
3from plain.auth.views import AuthViewMixin
4from plain.urls import reverse
5from plain.utils import timezone
6from plain.views import (
7 TemplateView,
8)
9
10from .registry import registry
11from .types import Img
12
13if TYPE_CHECKING:
14 from ..cards import Card
15
16
17URL_NAMESPACE = "admin"
18
19
20class AdminView(AuthViewMixin, TemplateView):
21 admin_required = True
22
23 title: str = ""
24 path: str = ""
25 description: str = ""
26 image: Img | None = None
27
28 # Leave empty to hide from nav
29 #
30 # An explicit disabling of showing this url/page in the nav
31 # which importantly effects the (future) recent pages list
32 # so you can also use this for pages that can never be bookmarked
33 nav_section = "App"
34 nav_title = ""
35
36 links: dict[str] = {}
37
38 parent_view_class: "AdminView" = None
39
40 template_name = "admin/page.html"
41 cards: list["Card"] = []
42
43 def get_response(self):
44 response = super().get_response()
45 response.headers["Cache-Control"] = (
46 "no-cache, no-store, must-revalidate, max-age=0"
47 )
48 return response
49
50 def get_template_context(self):
51 context = super().get_template_context()
52 context["title"] = self.get_title()
53 context["image"] = self.get_image()
54 context["slug"] = self.get_slug()
55 context["description"] = self.get_description()
56 context["links"] = self.get_links()
57 context["parent_view_classes"] = self.get_parent_view_classes()
58 context["admin_registry"] = registry
59 context["cards"] = self.get_cards()
60 context["render_card"] = lambda card: card().render(self, self.request)
61 context["time_zone"] = timezone.get_current_timezone_name()
62 return context
63
64 @classmethod
65 def view_name(cls) -> str:
66 return f"view_{cls.get_slug()}"
67
68 @classmethod
69 def get_slug(cls) -> str:
70 return f"{cls.__module__}.{cls.__qualname__}".lower().replace(".", "_")
71
72 # Can actually use @classmethod, @staticmethod or regular method for these?
73 def get_title(self) -> str:
74 return self.title
75
76 def get_image(self) -> Img | None:
77 return self.image
78
79 def get_description(self) -> str:
80 return self.description
81
82 @classmethod
83 def get_path(cls) -> str:
84 return cls.path
85
86 @classmethod
87 def get_parent_view_classes(cls) -> list["AdminView"]:
88 parents = []
89 parent = cls.parent_view_class
90 while parent:
91 parents.append(parent)
92 parent = parent.parent_view_class
93 return parents
94
95 @classmethod
96 def get_nav_section(cls) -> bool:
97 if not cls.nav_section:
98 return ""
99
100 if cls.parent_view_class:
101 # Don't show child views by default
102 return ""
103
104 return cls.nav_section
105
106 @classmethod
107 def get_nav_title(cls) -> str:
108 if cls.nav_title:
109 return cls.nav_title
110
111 if cls.title:
112 return cls.title
113
114 raise NotImplementedError(
115 f"Please set a title or nav_title on the {cls} class or implement get_nav_title()."
116 )
117
118 @classmethod
119 def get_view_url(cls, obj=None) -> str:
120 if obj:
121 return reverse(f"{URL_NAMESPACE}:" + cls.view_name(), pk=obj.pk)
122 else:
123 return reverse(f"{URL_NAMESPACE}:" + cls.view_name())
124
125 def get_links(self) -> dict[str]:
126 return self.links.copy()
127
128 def get_cards(self):
129 return self.cards.copy()