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