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"