1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
"""Nextcloud API for declaring UI for settings."""
import dataclasses
import enum
import typing
from ..._exceptions import NextcloudExceptionNotFound
from ..._misc import require_capabilities
from ..._session import AsyncNcSessionApp, NcSessionApp
class SettingsFieldType(enum.Enum): # StrEnum
"""Declarative Settings Field Type."""
TEXT = "text"
"""NcInputField type text"""
PASSWORD = "password" # noqa
"""NcInputField type password"""
EMAIL = "email"
"""NcInputField type email"""
TEL = "tel"
"""NcInputField type tel"""
URL = "url"
"""NcInputField type url"""
NUMBER = "number"
"""NcInputField type number"""
CHECKBOX = "checkbox"
"""NcCheckboxRadioSwitch type checkbox"""
MULTI_CHECKBOX = "multi-checkbox"
"""Multiple NcCheckboxRadioSwitch type checkbox representing a one config value (saved as JSON object)"""
RADIO = "radio"
"""NcCheckboxRadioSwitch type radio"""
SELECT = "select"
"""NcSelect"""
MULTI_SELECT = "multi-select"
"""Multiple NcSelect representing a one config value (saved as JSON array)"""
@dataclasses.dataclass
class SettingsField:
"""Section field."""
id: str
title: str
type: SettingsFieldType
default: bool | int | float | str | list[bool | int | float | str] | dict[str, typing.Any]
options: dict | list = dataclasses.field(default_factory=dict)
description: str = ""
placeholder: str = ""
label: str = ""
notify = False # to be supported in future
@classmethod
def from_dict(cls, data: dict) -> "SettingsField":
"""Creates instance of class from dict, ignoring unknown keys."""
filtered_data = {
k: SettingsFieldType(v) if k == "type" else v for k, v in data.items() if k in cls.__annotations__
}
return cls(**filtered_data)
def to_dict(self) -> dict:
"""Returns data in format that is accepted by AppAPI."""
return {
"id": self.id,
"title": self.title,
"type": self.type.value,
"default": self.default,
"description": self.description,
"options": (
[{"name": key, "value": value} for key, value in self.options.items()]
if isinstance(self.options, dict)
else self.options
),
"placeholder": self.placeholder,
"label": self.label,
"notify": self.notify,
}
@dataclasses.dataclass
class SettingsForm:
"""Settings Form and Section."""
id: str
section_id: str
title: str
fields: list[SettingsField] = dataclasses.field(default_factory=list)
description: str = ""
priority: int = 50
doc_url: str = ""
section_type: str = "personal"
@classmethod
def from_dict(cls, data: dict) -> "SettingsForm":
"""Creates instance of class from dict, ignoring unknown keys."""
filtered_data = {k: v for k, v in data.items() if k in cls.__annotations__}
filtered_data["fields"] = [SettingsField.from_dict(i) for i in filtered_data.get("fields", [])]
return cls(**filtered_data)
def to_dict(self) -> dict:
"""Returns data in format that is accepted by AppAPI."""
return {
"id": self.id,
"priority": self.priority,
"section_type": self.section_type,
"section_id": self.section_id,
"title": self.title,
"description": self.description,
"doc_url": self.doc_url,
"fields": [i.to_dict() for i in self.fields],
}
_EP_SUFFIX: str = "ui/settings"
class _DeclarativeSettingsAPI:
"""Class providing API for creating UI for the ExApp settings, avalaible as **nc.ui.settings.<method>**."""
def __init__(self, session: NcSessionApp):
self._session = session
def register_form(self, form_schema: SettingsForm | dict[str, typing.Any]) -> None:
"""Registers or edit the Settings UI Form."""
require_capabilities("app_api", self._session.capabilities)
param = {"formScheme": form_schema.to_dict() if isinstance(form_schema, SettingsForm) else form_schema}
self._session.ocs("POST", f"{self._session.ae_url}/{_EP_SUFFIX}", json=param)
def unregister_form(self, form_id: str, not_fail=True) -> None:
"""Removes Settings UI Form."""
require_capabilities("app_api", self._session.capabilities)
try:
self._session.ocs("DELETE", f"{self._session.ae_url}/{_EP_SUFFIX}", params={"formId": form_id})
except NextcloudExceptionNotFound as e:
if not not_fail:
raise e from None
def get_entry(self, form_id: str) -> SettingsForm | None:
"""Get information of the Settings UI Form."""
require_capabilities("app_api", self._session.capabilities)
try:
return SettingsForm.from_dict(
self._session.ocs("GET", f"{self._session.ae_url}/{_EP_SUFFIX}", params={"formId": form_id})
)
except NextcloudExceptionNotFound:
return None
class _AsyncDeclarativeSettingsAPI:
"""Class providing async API for creating UI for the ExApp settings."""
def __init__(self, session: AsyncNcSessionApp):
self._session = session
async def register_form(self, form_schema: SettingsForm | dict[str, typing.Any]) -> None:
"""Registers or edit the Settings UI Form."""
require_capabilities("app_api", await self._session.capabilities)
param = {"formScheme": form_schema.to_dict() if isinstance(form_schema, SettingsForm) else form_schema}
await self._session.ocs("POST", f"{self._session.ae_url}/{_EP_SUFFIX}", json=param)
async def unregister_form(self, form_id: str, not_fail=True) -> None:
"""Removes Settings UI Form."""
require_capabilities("app_api", await self._session.capabilities)
try:
await self._session.ocs("DELETE", f"{self._session.ae_url}/{_EP_SUFFIX}", params={"formId": form_id})
except NextcloudExceptionNotFound as e:
if not not_fail:
raise e from None
async def get_entry(self, form_id: str) -> SettingsForm | None:
"""Get information of the Settings UI Form."""
require_capabilities("app_api", await self._session.capabilities)
try:
return SettingsForm.from_dict(
await self._session.ocs("GET", f"{self._session.ae_url}/{_EP_SUFFIX}", params={"formId": form_id})
)
except NextcloudExceptionNotFound:
return None
|