Source code for feretui.resources.actions

# This file is a part of the FeretUI project
#
#    Copyright (C) 2023-2024 Jean-Sebastien SUZANNE <js.suzanne@gmail.com>
#
# This Source Code Form is subject to the terms of the Mozilla Public License,
# v. 2.0. If a copy of the MPL was not distributed with this file,You can
# obtain one at http://mozilla.org/MPL/2.0/.
"""feretui.resources.actions's module.

Declare the actions.

"""

import urllib
from collections.abc import Callable
from typing import TYPE_CHECKING

from markupsafe import Markup
from polib import POFile

from feretui.context import ContextProperties
from feretui.session import Session

if TYPE_CHECKING:
    from feretui.feretui import FeretUI
    from feretui.resources.view import View
    from feretui.translation import Translation


[docs] class ActionI18nMixin(ContextProperties): """Mixin to declare and get the translation."""
[docs] def get_label(self: "Action") -> str: """Return the translated label. :return: The label translated in the user lang :rtype: str """ return self.feretui.translation.get( self.request.session.lang, f"{self.context}:label", self.label, )
[docs] def get_description(self: "Action") -> str: """Return the translated description. :return: The label translated in the user lang :rtype: str """ return self.feretui.translation.get( self.request.session.lang, f"{self.context}:description", self.description, )
[docs] def export_catalog( self: "Action", translation: "Translation", po: POFile, ) -> None: """Export the menu translation in the catalog. :param translation: The translation instance to add also inside it. :type translation: :class:`.Translation` :param po: The catalog instance :type po: PoFile_ """ po.append(translation.define(f"{self.context}:label", self.label)) if self.description: po.append( translation.define( f"{self.context}:description", self.description, ), )
[docs] class Action(ActionI18nMixin): """Action class. Define an action in actionset in the view meta. """ template_id: str = "feretui-page-resource-action" def __init__( self: "Action", label: str, method: str, icon: str = None, description: str = None, visible_callback: Callable = None, ) -> None: """Construct the Action class. :param label: The label of the menu :type label: str :param method: The method name to call on the resource :type method: str :param icon: The icon html class used in the render :type icon: str :param description: The tooltip, it is a helper to understand the role of the menu :type description: str :param visible_callback: Callback to determine with the session, if the menu is visible or not. :type visible_callback: Callback[:class:`feretui.session.Session`, bool] """ self.label = label self.method = method self.icon = icon self.description = description self.visible_callback = visible_callback
[docs] def get_url( self: "Action", feretui: "FeretUI", # noqa: ARG002 session: Session, # noqa: ARG002 options: dict, # noqa: ARG002 ) -> str: """Return the hx-post url. :param feretui: The feretui client instance. :type feretui: :class:`feretui.feretui.FeretUI` :param session: The session of the user :type session: :class:`feretui.session.Session` :param options: The querystring :type options: dict :return: The url :rtype: str """ return ( f"{feretui.base_url}/action/resource?" f"action=call&method={self.method}" )
[docs] def render( self: "Action", feretui: "FeretUI", session: Session, options: dict, resource_code: str, view_code: str, ) -> Markup: """Return the html of the action. :param feretui: The feretui client instance. :type feretui: :class:`feretui.feretui.FeretUI` :param session: The session of the user :type session: :class:`feretui.session.Session` :param options: The querystring :type options: dict :return: The html :rtype: Markup """ return Markup.unescape( feretui.render_template( session, self.template_id, url=self.get_url(feretui, session, options), label=self.get_label(), description=self.get_description(), icon=self.icon, rcode=resource_code, vcode=view_code, ), )
[docs] def is_visible( self: "Action", session: Session, # noqa: ARG002 ) -> bool: """Return True is the action should be displayed. :param session: The session of the user :type session: :class:`feretui.session.Session` :rtype: bool """ return True
[docs] class GotoViewAction(Action): """Action class. Define an action in actionset in the view meta. """ def __init__( self: "GotoViewAction", label: str, view: str, icon: str = None, description: str = None, visible_callback: Callable = None, ) -> None: """GotoViewAction class. :param label: The label of the menu :type label: str :param view: The view to render :type view: str :param icon: The icon html class used in the render :type icon: str :param description: The tooltip, it is a helper to understand the role of the menu :type description: str :param visible_callback: Callback to determine with the session, if the menu is visible or not. :type visible_callback: Callback[:class:`feretui.session.Session`, bool] """ super().__init__( label, view, icon=icon, description=description, visible_callback=visible_callback, )
[docs] def get_url( self: "Action", feretui: "FeretUI", # noqa: ARG002 session: Session, # noqa: ARG002 options: dict, ) -> str: """Return the hx-post url. :param feretui: The feretui client instance. :type feretui: :class:`feretui.feretui.FeretUI` :param session: The session of the user :type session: :class:`feretui.session.Session` :param options: The querystring :type options: dict :return: The url :rtype: str """ options = options.copy() options.update( { "action": "goto", "view": self.method, }, ) return ( f"{feretui.base_url}/action/resource?" f"{urllib.parse.urlencode(options, doseq=True)}" )
[docs] class SelectedRowsAction(Action): """SelectedRowsAction class. This action is not disabled when an entry is selected. """ template_id: str = "feretui-page-resource-action-for-selected-rows"
[docs] class Actionset(ActionI18nMixin): """Actionset class. The Actionset is a set of action with a label. This need to group the action in the same place """ def __init__( self: "Actionset", label: str, actions: list[Action], icon: str = None, description: str = None, ) -> None: """Actionset constructor. :param label: The label of the menu :type label: str :param actions: The actions in this set :type actions: list[Action] :param icon: The icon html class used in the render :type icon: str :param description: The tooltip, it is a helper to understand the role of the menu :type description: str """ self.label = label self.actions = actions self.icon = icon self.description = description
[docs] def export_catalog( self: "View", translation: "Translation", po: POFile, ) -> None: """Export the translations in the catalog. :param translation: The translation instance to add also inside it. :type translation: :class:`.Translation` :param po: The catalog instance :type po: PoFile_ """ super().export_catalog(translation, po) for action in self.actions: action.export_catalog(translation, po)
[docs] def render( self: "Actionset", feretui: "FeretUI", session: Session, options: dict, resource_code: str, view_code: str, ) -> Markup: """Return the html of the action. :param feretui: The feretui client instance. :type feretui: :class:`feretui.feretui.FeretUI` :param session: The session of the user :type session: :class:`feretui.session.Session` :param options: The querystring :type options: dict :param resource_code: the code of the resource :type resource_code: str :param view_code: the code of the view :type view_code: str :return: The html :rtype: Markup """ return Markup.unescape( feretui.render_template( session, "feretui-page-resource-action-set", label=self.get_label(), actions=[ action.render( feretui, session, options, resource_code, view_code, ) for action in self.actions if action.is_visible(session) ], description=self.description, ), )
[docs] def is_visible( self: "Actionset", session: Session, ) -> bool: """Return True is the action should be displayed. :param session: The session of the user :type session: :class:`feretui.session.Session` :rtype: bool """ return all(action.is_visible(session) for action in self.actions)