Skip to content

Commit 707f1f1

Browse files
authored
initial commit for future SharesAPI (#42)
Signed-off-by: Alexander Piskun <[email protected]>
1 parent e3c5b80 commit 707f1f1

File tree

14 files changed

+300
-8
lines changed

14 files changed

+300
-8
lines changed

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ All notable changes to this project will be documented in this file.
66

77
### Changed
88

9-
- Updated documentation, description
10-
-
9+
- Updated documentation, description.
10+
- Updated `FsNode` class with properties for parsing permissions.
1111

1212
## [0.0.24 - 2023-07-18]
1313

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Project cloud-py-api was **abandoned** and divided into two parts:
2525
* User status manipulation
2626
* Weather status
2727
* ~~Nextcloud notifications support~~
28-
* ~~Shares operations support~~
28+
* Shares support
2929
* ~~Talk support~~
3030

3131
### Extended Features with installed App_ecosystem_v2:

docs/reference/Shares.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.. py:currentmodule:: nc_py_api.files_sharing
2+
3+
File Sharing
4+
============
5+
6+
The Shares API is universal for both modes and provides all the necessary methods for working with the Nextcloud Shares system.
7+
Refer to the **share examples** to see how to use them nicely.
8+
9+
.. autoclass:: Share
10+
:members:
11+
12+
.. autoclass:: FilesSharingAPI
13+
:members:

docs/reference/constants.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.. py:currentmodule:: nc_py_api
2+
3+
Constants
4+
=========
5+
6+
.. autoclass:: SharePermissions
7+
:members:
8+
9+
.. autoclass:: ShareType
10+
:members:
11+
12+
.. autoclass:: ShareStatus
13+
:members:

docs/reference/index.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,7 @@ Reference
44
.. toctree::
55
:maxdepth: 2
66

7-
Files.rst
8-
Session.rst
7+
Files
8+
Shares
9+
Session
10+
constants

nc_py_api/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
"""
44

55
from ._version import __version__
6-
from .constants import ApiScope, LogLvl
6+
from .constants import ApiScope, LogLvl, SharePermissions, ShareStatus, ShareType
77
from .exceptions import NextcloudException, NextcloudExceptionNotFound, check_error
88
from .files import FsNode, FsNodeInfo
9+
from .files_sharing import Share
910
from .integration_fastapi import (
1011
enable_heartbeat,
1112
nc_app,

nc_py_api/constants.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Common constants. Do not use them directly, all public ones are imported to __init__.py"""
22

3-
from enum import IntEnum
3+
from enum import IntEnum, IntFlag
44
from typing import TypedDict
55

66

@@ -22,6 +22,7 @@ class ApiScope(IntEnum):
2222
USER_STATUS = 11
2323
NOTIFICATIONS = 12
2424
WEATHER_STATUS = 13
25+
FILES_SHARING = 14
2526

2627

2728
class ApiScopesStruct(TypedDict):
@@ -34,3 +35,56 @@ class OCSRespond(IntEnum):
3435
RESPOND_UNAUTHORISED = 997
3536
RESPOND_NOT_FOUND = 998
3637
RESPOND_UNKNOWN_ERROR = 999
38+
39+
40+
class SharePermissions(IntFlag):
41+
"""The share permissions to be set"""
42+
43+
PERMISSION_READ = 1
44+
"""Access to read"""
45+
PERMISSION_UPDATE = 2
46+
"""Access to write"""
47+
PERMISSION_CREATE = 4
48+
"""Access to create new objects in the share"""
49+
PERMISSION_DELETE = 8
50+
"""Access to remove objects in the share"""
51+
PERMISSION_SHARE = 16
52+
"""Access to re-share objects in the share"""
53+
54+
55+
class ShareType(IntEnum):
56+
"""Type of the object that will receive share"""
57+
58+
TYPE_USER = 0
59+
"""Share to the user"""
60+
TYPE_GROUP = 1
61+
"""Share to the group"""
62+
TYPE_LINK = 3
63+
"""Share by link"""
64+
TYPE_EMAIL = 4
65+
"""Share by the email"""
66+
TYPE_REMOTE = 6
67+
"""Share to the Federation"""
68+
TYPE_CIRCLE = 7
69+
"""Share to the Nextcloud Circle"""
70+
TYPE_GUEST = 8
71+
"""Share to `Guest`"""
72+
TYPE_REMOTE_GROUP = 9
73+
"""Share to the Federation group"""
74+
TYPE_ROOM = 10
75+
"""Share to the Talk room"""
76+
TYPE_DECK = 11
77+
"""Share to the Nextcloud Deck"""
78+
TYPE_SCIENCE_MESH = 15
79+
"""Share to the Reva instance(Science Mesh)"""
80+
81+
82+
class ShareStatus(IntEnum):
83+
"""Status of the share"""
84+
85+
STATUS_PENDING = 0
86+
"""The share waits for acceptance"""
87+
STATUS_ACCEPTED = 1
88+
"""The share was for accepted"""
89+
STATUS_REJECTED = 2
90+
"""The share was for rejected"""

nc_py_api/files.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Nextcloud API for working with file system.
2+
Nextcloud API for working with the file system.
33
"""
44

55
import builtins
@@ -35,6 +35,7 @@ class FsNodeInfo(TypedDict):
3535
content_length: int
3636
last_modified: datetime
3737
permissions: str
38+
"""Permissions for the object."""
3839
favorite: bool
3940

4041

@@ -99,6 +100,38 @@ def __str__(self):
99100
f" last modified at {str(self.last_modified)} and {self.info['permissions']} permissions."
100101
)
101102

103+
@property
104+
def is_shared(self) -> bool:
105+
return self.info["permissions"].find("S") != -1
106+
107+
@property
108+
def is_shareable(self) -> bool:
109+
return self.info["permissions"].find("R") != -1
110+
111+
@property
112+
def is_mounted(self) -> bool:
113+
return self.info["permissions"].find("M") != -1
114+
115+
@property
116+
def is_readable(self) -> bool:
117+
return self.info["permissions"].find("G") != -1
118+
119+
@property
120+
def is_deletable(self) -> bool:
121+
return self.info["permissions"].find("D") != -1
122+
123+
@property
124+
def is_updatable(self) -> bool:
125+
if self.is_dir:
126+
return self.info["permissions"].find("NV") != -1
127+
return self.info["permissions"].find("W") != -1
128+
129+
@property
130+
def is_creatable(self) -> bool:
131+
if not self.is_dir:
132+
return False
133+
return self.info["permissions"].find("CK") != -1
134+
102135

103136
PROPFIND_PROPERTIES = [
104137
"d:resourcetype",
@@ -135,6 +168,8 @@ def __str__(self):
135168

136169

137170
class FilesAPI:
171+
"""This class provides all WebDAV functionality related to the files."""
172+
138173
def __init__(self, session: NcSessionBasic):
139174
self._session = session
140175

nc_py_api/files_sharing.py

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
"""
2+
Nextcloud API for working with the files shares.
3+
"""
4+
5+
from typing import Union
6+
7+
from ._session import NcSessionBasic
8+
from .constants import SharePermissions, ShareType
9+
from .files import FsNode
10+
from .misc import check_capabilities, require_capabilities
11+
12+
ENDPOINT_BASE = "/ocs/v1.php/apps/files_sharing/api/v1/"
13+
14+
15+
class Share:
16+
"""Class represents one Nextcloud Share."""
17+
18+
def __init__(self, raw_data: dict):
19+
self.raw_data = raw_data
20+
21+
@property
22+
def share_id(self) -> int:
23+
return int(self.raw_data["id"])
24+
25+
@property
26+
def type(self) -> ShareType:
27+
return ShareType(int(self.raw_data["share_type"]))
28+
29+
@property
30+
def permissions(self) -> SharePermissions:
31+
"""Recipient permissions"""
32+
33+
return SharePermissions(int(self.raw_data["permissions"]))
34+
35+
@property
36+
def url(self) -> str:
37+
return self.raw_data.get("url", "")
38+
39+
@property
40+
def path(self) -> str:
41+
return self.raw_data.get("path", "")
42+
43+
@property
44+
def label(self) -> str:
45+
return self.raw_data.get("label", "")
46+
47+
@property
48+
def note(self) -> str:
49+
return self.raw_data.get("note", "")
50+
51+
@property
52+
def mimetype(self) -> str:
53+
return self.raw_data.get("mimetype", "")
54+
55+
56+
class FilesSharingAPI:
57+
"""This class provides all File Sharing functionality."""
58+
59+
def __init__(self, session: NcSessionBasic):
60+
self._session = session
61+
62+
@property
63+
def available(self) -> bool:
64+
"""Returns True if the Nextcloud instance supports this feature, False otherwise."""
65+
66+
return not check_capabilities("files_sharing", self._session.capabilities)
67+
68+
def get_list(
69+
self, shared_with_me=False, reshares=False, subfiles=False, path: Union[str, FsNode] = ""
70+
) -> list[Share]:
71+
"""Returns lists of shares."""
72+
73+
require_capabilities("files_sharing", self._session.capabilities)
74+
path = path.path if isinstance(path, FsNode) else path
75+
params = {
76+
"shared_with_me": "true" if shared_with_me else "false",
77+
"reshares": "true" if reshares else "false",
78+
"subfiles": "true" if subfiles else "false",
79+
}
80+
if path:
81+
params["path"] = path
82+
result = self._session.ocs(method="GET", path=f"{ENDPOINT_BASE}/shares", params=params)
83+
return [Share(i) for i in result]
84+
85+
def create(
86+
self,
87+
path: Union[str, FsNode],
88+
permissions: SharePermissions,
89+
share_type: ShareType,
90+
share_with: str = "",
91+
**kwargs,
92+
) -> Share:
93+
"""Creates a new share.
94+
95+
:param path: The path of an existing file/directory.
96+
:param permissions: combination of the :py:class:`~nc_py_api.SharePermissions` object values.
97+
:param share_type: :py:class:`~nc_py_api.ShareType` value.
98+
:param share_with: the recipient of the shared object.
99+
:param kwargs: *Additionally supported arguments*
100+
101+
Additionally supported arguments:
102+
``public`` - boolean indicating should share be available for non-registered users.
103+
default = ``False``
104+
``password`` - string with password to protect share.
105+
default = ``""``
106+
``send_password_by_talk`` - boolean indicating should password be automatically delivered using Talk.
107+
default = ``False``
108+
``expire_date`` - py:class:`datetime` time when share should expire. `hours, minutes, seconds` are ignored.
109+
default = None
110+
``note`` - string with note, if any.
111+
default = ``""``
112+
``label`` - string with label, if any.
113+
default = ``""``
114+
"""
115+
116+
require_capabilities("files_sharing", self._session.capabilities)
117+
path = path.path if isinstance(path, FsNode) else path
118+
params = {
119+
"path": path,
120+
"permissions": int(permissions),
121+
"shareType": int(share_type),
122+
}
123+
if share_with:
124+
kwargs["shareWith"] = share_with
125+
if kwargs.get("public", False):
126+
params["publicUpload"] = "true"
127+
if "password" in kwargs:
128+
params["publicUpload"] = kwargs["password"]
129+
if kwargs.get("send_password_by_talk", False):
130+
params["sendPasswordByTalk"] = "true"
131+
if "expire_date" in kwargs:
132+
params["expireDate"] = kwargs["expire_date"].isoformat()
133+
if "note" in kwargs:
134+
params["note"] = kwargs["note"]
135+
if "label" in kwargs:
136+
params["label"] = kwargs["label"]
137+
return Share(self._session.ocs(method="POST", path=f"{ENDPOINT_BASE}/shares", params=params))
138+
139+
def delete(self, share_id: Union[int, Share]) -> None:
140+
"""Removes the given share.
141+
142+
:param share_id: The Share object or an ID of the share.
143+
"""
144+
145+
share_id = share_id.share_id if isinstance(share_id, Share) else share_id
146+
self._session.ocs(method="DELETE", path=f"{ENDPOINT_BASE}/shares/{share_id}")

nc_py_api/nextcloud.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from .apps import AppAPI
1212
from .constants import APP_V2_BASIC_URL, ApiScope, LogLvl
1313
from .files import FilesAPI
14+
from .files_sharing import FilesSharingAPI
1415
from .misc import check_capabilities
1516
from .preferences import PreferencesAPI
1617
from .theming import ThemingInfo, get_parsed_theme
@@ -24,6 +25,7 @@
2425
class NextcloudBasic(ABC):
2526
apps: AppAPI
2627
files: FilesAPI
28+
files_sharing: FilesSharingAPI
2729
preferences_api: PreferencesAPI
2830
users: UsersAPI
2931
users_groups: UserGroupsAPI
@@ -34,6 +36,7 @@ class NextcloudBasic(ABC):
3436
def _init_api(self, session: NcSessionBasic):
3537
self.apps = AppAPI(session)
3638
self.files = FilesAPI(session)
39+
self.files_sharing = FilesSharingAPI(session)
3740
self.preferences_api = PreferencesAPI(session)
3841
self.users = UsersAPI(session)
3942
self.users_groups = UserGroupsAPI(session)

0 commit comments

Comments
 (0)