Skip to content

Plugin API Reference

This page shows the available public API available for plugins to access.

You can access them by importing them like this:

from web_portal.plugin_api import login_admin_required

# Or

from web_portal import plugin_api

Variables

Route Decorators

used to ensure the app has not gone through setup wizard, aborting to 404 if it has.

Source code in web_portal/core/auth.py
def ensure_not_setup(func: Callable) -> Callable:
    """
    used to ensure the app has not gone through setup wizard,
    aborting to 404 if it has.
    """

    @wraps(func)
    async def wrapper(*args: Any, **kwargs: Any) -> Any:
        if await get_system_setting(SystemSettingKeys.HAS_SETUP, default=False):
            abort(404)
        return await func(*args, **kwargs)

    return wrapper

used the same as login_required but checks whether user is admin

Source code in web_portal/core/auth.py
def login_admin_required(func: Callable) -> Callable:
    """
    used the same as login_required
    but checks whether user is admin
    """

    @wraps(func)
    async def wrapper(*args: Any, **kwargs: Any) -> Any:
        if not (await current_user.is_authenticated_admin):
            raise quart_auth.Unauthorized
        return await func(*args, **kwargs)

    return wrapper

login is required if public access is disabled, otherwise skip

Source code in web_portal/core/auth.py
def login_required_if_secured(func: Callable) -> Callable:
    """
    login is required if public access is disabled, otherwise skip
    """

    @wraps(func)
    async def wrapper(*args: Any, **kwargs: Any) -> Any:
        if (await get_system_setting(SystemSettingKeys.PORTAL_SECURED, default=False)) and not (
            await current_user.is_authenticated
        ):
            raise quart_auth.Unauthorized
        return await func(*args, **kwargs)

    return wrapper

Used to decorate a Quart response, allowing redirects to a provided back_to request arg

Source code in web_portal/core/helpers.py
def redirect_using_back_to(func: Callable) -> Callable:
    """
    Used to decorate a Quart response,
    allowing redirects to a provided back_to request arg
    """

    @wraps(func)
    async def wrapper(*args: Any, **kwargs: Any) -> Response:
        response = await func(*args, **kwargs)
        if response is not None:
            # allow the function to overide wrapper
            return response
        # redirect from request args
        if (back_to_url := request.args.get("back_to")) is not None:
            return redirect(back_to_url)
        # failover if nothing else applies
        return redirect(url_for("portal.portal"))

    return wrapper

Settings Access

Gets a plugin's system setting stored in db or from cache

Parameters:

Name Type Description Default
plugin_name str

The plugin's internal name

required
key str

The setting's key

required
default Any | None

The default value to use if no setting was found, defaults to None

None
skip_cache bool

Whether the skip cache and load from db directly, defaults to False

False

Returns:

Type Description
Any | None

The loaded value or None

Source code in web_portal/core/plugin.py
async def get_plugin_system_setting(
    plugin_name: str, key: str, /, *, default: Any | None = None, skip_cache: bool = False
) -> Any | None:
    """
    Gets a plugin's system setting stored in db or from cache

    :param plugin_name: The plugin's internal name
    :param key: The setting's key
    :param default: The default value to use if no setting was found, defaults to None
    :param skip_cache: Whether the skip cache and load from db directly, defaults to False
    :return: The loaded value or None
    """
    full_key = make_system_setting_plugin_key(plugin_name, key)
    return await get_system_setting(full_key, default=default, skip_cache=skip_cache)

Set a plugin's system setting stored in db and updates cache

Parameters:

Name Type Description Default
plugin_name str

The plugin's internal name

required
key str

The setting's key

required
value Any

Value to update setting to

required
Source code in web_portal/core/plugin.py
async def set_plugin_system_setting(plugin_name: str, key: str, value: Any, /):
    """
    Set a plugin's system setting stored in db and updates cache

    :param plugin_name: The plugin's internal name
    :param key: The setting's key
    :param value: Value to update setting to
    """
    full_key = make_system_setting_plugin_key(plugin_name, key)
    await set_system_setting(full_key, value)

Removes a set plugin's system setting stored in db and cache

Parameters:

Name Type Description Default
plugin_name str

The plugin's internal name

required
key str

The setting's key

required
Source code in web_portal/core/plugin.py
async def remove_plugin_system_setting(plugin_name: str, key: str, /):
    """
    Removes a set plugin's system setting stored in db and cache

    :param plugin_name: The plugin's internal name
    :param key: The setting's key
    """
    full_key = make_system_setting_plugin_key(plugin_name, key)
    await remove_system_setting(full_key)

Widget Access

