Source code for chrysalio.lib.panel

"""Panel class."""

from __future__ import annotations

from chameleon import PageTemplateFile

from pyramid.registry import Registry
from pyramid.request import Request
from pyramid.asset import abspath_from_asset_spec

from ..lib.utils import tostr
from ..lib.form import get_action, Form
from ..lib.navigation import NavPanel
from ..helpers.literal import Literal
from ..includes.themes import theme_static_prefix
from .i18n import _

PANEL_ITEM_PREFIX = 'panel:'


# =============================================================================
[docs] class Panel(): """Class to manage side panel. :param list area: (optional) List of route names which determine the area where the panel is visible. """ uid: str = '' label: str = '' nav_location: tuple[str | None, int] = (None, 0) # (menu_id, index) nav_button_class: str = '' nav_entry = NavPanel template = 'chrysalio:Templates/panel.pt' css = () javascripts = () constants: dict = {} need_form: bool = False can_reopen: bool = False area: tuple[str, ...] = () # ------------------------------------------------------------------------- def __init__(self, area: tuple[str, ...] | None = None): """Constructor method.""" if not self.label: self.label = self.uid if area is not None: self.area = area # -------------------------------------------------------------------------
[docs] @classmethod def register( cls, registry: Registry, panel_class, area: tuple[str, ...] | None = None) -> Panel | None: """Method to register the panel and possibly add it to a navigation menu. :type registry: pyramid.registry.Registry :param registry: Application registry. :param panel_class: Panel class. :param list area: (optional) List of route names which determine the area where the panel is visible. :param bool add2systray: (default=True) If ``True`` add the panel to systray. :rtype: :class:`.lib.panel.Panel` or ``None`` """ if not panel_class.uid: return None if 'panels' not in registry: registry['panels'] = {} if panel_class.uid in registry['panels']: return registry['panels'][panel_class.uid] registry['panels'][panel_class.uid] = panel_class(area) if panel_class.nav_location[0] is None \ or not panel_class.nav_button_class: return registry['panels'][panel_class.uid] if panel_class.nav_location[0] not in registry['navigation']: registry['navigation'][panel_class.nav_location[0]] = [] registry['navigation'][panel_class.nav_location[0]].insert( panel_class.nav_location[1], panel_class.nav_entry( panel_class.uid, panel_class.label, panel_class.nav_button_class, area)) return registry['panels'][panel_class.uid]
# -------------------------------------------------------------------------
[docs] @classmethod def has_open_panel(cls, request: Request) -> str | None: """Return ``'cioHasOpenPanel'`` if almost one panel is open. :type request: pyramid.request.Request :param request: Current request. :rtype: class:`str` or ``None`` """ if 'panels' not in request.registry or 'panels' not in request.session: return None for panel in request.registry['panels'].values(): if panel.is_open(request): return 'cioHasOpenPanel' return None
# -------------------------------------------------------------------------
[docs] def is_open(self, request: Request) -> bool: """``True`` if the panel is open. :type request: pyramid.request.Request :param request: Current request. :rtype: bool """ if self.area and (request.matched_route is None or request.matched_route.name not in self.area): return False self._prepare_session(request) return request.session['panels'][self.uid]['is_open']
# -------------------------------------------------------------------------
[docs] def was_open(self, request: Request) -> bool: """``True`` if the panel was previously open. :type request: pyramid.request.Request :param request: Current request. :rtype: bool """ self._prepare_session(request) return request.session['panels'][self.uid]['was_open']
# -------------------------------------------------------------------------
[docs] def open(self, request: Request): """Open the panel and memorize the state. :type request: pyramid.request.Request :param request: Current request. """ if 'panel' in request.GET: del request.GET['panel'] self._prepare_session(request) request.session['panels'][self.uid]['was_open'] = \ request.session['panels'][self.uid]['is_open'] request.session['panels'][self.uid]['is_open'] = True request.session['panels'][self.uid]['can_reopen'] = False if 'panels' in request.registry: for other in request.registry['panels'].values(): if other.uid != self.uid: other.close(request, self.can_reopen)
# -------------------------------------------------------------------------
[docs] @classmethod def reopen_panel(cls, request: Request) -> Panel | None: """Return a possible panel which can be reopened. :type request: pyramid.request.Request :param request: Current request. :rtype: .lib.panel.Panel """ if 'panels' not in request.registry or 'panels' not in request.session: return None for panel in request.registry['panels'].values(): if panel.can_reopen and panel.uid in request.session['panels'] \ and (request.session['panels'][panel.uid]['can_reopen'] or request.session['panels'][panel.uid]['is_open']): return panel return None
# -------------------------------------------------------------------------
[docs] @classmethod def reopen(cls, request: Request): """Reopen a panel. :type request: pyramid.request.Request :param request: Current request. """ if 'panels' not in request.registry or 'panels' not in request.session: return for panel in request.registry['panels'].values(): if panel.can_reopen and panel.uid in request.session['panels'] \ and request.session['panels'][panel.uid]['can_reopen'] \ and panel.refresh(request): panel.open(request) return
# -------------------------------------------------------------------------
[docs] def refresh(self, request: Request) -> bool: """Try to refresh the content of the panel. :type request: pyramid.request.Request :param request: Current request. """ # pylint: disable = unused-argument return True
# -------------------------------------------------------------------------
[docs] def close(self, request: Request, prevent_reopen: bool = False): """Close the panel and memorize the state. :type request: pyramid.request.Request :param request: Current request. :param bool prevent_reopen: If ``True``, prevent the panel to be reopened. """ if 'panels' not in request.session \ or self.uid not in request.session['panels']: return can_reopen = not prevent_reopen and self.can_reopen \ and 'values' in request.session['panels'][self.uid] if not can_reopen and 'values' in request.session['panels'][self.uid]: del request.session['panels'][self.uid]['values'] request.session['panels'][self.uid]['was_open'] = False request.session['panels'][self.uid]['is_open'] = False request.session['panels'][self.uid]['can_reopen'] = can_reopen
# -------------------------------------------------------------------------
[docs] def clear_values(self, request: Request): """Clear all values of this panel. :type request: pyramid.request.Request :param request: Current request. """ if 'panels' in request.session \ and self.uid in request.session['panels'] \ and 'values' in request.session['panels'][self.uid]: del request.session['panels'][self.uid]['values'] request.session['panels'][self.uid]['can_reopen'] = False
# -------------------------------------------------------------------------
[docs] def set_values(self, request: Request, values: dict): """Set values of this panel. :type request: pyramid.request.Request :param request: Current request. :param dict values: Values to set. """ self._prepare_session(request) request.session['panels'][self.uid]['values'] = values
# -------------------------------------------------------------------------
[docs] def values(self, request: Request) -> dict: """Return values of this panel. :type request: pyramid.request.Request :param request: Current request. :rtype: dict """ self._prepare_session(request) return request.session['panels'][self.uid].get('values', {})
# -------------------------------------------------------------------------
[docs] def set_value(self, request: Request, value_id: str, value): """Set a value of this panel. :type request: pyramid.request.Request :param request: Current request. :param str value_id: Value ID. :param value: Value to set. """ self._prepare_session(request) if 'values' not in request.session['panels'][self.uid]: request.session['panels'][self.uid]['values'] = {} request.session['panels'][self.uid]['values'][value_id] = value
# -------------------------------------------------------------------------
[docs] def value(self, request: Request, value_id: str) -> str | None: """Return the value ``value_id`` of this panel. :type request: pyramid.request.Request :param request: Current request. :param str value_id: ID of the requested value. :rtype: str """ return self.values(request).get(value_id)
# -------------------------------------------------------------------------
[docs] def render( self, request: Request, ts_factory=_, panel_class: str = 'cioPanel') -> str: """Return the content of the panel. :type request: pyramid.request.Request :param request: Current request. :param ts_factory: (default=_) Translation String Factory fucntion. :param str panel_class: (default = 'cioPanel') CSS class for the panel. :rtype: helpers.literal.Literal """ if self.area and (request.matched_route is None or request.matched_route.name not in self.area): return '' my_domain = self.template.partition(':')[0] \ if ':' in self.template else 'chrysalio' def _translate( msgid, domain=my_domain, mapping=None, default=None, context=None, target_language=None): """Translation for Chameleon.""" # pylint: disable = unused-argument return request.localizer.translate( msgid, domain=domain or my_domain, mapping=mapping) params = { # yapf: disable 'request': request, '_': ts_factory, 'panel_class': panel_class, 'route': lambda name, *elts, **kwargs: tostr( request.route_path(name, *elts, **kwargs)), 'theme': theme_static_prefix(request), 'get_action': lambda request: get_action(request, True), 'PANEL_ITEM_PREFIX': PANEL_ITEM_PREFIX, 'panel': self} params.update(self.constants) params.update(self.values(request)) if self.need_form: params['form'] = Form( request, defaults=params.get('form_defaults'), force_defaults='form_defaults' in params) params['form'].forget(PANEL_ITEM_PREFIX) return Literal( PageTemplateFile( abspath_from_asset_spec(self.template), translate=_translate).render(**params))
# -------------------------------------------------------------------------
[docs] def route(self, request: Request) -> str: """Return the route to toggle the state of the panel. :type request: pyramid.request.Request :param request: Current request. :rtype: str """ if request.matched_route is None: self.close(request) return '' query_string = dict(request.GET) query_string.update({'panel': self.uid}) if 'close' in query_string: del query_string['close'] return request.current_route_path(_query=query_string)
# -------------------------------------------------------------------------
[docs] @classmethod def open_panel_css(cls, request: Request) -> tuple: """Return a list of URL of CSS used by the open panel. :type request: pyramid.request.Request :param request: Current request. :rtype: tuple """ for panel in request.registry.get('panels', {}).values(): if request.session.get( # yapf: disable 'panels', {}).get(panel.uid, {}).get('is_open'): return panel.css return ()
# -------------------------------------------------------------------------
[docs] @classmethod def open_panel_js(cls, request: Request) -> tuple: """Return a list of URL of Javascript used by the open panel. :type request: pyramid.request.Request :param request: Current request. :rtype: tuple """ for panel in request.registry.get('panels', {}).values(): if request.session.get( # yapf: disable 'panels', {}).get(panel.uid, {}).get('is_open'): return panel.javascripts return ()
# -------------------------------------------------------------------------
[docs] @classmethod def manage_panels(cls, request: Request): """Possibly, toggle the current panel state. :type request: pyramid.request.Request :param request: Current request. """ if 'panels' not in request.registry: return # Was open for panel in request.registry['panels'].values(): if request.session.get( # yapf: disable 'panels', {}).get(panel.uid, {}).get('is_open'): request.session['panels'][panel.uid]['was_open'] = True # Close or open a panel panel_id = request.GET.get('panel') if panel_id and panel_id in request.registry['panels']: panel = request.registry['panels'][panel_id] if request.GET.get('close'): panel.close(request) else: panel.open(request)
# ------------------------------------------------------------------------- def _prepare_session(self, request: Request): """Prepare session to memorize panel state. :type request: pyramid.request.Request :param request: Current request. """ if 'panels' not in request.session: request.session['panels'] = {} if self.uid not in request.session['panels']: request.session['panels'][self.uid] = { 'was_open': False, 'is_open': False, 'can_reopen': False }