nc-py-api 0.20.0__tar.gz → 0.20.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/CHANGELOG.md +12 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/PKG-INFO +1 -1
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/_preferences_ex.py +32 -48
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/_version.py +1 -1
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/ex_app/integration_fastapi.py +1 -1
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/ex_app/ui/settings.py +2 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/files/_files.py +13 -1
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/pyproject.toml +0 -3
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/.gitignore +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/AUTHORS +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/LICENSE.txt +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/README.md +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/__init__.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/_deffered_error.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/_exceptions.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/_misc.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/_preferences.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/_session.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/_talk_api.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/_theming.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/activity.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/apps.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/calendar_api.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/ex_app/__init__.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/ex_app/defs.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/ex_app/logger.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/ex_app/misc.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/ex_app/occ_commands.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/ex_app/persist_transformers_cache.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/ex_app/providers/__init__.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/ex_app/providers/providers.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/ex_app/providers/task_processing.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/ex_app/ui/__init__.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/ex_app/ui/files_actions.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/ex_app/ui/resources.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/ex_app/ui/top_menu.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/ex_app/ui/ui.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/ex_app/uvicorn_fastapi.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/files/__init__.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/files/files.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/files/files_async.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/files/sharing.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/loginflow_v2.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/nextcloud.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/notes.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/notifications.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/options.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/talk.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/talk_bot.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/user_status.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/users.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/users_groups.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/weather_status.py +0 -0
- {nc_py_api-0.20.0 → nc_py_api-0.20.2}/nc_py_api/webhooks.py +0 -0
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.20.2 - 2025-05-28]
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- ExApps(NC32+): field `sensitive` to the `SettingsField` class (Declarative Settings) and to the `PreferencesExAPI` class. #357
|
|
10
|
+
|
|
11
|
+
## [0.20.1 - 2025-05-06]
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- ExApps: `AppAPIAuthMiddleware`: correct handling of `disable_for` parameter. #357 Thanks to @BerengarWLehr
|
|
16
|
+
|
|
5
17
|
## [0.20.0 - 2025-04-28]
|
|
6
18
|
|
|
7
19
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nc-py-api
|
|
3
|
-
Version: 0.20.
|
|
3
|
+
Version: 0.20.2
|
|
4
4
|
Summary: Nextcloud Python Framework
|
|
5
5
|
Project-URL: Changelog, https://github.com/cloud-py-api/nc_py_api/blob/main/CHANGELOG.md
|
|
6
6
|
Project-URL: Documentation, https://cloud-py-api.github.io/nc_py_api/
|
|
@@ -61,6 +61,21 @@ class _BasicAppCfgPref:
|
|
|
61
61
|
if not not_fail:
|
|
62
62
|
raise e from None
|
|
63
63
|
|
|
64
|
+
def set_value(self, key: str, value: str, sensitive: bool | None = None) -> None:
|
|
65
|
+
"""Sets a value and if specified the sensitive flag for a key.
|
|
66
|
+
|
|
67
|
+
.. note:: A sensitive flag ensures key value are encrypted and truncated in Nextcloud logs.
|
|
68
|
+
Default for new records is ``False`` when sensitive is *unspecified*, if changes existing record and
|
|
69
|
+
sensitive is *unspecified* it will not change the existing `sensitive` flag.
|
|
70
|
+
"""
|
|
71
|
+
if not key:
|
|
72
|
+
raise ValueError("`key` parameter can not be empty")
|
|
73
|
+
require_capabilities("app_api", self._session.capabilities)
|
|
74
|
+
params: dict = {"configKey": key, "configValue": value}
|
|
75
|
+
if sensitive is not None:
|
|
76
|
+
params["sensitive"] = sensitive
|
|
77
|
+
self._session.ocs("POST", f"{self._session.ae_url}/{self._url_suffix}", json=params)
|
|
78
|
+
|
|
64
79
|
|
|
65
80
|
class _AsyncBasicAppCfgPref:
|
|
66
81
|
_url_suffix: str
|
|
@@ -104,72 +119,41 @@ class _AsyncBasicAppCfgPref:
|
|
|
104
119
|
if not not_fail:
|
|
105
120
|
raise e from None
|
|
106
121
|
|
|
122
|
+
async def set_value(self, key: str, value: str, sensitive: bool | None = None) -> None:
|
|
123
|
+
"""Sets a value and if specified the sensitive flag for a key.
|
|
124
|
+
|
|
125
|
+
.. note:: A sensitive flag ensures key value are encrypted and truncated in Nextcloud logs.
|
|
126
|
+
Default for new records is ``False`` when sensitive is *unspecified*, if changes existing record and
|
|
127
|
+
sensitive is *unspecified* it will not change the existing `sensitive` flag.
|
|
128
|
+
"""
|
|
129
|
+
if not key:
|
|
130
|
+
raise ValueError("`key` parameter can not be empty")
|
|
131
|
+
require_capabilities("app_api", await self._session.capabilities)
|
|
132
|
+
params: dict = {"configKey": key, "configValue": value}
|
|
133
|
+
if sensitive is not None:
|
|
134
|
+
params["sensitive"] = sensitive
|
|
135
|
+
await self._session.ocs("POST", f"{self._session.ae_url}/{self._url_suffix}", json=params)
|
|
136
|
+
|
|
107
137
|
|
|
108
138
|
class PreferencesExAPI(_BasicAppCfgPref):
|
|
109
|
-
"""User specific preferences API,
|
|
139
|
+
"""User specific preferences API, available as **nc.preferences_ex.<method>**."""
|
|
110
140
|
|
|
111
141
|
_url_suffix = "ex-app/preference"
|
|
112
142
|
|
|
113
|
-
def set_value(self, key: str, value: str) -> None:
|
|
114
|
-
"""Sets a value for a key."""
|
|
115
|
-
if not key:
|
|
116
|
-
raise ValueError("`key` parameter can not be empty")
|
|
117
|
-
require_capabilities("app_api", self._session.capabilities)
|
|
118
|
-
params = {"configKey": key, "configValue": value}
|
|
119
|
-
self._session.ocs("POST", f"{self._session.ae_url}/{self._url_suffix}", json=params)
|
|
120
|
-
|
|
121
143
|
|
|
122
144
|
class AsyncPreferencesExAPI(_AsyncBasicAppCfgPref):
|
|
123
145
|
"""User specific preferences API."""
|
|
124
146
|
|
|
125
147
|
_url_suffix = "ex-app/preference"
|
|
126
148
|
|
|
127
|
-
async def set_value(self, key: str, value: str) -> None:
|
|
128
|
-
"""Sets a value for a key."""
|
|
129
|
-
if not key:
|
|
130
|
-
raise ValueError("`key` parameter can not be empty")
|
|
131
|
-
require_capabilities("app_api", await self._session.capabilities)
|
|
132
|
-
params = {"configKey": key, "configValue": value}
|
|
133
|
-
await self._session.ocs("POST", f"{self._session.ae_url}/{self._url_suffix}", json=params)
|
|
134
|
-
|
|
135
149
|
|
|
136
150
|
class AppConfigExAPI(_BasicAppCfgPref):
|
|
137
|
-
"""Non-user(App) specific preferences API,
|
|
151
|
+
"""Non-user(App) specific preferences API, available as **nc.appconfig_ex.<method>**."""
|
|
138
152
|
|
|
139
153
|
_url_suffix = "ex-app/config"
|
|
140
154
|
|
|
141
|
-
def set_value(self, key: str, value: str, sensitive: bool | None = None) -> None:
|
|
142
|
-
"""Sets a value and if specified the sensitive flag for a key.
|
|
143
|
-
|
|
144
|
-
.. note:: A sensitive flag ensures key values are truncated in Nextcloud logs.
|
|
145
|
-
Default for new records is ``False`` when sensitive is *unspecified*, if changes existing record and
|
|
146
|
-
sensitive is *unspecified* it will not change the existing `sensitive` flag.
|
|
147
|
-
"""
|
|
148
|
-
if not key:
|
|
149
|
-
raise ValueError("`key` parameter can not be empty")
|
|
150
|
-
require_capabilities("app_api", self._session.capabilities)
|
|
151
|
-
params: dict = {"configKey": key, "configValue": value}
|
|
152
|
-
if sensitive is not None:
|
|
153
|
-
params["sensitive"] = sensitive
|
|
154
|
-
self._session.ocs("POST", f"{self._session.ae_url}/{self._url_suffix}", json=params)
|
|
155
|
-
|
|
156
155
|
|
|
157
156
|
class AsyncAppConfigExAPI(_AsyncBasicAppCfgPref):
|
|
158
157
|
"""Non-user(App) specific preferences API."""
|
|
159
158
|
|
|
160
159
|
_url_suffix = "ex-app/config"
|
|
161
|
-
|
|
162
|
-
async def set_value(self, key: str, value: str, sensitive: bool | None = None) -> None:
|
|
163
|
-
"""Sets a value and if specified the sensitive flag for a key.
|
|
164
|
-
|
|
165
|
-
.. note:: A sensitive flag ensures key values are truncated in Nextcloud logs.
|
|
166
|
-
Default for new records is ``False`` when sensitive is *unspecified*, if changes existing record and
|
|
167
|
-
sensitive is *unspecified* it will not change the existing `sensitive` flag.
|
|
168
|
-
"""
|
|
169
|
-
if not key:
|
|
170
|
-
raise ValueError("`key` parameter can not be empty")
|
|
171
|
-
require_capabilities("app_api", await self._session.capabilities)
|
|
172
|
-
params: dict = {"configKey": key, "configValue": value}
|
|
173
|
-
if sensitive is not None:
|
|
174
|
-
params["sensitive"] = sensitive
|
|
175
|
-
await self._session.ocs("POST", f"{self._session.ae_url}/{self._url_suffix}", json=params)
|
|
@@ -247,7 +247,7 @@ class AppAPIAuthMiddleware:
|
|
|
247
247
|
|
|
248
248
|
conn = HTTPConnection(scope)
|
|
249
249
|
url_path = conn.url.path.lstrip("/")
|
|
250
|
-
if not fnmatch.
|
|
250
|
+
if not any(fnmatch.fnmatch(url_path, i) for i in self._disable_for):
|
|
251
251
|
try:
|
|
252
252
|
scope["username"] = _request_sign_check(conn, AsyncNextcloudApp())
|
|
253
253
|
except HTTPException as exc:
|
|
@@ -48,6 +48,7 @@ class SettingsField:
|
|
|
48
48
|
description: str = ""
|
|
49
49
|
placeholder: str = ""
|
|
50
50
|
label: str = ""
|
|
51
|
+
sensitive: bool = False
|
|
51
52
|
notify = False # to be supported in future
|
|
52
53
|
|
|
53
54
|
@classmethod
|
|
@@ -74,6 +75,7 @@ class SettingsField:
|
|
|
74
75
|
"placeholder": self.placeholder,
|
|
75
76
|
"label": self.label,
|
|
76
77
|
"notify": self.notify,
|
|
78
|
+
"sensitive": self.sensitive,
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"""Helper functions for **FilesAPI** and **AsyncFilesAPI** classes."""
|
|
2
2
|
|
|
3
3
|
import enum
|
|
4
|
+
from datetime import datetime, timezone
|
|
4
5
|
from io import BytesIO
|
|
5
6
|
from json import dumps, loads
|
|
7
|
+
from typing import Any
|
|
6
8
|
from urllib.parse import unquote
|
|
7
9
|
from xml.etree import ElementTree
|
|
8
10
|
|
|
@@ -69,6 +71,16 @@ def get_propfind_properties(capabilities: dict) -> list:
|
|
|
69
71
|
return r
|
|
70
72
|
|
|
71
73
|
|
|
74
|
+
def _dav_literal(val: Any) -> str:
|
|
75
|
+
"""Return a string suitable for <d:literal>."""
|
|
76
|
+
if isinstance(val, datetime):
|
|
77
|
+
# make timezone-aware, force UTC, second precision
|
|
78
|
+
dt = val if val.tzinfo else val.replace(tzinfo=timezone.utc)
|
|
79
|
+
dt = dt.astimezone(timezone.utc).replace(microsecond=0)
|
|
80
|
+
return dt.isoformat().replace("+00:00", "Z") # 2025-03-10T12:34:56Z
|
|
81
|
+
return str(val)
|
|
82
|
+
|
|
83
|
+
|
|
72
84
|
def build_find_request(req: list, path: str | FsNode, user: str, capabilities: dict) -> ElementTree.Element:
|
|
73
85
|
path = path.user_path if isinstance(path, FsNode) else path
|
|
74
86
|
root = ElementTree.Element(
|
|
@@ -126,7 +138,7 @@ def build_search_req(xml_element_where, req: list) -> None:
|
|
|
126
138
|
ElementTree.SubElement(_, SEARCH_PROPERTIES_MAP[req.pop(0)])
|
|
127
139
|
_ = ElementTree.SubElement(_root, "d:literal")
|
|
128
140
|
value = req.pop(0)
|
|
129
|
-
_.text =
|
|
141
|
+
_.text = _dav_literal(value)
|
|
130
142
|
|
|
131
143
|
while len(req):
|
|
132
144
|
where_part = req.pop(0)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|