Used for storing information about a widget, returned by get_widget_details()

Source code in web_portal/core/plugin.py
@dataclass
class WidgetDetails:
    """
    Used for storing information about a widget,
    returned by get_widget_details()
    """

    human_name: str
    internal_name: str
    plugin_name: str
    config: Any | None

Get a widget's owner id

Parameters:

Name Type Description Default
widget_id int

The widgets id

required

Returns:

Type Description
int

The owner id

Source code in web_portal/core/plugin.py
async def get_widget_owner_id(widget_id: int, /) -> int:
    """
    Get a widget's owner id

    :param widget_id: The widgets id
    :return: The owner id
    """
    widget = await app_models.DashboardWidget.get(id=widget_id).prefetch_related("dashboard")
    return widget.dashboard.owner_id

Get a widgets details

Parameters:

Name Type Description Default
widget_id int

The widgets id

required

Returns:

Type Description
WidgetDetails

The details

Source code in web_portal/core/plugin.py
async def get_widget_details(widget_id: int, /) -> WidgetDetails:
    """
    Get a widgets details

    :param widget_id: The widgets id
    :return: The details
    """
    widget = await app_models.DashboardWidget.get(id=widget_id).prefetch_related(
        "widget",
        "widget__plugin",
    )
    return WidgetDetails(
        widget.name,
        deconstruct_widget_name(widget.widget.plugin.internal_name, widget.widget.internal_name),
        widget.widget.plugin.internal_name,
        widget.config,
    )

Set a widgets config

Parameters:

Name Type Description Default
widget_id int

The widgets id

required
config Any | None

The config to update to

required
Source code in web_portal/core/plugin.py
async def set_widget_config(widget_id: int, config: Any | None, /):
    """
    Set a widgets config

    :param widget_id: The widgets id
    :param config: The config to update to
    """
    widget = await app_models.DashboardWidget.get(id=widget_id)
    widget.config = config
    await widget.save()

Plugin

Class used when creating a plugin, stores all information about a plugin and what it supports.

Source code in web_portal/core/plugin.py
@dataclass
class PluginMeta:
    """
    Class used when creating a plugin,
    stores all information about a plugin and what it supports.
    """

    version_specifier: str
    human_name: str
    widgets: dict[str, str]
    db_models: Collection[str | ModuleType]
    blueprints: Collection[Blueprint]
    index_route_url: str
    get_rendered_widget: Callable[[str, int, dict | None], Awaitable[str]] | None = None
    get_rendered_widget_edit: Callable[[str, int, dict | None, str], Awaitable[str]] | None = None
    get_settings: Callable[[], dict] | None = None
    get_injected_head: Callable[[], Awaitable[str]] | None = None
    do_demo_setup: Callable[[], Awaitable] | None = None

    def is_supported_version(self, app_version: str) -> bool:
        """
        Check whether the version requirement matches the given app version

        :param app_version: The app version, given as a semantic version number
        :return: Whether it is supported
        """
        try:
            ver_specifier = SpecifierSet(self.version_specifier)
        except InvalidSpecifier:
            raise PluginVersionException(
                "unexpected version specifier, please use format from PEP 440 e.g. '== 2'"
            ) from None
        else:
            return app_version in ver_specifier

is_supported_version(app_version)

Check whether the version requirement matches the given app version

Parameters:

Name Type Description Default
app_version str

The app version, given as a semantic version number

required

Returns:

Type Description
bool

Whether it is supported

Source code in web_portal/core/plugin.py
def is_supported_version(self, app_version: str) -> bool:
    """
    Check whether the version requirement matches the given app version

    :param app_version: The app version, given as a semantic version number
    :return: Whether it is supported
    """
    try:
        ver_specifier = SpecifierSet(self.version_specifier)
    except InvalidSpecifier:
        raise PluginVersionException(
            "unexpected version specifier, please use format from PEP 440 e.g. '== 2'"
        ) from None
    else:
        return app_version in ver_specifier

Get a plugins's data path for storing persistant data outside of the database, path will be created if not exists when this function is run

Parameters:

Name Type Description Default
plugin_name str

The plugin's internal name

required

Returns:

Type Description
Path

The data path

Source code in web_portal/core/plugin.py
def get_plugin_data_path(plugin_name: str) -> Path:
    """
    Get a plugins's data path for storing
    persistant data outside of the database,
    path will be created if not exists
    when this function is run

    :param plugin_name: The plugin's internal name
    :return: The data path
    """
    data_path = get_settings().DATA_PATH / "plugins" / plugin_name
    data_path.mkdir(parents=True, exist_ok=True)
    return data_path

Endpoints