File: settings.py

package info (click to toggle)
nc-py-api 0.19.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,320 kB
  • sloc: python: 12,415; makefile: 238; xml: 100; javascript: 56; sh: 14
file content (178 lines) | stat: -rw-r--r-- 6,623 bytes parent folder | download
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