1from __future__ import annotations
2
3import sys
4from typing import TYPE_CHECKING, Any
5
6from jinja2.runtime import Context
7
8from plain.runtime import settings
9from plain.templates import Template
10from plain.utils.safestring import SafeString, mark_safe
11
12from .registry import register_toolbar_item, registry
13
14if TYPE_CHECKING:
15 from plain.http import Request
16
17try:
18 from plain.auth import get_request_user
19except ImportError:
20 get_request_user: Any = None
21
22try:
23 from plain.admin.impersonate import get_request_impersonator
24except ImportError:
25 get_request_impersonator: Any = None
26
27
28class Toolbar:
29 def __init__(self, context: Context) -> None:
30 self.context = context
31 self.request: Request = context["request"]
32 self.version: str = settings.VERSION
33
34 def should_render(self) -> bool:
35 if settings.DEBUG:
36 return True
37
38 if get_request_impersonator:
39 if impersonator := get_request_impersonator(self.request):
40 return getattr(impersonator, "is_admin", False)
41
42 if get_request_user:
43 if user := get_request_user(self.request):
44 return getattr(user, "is_admin", False)
45
46 return False
47
48 def get_items(self) -> list[ToolbarItem]:
49 items = [item(self.context) for item in registry.get_items()]
50 return [item for item in items if item.is_enabled()]
51
52
53class ToolbarItem:
54 name: str = ""
55 panel_template_name: str = ""
56 button_template_name: str = ""
57
58 def __init__(self, context: Context) -> None:
59 self.context = context
60 self.request: Request = context["request"]
61
62 def get_template_context(self) -> dict[str, Any]:
63 context = dict(self.context)
64 context["panel"] = self
65 return context
66
67 def render_panel(self) -> SafeString:
68 template = Template(self.panel_template_name)
69 context = self.get_template_context()
70 return mark_safe(template.render(context))
71
72 def render_button(self) -> SafeString:
73 """Render the toolbar button for the minimized state."""
74 template = Template(self.button_template_name)
75 context = self.get_template_context()
76 return mark_safe(template.render(context))
77
78 def is_enabled(self) -> bool:
79 """Return whether this toolbar item should be displayed."""
80 return True
81
82
83@register_toolbar_item
84class _ExceptionToolbarItem(ToolbarItem):
85 name = "Exception"
86 panel_template_name = "toolbar/exception.html"
87 button_template_name = "toolbar/exception_button.html"
88
89 def __init__(self, context: Context) -> None:
90 super().__init__(context)
91 exception = sys.exception()
92 if exception:
93 from .exceptions import ExceptionContext
94
95 self.exception_context: ExceptionContext | None = ExceptionContext(
96 exception
97 )
98 else:
99 self.exception_context = None
100
101 def is_enabled(self) -> bool:
102 return self.exception_context is not None
103
104 def get_template_context(self) -> dict[str, Any]:
105 ctx = super().get_template_context()
106 ctx["exception_context"] = self.exception_context
107 return ctx
108
109
110@register_toolbar_item
111class _RequestToolbarItem(ToolbarItem):
112 name = "Request"
113 panel_template_name = "toolbar/request.html"