nc-py-api 0.10.0__tar.gz → 0.11.0__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.10.0 → nc_py_api-0.11.0}/CHANGELOG.md +10 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/PKG-INFO +2 -2
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/__init__.py +1 -1
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/_version.py +1 -1
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/ex_app/providers/translations.py +9 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/files/__init__.py +61 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/files/_files.py +32 -10
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/files/files.py +64 -7
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/pyproject.toml +1 -1
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/.gitignore +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/AUTHORS +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/LICENSE.txt +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/README.md +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/_deffered_error.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/_exceptions.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/_misc.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/_preferences.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/_preferences_ex.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/_session.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/_talk_api.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/_theming.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/activity.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/apps.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/calendar.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/ex_app/__init__.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/ex_app/defs.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/ex_app/integration_fastapi.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/ex_app/misc.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/ex_app/persist_transformers_cache.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/ex_app/providers/__init__.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/ex_app/providers/providers.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/ex_app/providers/speech_to_text.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/ex_app/providers/text_processing.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/ex_app/ui/__init__.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/ex_app/ui/files_actions.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/ex_app/ui/resources.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/ex_app/ui/settings.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/ex_app/ui/top_menu.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/ex_app/ui/ui.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/ex_app/uvicorn_fastapi.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/files/sharing.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/nextcloud.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/notes.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/notifications.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/options.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/talk.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/talk_bot.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/user_status.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/users.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/users_groups.py +0 -0
- {nc_py_api-0.10.0 → nc_py_api-0.11.0}/nc_py_api/weather_status.py +0 -0
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.11.0 - 2024-02-17]
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Files: `lock` and `unlock` methods, lock file information to `FsNode`. #227
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- NextcloudApp: `MachineTranslation` provider registration - added optional `actionDetectLang` param. #229
|
|
14
|
+
|
|
5
15
|
## [0.10.0 - 2024-02-14]
|
|
6
16
|
|
|
7
17
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: nc-py-api
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
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/
|
|
@@ -10,7 +10,7 @@ License-Expression: BSD-3-Clause
|
|
|
10
10
|
License-File: AUTHORS
|
|
11
11
|
License-File: LICENSE.txt
|
|
12
12
|
Keywords: api,client,framework,library,nextcloud
|
|
13
|
-
Classifier: Development Status ::
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
14
|
Classifier: Intended Audience :: Developers
|
|
15
15
|
Classifier: License :: OSI Approved :: BSD License
|
|
16
16
|
Classifier: Operating System :: MacOS :: MacOS X
|
|
@@ -7,6 +7,6 @@ from ._exceptions import (
|
|
|
7
7
|
NextcloudMissingCapabilities,
|
|
8
8
|
)
|
|
9
9
|
from ._version import __version__
|
|
10
|
-
from .files import FilePermissions, FsNode
|
|
10
|
+
from .files import FilePermissions, FsNode, LockType
|
|
11
11
|
from .files.sharing import ShareType
|
|
12
12
|
from .nextcloud import AsyncNextcloud, AsyncNextcloudApp, Nextcloud, NextcloudApp
|
|
@@ -42,6 +42,11 @@ class TranslationsProvider:
|
|
|
42
42
|
"""Relative ExApp url which will be called by Nextcloud."""
|
|
43
43
|
return self._raw_data["action_handler"]
|
|
44
44
|
|
|
45
|
+
@property
|
|
46
|
+
def action_handler_detect_lang(self) -> str:
|
|
47
|
+
"""Relative ExApp url which will be called by Nextcloud to detect language."""
|
|
48
|
+
return self._raw_data.get("action_detect_lang", "")
|
|
49
|
+
|
|
45
50
|
def __repr__(self):
|
|
46
51
|
return f"<{self.__class__.__name__} name={self.name}, handler={self.action_handler}>"
|
|
47
52
|
|
|
@@ -59,6 +64,7 @@ class _TranslationsProviderAPI:
|
|
|
59
64
|
callback_url: str,
|
|
60
65
|
from_languages: dict[str, str],
|
|
61
66
|
to_languages: dict[str, str],
|
|
67
|
+
detect_lang_callback_url: str = "",
|
|
62
68
|
) -> None:
|
|
63
69
|
"""Registers or edit the Translations provider."""
|
|
64
70
|
require_capabilities("app_api", self._session.capabilities)
|
|
@@ -68,6 +74,7 @@ class _TranslationsProviderAPI:
|
|
|
68
74
|
"fromLanguages": from_languages,
|
|
69
75
|
"toLanguages": to_languages,
|
|
70
76
|
"actionHandler": callback_url,
|
|
77
|
+
"actionDetectLang": detect_lang_callback_url,
|
|
71
78
|
}
|
|
72
79
|
self._session.ocs("POST", f"{self._session.ae_url}/{_EP_SUFFIX}", json=params)
|
|
73
80
|
|
|
@@ -114,6 +121,7 @@ class _AsyncTranslationsProviderAPI:
|
|
|
114
121
|
callback_url: str,
|
|
115
122
|
from_languages: dict[str, str],
|
|
116
123
|
to_languages: dict[str, str],
|
|
124
|
+
detect_lang_callback_url: str = "",
|
|
117
125
|
) -> None:
|
|
118
126
|
"""Registers or edit the Translations provider."""
|
|
119
127
|
require_capabilities("app_api", await self._session.capabilities)
|
|
@@ -123,6 +131,7 @@ class _AsyncTranslationsProviderAPI:
|
|
|
123
131
|
"fromLanguages": from_languages,
|
|
124
132
|
"toLanguages": to_languages,
|
|
125
133
|
"actionHandler": callback_url,
|
|
134
|
+
"actionDetectLang": detect_lang_callback_url,
|
|
126
135
|
}
|
|
127
136
|
await self._session.ocs("POST", f"{self._session.ae_url}/{_EP_SUFFIX}", json=params)
|
|
128
137
|
|
|
@@ -9,6 +9,63 @@ import warnings
|
|
|
9
9
|
from .. import _misc
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
class LockType(enum.IntEnum):
|
|
13
|
+
"""Nextcloud File Locks types."""
|
|
14
|
+
|
|
15
|
+
MANUAL_LOCK = 0
|
|
16
|
+
COLLABORATIVE_LOCK = 1
|
|
17
|
+
WEBDAV_TOKEN = 2
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclasses.dataclass
|
|
21
|
+
class FsNodeLockInfo:
|
|
22
|
+
"""File Lock information if Nextcloud `files_lock` is enabled."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, **kwargs):
|
|
25
|
+
self._is_locked = bool(int(kwargs.get("is_locked", False)))
|
|
26
|
+
self._lock_owner_type = LockType(int(kwargs.get("lock_owner_type", 0)))
|
|
27
|
+
self._lock_owner = kwargs.get("lock_owner", "")
|
|
28
|
+
self._owner_display_name = kwargs.get("owner_display_name", "")
|
|
29
|
+
self._owner_editor = kwargs.get("lock_owner_editor", "")
|
|
30
|
+
self._lock_time = int(kwargs.get("lock_time", 0))
|
|
31
|
+
self._lock_ttl = int(kwargs.get("_lock_ttl", 0))
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def is_locked(self) -> bool:
|
|
35
|
+
"""Returns ``True`` if the file is locked, ``False`` otherwise."""
|
|
36
|
+
return self._is_locked
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def type(self) -> LockType:
|
|
40
|
+
"""Type of the lock."""
|
|
41
|
+
return LockType(self._lock_owner_type)
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def owner(self) -> str:
|
|
45
|
+
"""User id of the lock owner."""
|
|
46
|
+
return self._lock_owner
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def owner_display_name(self) -> str:
|
|
50
|
+
"""Display name of the lock owner."""
|
|
51
|
+
return self._owner_display_name
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def owner_editor(self) -> str:
|
|
55
|
+
"""App id of an app owned lock to allow clients to suggest joining the collaborative editing session."""
|
|
56
|
+
return self._owner_editor
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def lock_creation_time(self) -> datetime.datetime:
|
|
60
|
+
"""Lock creation time."""
|
|
61
|
+
return datetime.datetime.utcfromtimestamp(self._lock_time).replace(tzinfo=datetime.timezone.utc)
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def lock_ttl(self) -> int:
|
|
65
|
+
"""TTL of the lock in seconds staring from the creation time. A value of 0 means the timeout is infinite."""
|
|
66
|
+
return self._lock_ttl
|
|
67
|
+
|
|
68
|
+
|
|
12
69
|
@dataclasses.dataclass
|
|
13
70
|
class FsNodeInfo:
|
|
14
71
|
"""Extra FS object attributes from Nextcloud."""
|
|
@@ -116,11 +173,15 @@ class FsNode:
|
|
|
116
173
|
info: FsNodeInfo
|
|
117
174
|
"""Additional extra information for the object"""
|
|
118
175
|
|
|
176
|
+
lock_info: FsNodeLockInfo
|
|
177
|
+
"""Class describing `lock` information if any."""
|
|
178
|
+
|
|
119
179
|
def __init__(self, full_path: str, **kwargs):
|
|
120
180
|
self.full_path = full_path
|
|
121
181
|
self.file_id = kwargs.get("file_id", "")
|
|
122
182
|
self.etag = kwargs.get("etag", "")
|
|
123
183
|
self.info = FsNodeInfo(**kwargs)
|
|
184
|
+
self.lock_info = FsNodeLockInfo(**kwargs)
|
|
124
185
|
|
|
125
186
|
@property
|
|
126
187
|
def is_dir(self) -> bool:
|
|
@@ -10,7 +10,7 @@ import xmltodict
|
|
|
10
10
|
from httpx import Response
|
|
11
11
|
|
|
12
12
|
from .._exceptions import NextcloudException, check_error
|
|
13
|
-
from .._misc import clear_from_params_empty
|
|
13
|
+
from .._misc import check_capabilities, clear_from_params_empty
|
|
14
14
|
from . import FsNode, SystemTag
|
|
15
15
|
|
|
16
16
|
PROPFIND_PROPERTIES = [
|
|
@@ -29,13 +29,16 @@ PROPFIND_PROPERTIES = [
|
|
|
29
29
|
"oc:share-types",
|
|
30
30
|
"oc:favorite",
|
|
31
31
|
"nc:is-encrypted",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
PROPFIND_LOCKING_PROPERTIES = [
|
|
32
35
|
"nc:lock",
|
|
33
36
|
"nc:lock-owner-displayname",
|
|
34
37
|
"nc:lock-owner",
|
|
35
38
|
"nc:lock-owner-type",
|
|
36
|
-
"nc:lock-owner-editor",
|
|
37
|
-
"nc:lock-time",
|
|
38
|
-
"nc:lock-timeout",
|
|
39
|
+
"nc:lock-owner-editor", # App id of an app owned lock
|
|
40
|
+
"nc:lock-time", # Timestamp of the log creation time
|
|
41
|
+
"nc:lock-timeout", # TTL of the lock in seconds staring from the creation time
|
|
39
42
|
]
|
|
40
43
|
|
|
41
44
|
SEARCH_PROPERTIES_MAP = {
|
|
@@ -57,7 +60,14 @@ class PropFindType(enum.IntEnum):
|
|
|
57
60
|
VERSIONS_FILE_ID = 3
|
|
58
61
|
|
|
59
62
|
|
|
60
|
-
def
|
|
63
|
+
def get_propfind_properties(capabilities: dict) -> list:
|
|
64
|
+
r = PROPFIND_PROPERTIES
|
|
65
|
+
if not check_capabilities("files.locking", capabilities):
|
|
66
|
+
r += PROPFIND_LOCKING_PROPERTIES
|
|
67
|
+
return r
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def build_find_request(req: list, path: str | FsNode, user: str, capabilities: dict) -> ElementTree.Element:
|
|
61
71
|
path = path.user_path if isinstance(path, FsNode) else path
|
|
62
72
|
root = ElementTree.Element(
|
|
63
73
|
"d:searchrequest",
|
|
@@ -65,7 +75,7 @@ def build_find_request(req: list, path: str | FsNode, user: str) -> ElementTree.
|
|
|
65
75
|
)
|
|
66
76
|
xml_search = ElementTree.SubElement(root, "d:basicsearch")
|
|
67
77
|
xml_select_prop = ElementTree.SubElement(ElementTree.SubElement(xml_search, "d:select"), "d:prop")
|
|
68
|
-
for i in
|
|
78
|
+
for i in get_propfind_properties(capabilities):
|
|
69
79
|
ElementTree.SubElement(xml_select_prop, i)
|
|
70
80
|
xml_from_scope = ElementTree.SubElement(ElementTree.SubElement(xml_search, "d:from"), "d:scope")
|
|
71
81
|
href = f"/files/{user}/{path.removeprefix('/')}"
|
|
@@ -76,7 +86,9 @@ def build_find_request(req: list, path: str | FsNode, user: str) -> ElementTree.
|
|
|
76
86
|
return root
|
|
77
87
|
|
|
78
88
|
|
|
79
|
-
def build_list_by_criteria_req(
|
|
89
|
+
def build_list_by_criteria_req(
|
|
90
|
+
properties: list[str] | None, tags: list[int | SystemTag] | None, capabilities: dict
|
|
91
|
+
) -> ElementTree.Element:
|
|
80
92
|
if not properties and not tags:
|
|
81
93
|
raise ValueError("Either specify 'properties' or 'tags' to filter results.")
|
|
82
94
|
root = ElementTree.Element(
|
|
@@ -84,7 +96,7 @@ def build_list_by_criteria_req(properties: list[str] | None, tags: list[int | Sy
|
|
|
84
96
|
attrib={"xmlns:d": "DAV:", "xmlns:oc": "http://owncloud.org/ns", "xmlns:nc": "http://nextcloud.org/ns"},
|
|
85
97
|
)
|
|
86
98
|
prop = ElementTree.SubElement(root, "d:prop")
|
|
87
|
-
for i in
|
|
99
|
+
for i in get_propfind_properties(capabilities):
|
|
88
100
|
ElementTree.SubElement(prop, i)
|
|
89
101
|
xml_filter_rules = ElementTree.SubElement(root, "oc:filter-rules")
|
|
90
102
|
if properties and "favorite" in properties:
|
|
@@ -243,7 +255,7 @@ def etag_fileid_from_response(response: Response) -> dict:
|
|
|
243
255
|
return {"etag": response.headers.get("OC-Etag", ""), "file_id": response.headers["OC-FileId"]}
|
|
244
256
|
|
|
245
257
|
|
|
246
|
-
def _parse_record(full_path: str, prop_stats: list[dict]) -> FsNode:
|
|
258
|
+
def _parse_record(full_path: str, prop_stats: list[dict]) -> FsNode: # noqa pylint: disable = too-many-branches
|
|
247
259
|
fs_node_args = {}
|
|
248
260
|
for prop_stat in prop_stats:
|
|
249
261
|
if str(prop_stat.get("d:status", "")).find("200 OK") == -1:
|
|
@@ -274,7 +286,17 @@ def _parse_record(full_path: str, prop_stats: list[dict]) -> FsNode:
|
|
|
274
286
|
fs_node_args["trashbin_original_location"] = prop["nc:trashbin-original-location"]
|
|
275
287
|
if "nc:trashbin-deletion-time" in prop_keys:
|
|
276
288
|
fs_node_args["trashbin_deletion_time"] = prop["nc:trashbin-deletion-time"]
|
|
277
|
-
|
|
289
|
+
for k, v in {
|
|
290
|
+
"nc:lock": "is_locked",
|
|
291
|
+
"nc:lock-owner-type": "lock_owner_type",
|
|
292
|
+
"nc:lock-owner": "lock_owner",
|
|
293
|
+
"nc:lock-owner-displayname": "lock_owner_displayname",
|
|
294
|
+
"nc:lock-owner-editor": "lock_owner_editor",
|
|
295
|
+
"nc:lock-time": "lock_time",
|
|
296
|
+
"nc:lock-timeout": "lock_ttl",
|
|
297
|
+
}.items():
|
|
298
|
+
if k in prop_keys and prop[k] is not None:
|
|
299
|
+
fs_node_args[v] = prop[k]
|
|
278
300
|
return FsNode(full_path, **fs_node_args)
|
|
279
301
|
|
|
280
302
|
|
|
@@ -10,7 +10,7 @@ from httpx import Headers
|
|
|
10
10
|
from .._exceptions import NextcloudException, NextcloudExceptionNotFound, check_error
|
|
11
11
|
from .._misc import random_string, require_capabilities
|
|
12
12
|
from .._session import AsyncNcSessionBasic, NcSessionBasic
|
|
13
|
-
from . import FsNode, SystemTag
|
|
13
|
+
from . import FsNode, LockType, SystemTag
|
|
14
14
|
from ._files import (
|
|
15
15
|
PROPFIND_PROPERTIES,
|
|
16
16
|
PropFindType,
|
|
@@ -25,6 +25,7 @@ from ._files import (
|
|
|
25
25
|
dav_get_obj_path,
|
|
26
26
|
element_tree_as_str,
|
|
27
27
|
etag_fileid_from_response,
|
|
28
|
+
get_propfind_properties,
|
|
28
29
|
lf_parse_webdav_response,
|
|
29
30
|
)
|
|
30
31
|
from .sharing import _AsyncFilesSharingAPI, _FilesSharingAPI
|
|
@@ -50,7 +51,7 @@ class FilesAPI:
|
|
|
50
51
|
"""
|
|
51
52
|
if exclude_self and not depth:
|
|
52
53
|
raise ValueError("Wrong input parameters, query will return nothing.")
|
|
53
|
-
properties =
|
|
54
|
+
properties = get_propfind_properties(self._session.capabilities)
|
|
54
55
|
path = path.user_path if isinstance(path, FsNode) else path
|
|
55
56
|
return self._listdir(self._session.user, path, properties=properties, depth=depth, exclude_self=exclude_self)
|
|
56
57
|
|
|
@@ -76,7 +77,7 @@ class FilesAPI:
|
|
|
76
77
|
:param path: path where to search from. Default = **""**.
|
|
77
78
|
"""
|
|
78
79
|
# `req` possible keys: "name", "mime", "last_modified", "size", "favorite", "fileid"
|
|
79
|
-
root = build_find_request(req, path, self._session.user)
|
|
80
|
+
root = build_find_request(req, path, self._session.user, self._session.capabilities)
|
|
80
81
|
webdav_response = self._session.adapter_dav.request(
|
|
81
82
|
"SEARCH", "", content=element_tree_as_str(root), headers={"Content-Type": "text/xml"}
|
|
82
83
|
)
|
|
@@ -248,7 +249,7 @@ class FilesAPI:
|
|
|
248
249
|
Supported values: **favorite**
|
|
249
250
|
:param tags: List of ``tags ids`` or ``SystemTag`` that should have been set for the file.
|
|
250
251
|
"""
|
|
251
|
-
root = build_list_by_criteria_req(properties, tags)
|
|
252
|
+
root = build_list_by_criteria_req(properties, tags, self._session.capabilities)
|
|
252
253
|
webdav_response = self._session.adapter_dav.request(
|
|
253
254
|
"REPORT", dav_get_obj_path(self._session.user), content=element_tree_as_str(root)
|
|
254
255
|
)
|
|
@@ -396,6 +397,34 @@ class FilesAPI:
|
|
|
396
397
|
"""Removes Tag from a file/directory."""
|
|
397
398
|
self._file_change_tag_state(file_id, tag_id, False)
|
|
398
399
|
|
|
400
|
+
def lock(self, path: FsNode | str, lock_type: LockType = LockType.MANUAL_LOCK) -> None:
|
|
401
|
+
"""Locks the file.
|
|
402
|
+
|
|
403
|
+
.. note:: Exception codes: 423 - existing lock present.
|
|
404
|
+
"""
|
|
405
|
+
require_capabilities("files.locking", self._session.capabilities)
|
|
406
|
+
full_path = dav_get_obj_path(self._session.user, path.user_path if isinstance(path, FsNode) else path)
|
|
407
|
+
response = self._session.adapter_dav.request(
|
|
408
|
+
"LOCK",
|
|
409
|
+
quote(full_path),
|
|
410
|
+
headers={"X-User-Lock": "1", "X-User-Lock-Type": str(lock_type.value)},
|
|
411
|
+
)
|
|
412
|
+
check_error(response, f"lock: user={self._session.user}, path={full_path}")
|
|
413
|
+
|
|
414
|
+
def unlock(self, path: FsNode | str) -> None:
|
|
415
|
+
"""Unlocks the file.
|
|
416
|
+
|
|
417
|
+
.. note:: Exception codes: 412 - the file is not locked, 423 - the lock is owned by another user.
|
|
418
|
+
"""
|
|
419
|
+
require_capabilities("files.locking", self._session.capabilities)
|
|
420
|
+
full_path = dav_get_obj_path(self._session.user, path.user_path if isinstance(path, FsNode) else path)
|
|
421
|
+
response = self._session.adapter_dav.request(
|
|
422
|
+
"UNLOCK",
|
|
423
|
+
quote(full_path),
|
|
424
|
+
headers={"X-User-Lock": "1"},
|
|
425
|
+
)
|
|
426
|
+
check_error(response, f"unlock: user={self._session.user}, path={full_path}")
|
|
427
|
+
|
|
399
428
|
def _file_change_tag_state(self, file_id: FsNode | int, tag_id: SystemTag | int, tag_state: bool) -> None:
|
|
400
429
|
fs_object = file_id.info.fileid if isinstance(file_id, FsNode) else file_id
|
|
401
430
|
tag = tag_id.tag_id if isinstance(tag_id, SystemTag) else tag_id
|
|
@@ -493,7 +522,7 @@ class AsyncFilesAPI:
|
|
|
493
522
|
"""
|
|
494
523
|
if exclude_self and not depth:
|
|
495
524
|
raise ValueError("Wrong input parameters, query will return nothing.")
|
|
496
|
-
properties =
|
|
525
|
+
properties = get_propfind_properties(await self._session.capabilities)
|
|
497
526
|
path = path.user_path if isinstance(path, FsNode) else path
|
|
498
527
|
return await self._listdir(
|
|
499
528
|
await self._session.user, path, properties=properties, depth=depth, exclude_self=exclude_self
|
|
@@ -521,7 +550,7 @@ class AsyncFilesAPI:
|
|
|
521
550
|
:param path: path where to search from. Default = **""**.
|
|
522
551
|
"""
|
|
523
552
|
# `req` possible keys: "name", "mime", "last_modified", "size", "favorite", "fileid"
|
|
524
|
-
root = build_find_request(req, path, await self._session.user)
|
|
553
|
+
root = build_find_request(req, path, await self._session.user, await self._session.capabilities)
|
|
525
554
|
webdav_response = await self._session.adapter_dav.request(
|
|
526
555
|
"SEARCH", "", content=element_tree_as_str(root), headers={"Content-Type": "text/xml"}
|
|
527
556
|
)
|
|
@@ -695,7 +724,7 @@ class AsyncFilesAPI:
|
|
|
695
724
|
Supported values: **favorite**
|
|
696
725
|
:param tags: List of ``tags ids`` or ``SystemTag`` that should have been set for the file.
|
|
697
726
|
"""
|
|
698
|
-
root = build_list_by_criteria_req(properties, tags)
|
|
727
|
+
root = build_list_by_criteria_req(properties, tags, await self._session.capabilities)
|
|
699
728
|
webdav_response = await self._session.adapter_dav.request(
|
|
700
729
|
"REPORT", dav_get_obj_path(await self._session.user), content=element_tree_as_str(root)
|
|
701
730
|
)
|
|
@@ -848,6 +877,34 @@ class AsyncFilesAPI:
|
|
|
848
877
|
"""Removes Tag from a file/directory."""
|
|
849
878
|
await self._file_change_tag_state(file_id, tag_id, False)
|
|
850
879
|
|
|
880
|
+
async def lock(self, path: FsNode | str, lock_type: LockType = LockType.MANUAL_LOCK) -> None:
|
|
881
|
+
"""Locks the file.
|
|
882
|
+
|
|
883
|
+
.. note:: Exception codes: 423 - existing lock present.
|
|
884
|
+
"""
|
|
885
|
+
require_capabilities("files.locking", await self._session.capabilities)
|
|
886
|
+
full_path = dav_get_obj_path(await self._session.user, path.user_path if isinstance(path, FsNode) else path)
|
|
887
|
+
response = await self._session.adapter_dav.request(
|
|
888
|
+
"LOCK",
|
|
889
|
+
quote(full_path),
|
|
890
|
+
headers={"X-User-Lock": "1", "X-User-Lock-Type": str(lock_type.value)},
|
|
891
|
+
)
|
|
892
|
+
check_error(response, f"lock: user={self._session.user}, path={full_path}")
|
|
893
|
+
|
|
894
|
+
async def unlock(self, path: FsNode | str) -> None:
|
|
895
|
+
"""Unlocks the file.
|
|
896
|
+
|
|
897
|
+
.. note:: Exception codes: 412 - the file is not locked, 423 - the lock is owned by another user.
|
|
898
|
+
"""
|
|
899
|
+
require_capabilities("files.locking", await self._session.capabilities)
|
|
900
|
+
full_path = dav_get_obj_path(await self._session.user, path.user_path if isinstance(path, FsNode) else path)
|
|
901
|
+
response = await self._session.adapter_dav.request(
|
|
902
|
+
"UNLOCK",
|
|
903
|
+
quote(full_path),
|
|
904
|
+
headers={"X-User-Lock": "1"},
|
|
905
|
+
)
|
|
906
|
+
check_error(response, f"unlock: user={self._session.user}, path={full_path}")
|
|
907
|
+
|
|
851
908
|
async def _file_change_tag_state(self, file_id: FsNode | int, tag_id: SystemTag | int, tag_state: bool) -> None:
|
|
852
909
|
fs_object = file_id.info.fileid if isinstance(file_id, FsNode) else file_id
|
|
853
910
|
tag = tag_id.tag_id if isinstance(tag_id, SystemTag) else tag_id
|
|
@@ -21,7 +21,7 @@ authors = [
|
|
|
21
21
|
]
|
|
22
22
|
requires-python = ">=3.10"
|
|
23
23
|
classifiers = [
|
|
24
|
-
"Development Status ::
|
|
24
|
+
"Development Status :: 4 - Beta",
|
|
25
25
|
"Intended Audience :: Developers",
|
|
26
26
|
"License :: OSI Approved :: BSD License",
|
|
27
27
|
"Operating System :: MacOS :: MacOS X",
|
|
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
|