Source code for platypush.plugins.nextcloud

import os
from dataclasses import dataclass
from datetime import datetime
from enum import IntEnum
from typing import Optional, List, Union, Dict

from platypush.plugins import Plugin, action


[docs]@dataclass class ClientConfig: url: str username: str password: str def to_dict(self): return { 'url': self.url, 'username': self.username, 'password': self.password, }
[docs]class ShareType(IntEnum): USER = 0 GROUP = 1 PUBLIC_LINK = 3 EMAIL = 4 FEDERATED_CLOUD_SHARE = 6 CIRCLE = 7 TALK_CONVERSATION = 10
[docs]class Permission(IntEnum): READ = 1 UPDATE = 2 CREATE = 4 DELETE = 8 SHARE = 16 ALL = 31
[docs]class NextcloudPlugin(Plugin): """ Plugin to interact with a NextCloud instance. Requires: * **nextcloud-api-wrapper** (``pip install nextcloud-api-wrapper``) """
[docs] def __init__( self, url: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, **kwargs ): """ :param url: URL to the index of your default NextCloud instance. :param username: Default NextCloud username. :param password: Default NextCloud password. """ super().__init__(**kwargs) self.conf = ClientConfig(url=url, username=username, password=password) self._client = self._get_client(**self.conf.to_dict())
def _get_client( self, url: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, raise_on_empty: bool = False, ): from nextcloud import NextCloud if not url: if not self.conf.url: if raise_on_empty: raise AssertionError('No url/username/password provided') return None return NextCloud( endpoint=self.conf.url, user=self.conf.username, password=self.conf.password, ) return NextCloud(endpoint=url, user=username, password=password) @staticmethod def _get_permissions(permissions: Optional[List[str]]) -> int: int_perm = 0 for perm in permissions or []: perm = perm.upper() assert hasattr( Permission, perm ), 'Unknown permissions type: {}. Supported permissions: {}'.format( perm, [p.name.lower() for p in Permission] ) if perm == 'ALL': int_perm = Permission.ALL.value break int_perm += getattr(Permission, perm).value return int_perm @staticmethod def _get_share_type(share_type: str) -> int: share_type = share_type.upper() assert hasattr( ShareType, share_type ), 'Unknown share type: {}. Supported share types: {}'.format( share_type, [s.name.lower() for s in ShareType] ) return getattr(ShareType, share_type).value def _execute(self, server_args: dict, method: str, *args, **kwargs): client = self._get_client(**server_args) assert hasattr(client, method), 'No such NextCloud method: {}'.format(method) response = getattr(client, method)(*args, **kwargs) if response is None: return assert response.is_ok, 'Error on {method}({args}{sep}{kwargs}): {error}'.format( method=method, args=', '.join(args), sep=', ' if args and kwargs else '', kwargs=', '.join(['{}={}'.format(k, v) for k, v in kwargs.items()]), error=response.meta.get('message', '[No message]') if hasattr(response, 'meta') else response.raw.reason, ) return response.json_data
[docs] @action def get_activities( self, since: Optional[id] = None, limit: Optional[int] = None, object_type: Optional[str] = None, object_id: Optional[int] = None, sort: str = 'desc', **server_args ) -> List[str]: """ Get the list of recent activities on an instance. :param since: Only return the activities that have occurred since the specified ID. :param limit: Maximum number of activities to be returned (default: ``None``). :param object_type: Filter by object type. :param object_id: Only get the activities related to a specific ``object_id``. :param sort: Sort mode, ``asc`` for ascending, ``desc`` for descending (default: ``desc``). :param server_args: Override the default server settings (see :meth:`._get_client` arguments). :return: The list of selected activities. """ return self._execute( server_args, 'get_activities', since=since, limit=limit, object_type=object_type, object_id=object_id, sort=sort, )
[docs] @action def get_apps(self, **server_args) -> List[str]: """ Get the list of apps installed on a NextCloud instance. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). :return: The list of installed apps as strings. """ return self._execute(server_args, 'get_apps').get('apps', [])
[docs] @action def enable_app(self, app_id: Union[str, int], **server_args): """ Enable an app. :param app_id: App ID. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute(server_args, 'enable_app', app_id)
[docs] @action def disable_app(self, app_id: Union[str, int], **server_args): """ Disable an app. :param app_id: App ID. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute(server_args, 'disable_app', app_id)
[docs] @action def get_app(self, app_id: Union[str, int], **server_args) -> dict: """ Provides information about an application. :param app_id: App ID. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ return self._execute(server_args, 'get_app', app_id)
[docs] @action def get_capabilities(self, **server_args) -> dict: """ Returns the capabilities of the server. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ return self._execute(server_args, 'get_capabilities')
[docs] @action def add_group(self, group_id: Union[str], **server_args): """ Create a new group. :param group_id: New group unique ID. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute(server_args, 'add_group', group_id)
[docs] @action def delete_group(self, group_id: Union[str], **server_args): """ Delete a group. :param group_id: Group ID. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute(server_args, 'delete_group', group_id)
[docs] @action def get_group(self, group_id: Union[str], **server_args) -> dict: """ Get the information of a group. :param group_id: Group ID. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ return self._execute(server_args, 'get_group', group_id)
[docs] @action def get_groups( self, search: Optional[str] = None, limit: Optional[int] = None, offset: Optional[int] = None, **server_args ) -> List[str]: """ Search for groups. :param search: Search for groups matching the specified substring. :param limit: Maximum number of returned entries. :param offset: Start offset. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ return self._execute( server_args, 'get_groups', search=search, limit=limit, offset=offset ).get('groups', [])
[docs] @action def create_group_folder(self, name: str, **server_args): """ Create a new group folder. :param name: Name/path of the folder. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute(server_args, 'create_group_folder', name)
[docs] @action def delete_group_folder(self, folder_id: Union[int, str], **server_args): """ Delete a new group folder. :param folder_id: Folder ID. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute(server_args, 'delete_group_folder', folder_id)
[docs] @action def get_group_folder(self, folder_id: Union[int, str], **server_args) -> dict: """ Get a new group folder. :param folder_id: Folder ID. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ return self._execute(server_args, 'get_group_folder', folder_id)
[docs] @action def get_group_folders(self, **server_args) -> list: """ Get the list new group folder. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ return self._execute(server_args, 'get_group_folders')
[docs] @action def rename_group_folder( self, folder_id: Union[int, str], new_name: str, **server_args ): """ Rename a group folder. :param folder_id: Folder ID. :param new_name: New folder name. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute(server_args, 'rename_group_folder', folder_id, new_name)
[docs] @action def grant_access_to_group_folder( self, folder_id: Union[int, str], group_id: str, **server_args ): """ Grant access to a group folder to a given group. :param folder_id: Folder ID. :param group_id: Group ID. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute(server_args, 'grant_access_to_group_folder', folder_id, group_id)
[docs] @action def revoke_access_to_group_folder( self, folder_id: Union[int, str], group_id: str, **server_args ): """ Revoke access to a group folder to a given group. :param folder_id: Folder ID. :param group_id: Group ID. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute(server_args, 'revoke_access_to_group_folder', folder_id, group_id)
[docs] @action def set_group_folder_quota( self, folder_id: Union[int, str], quota: Optional[int], **server_args ): """ Set the quota of a group folder. :param folder_id: Folder ID. :param quota: Quota in bytes - set None for unlimited. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute( server_args, 'set_quota_of_group_folder', folder_id, quota if quota is not None else -3, )
[docs] @action def set_group_folder_permissions( self, folder_id: Union[int, str], group_id: str, permissions: List[str], **server_args ): """ Set the permissions on a folder for a group. :param folder_id: Folder ID. :param group_id: Group ID. :param permissions: New permissions, as a list including any of the following: - ``read`` - ``update`` - ``create`` - ``delete`` - ``share`` - ``all`` :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute( server_args, 'set_permissions_to_group_folder', folder_id, group_id, self._get_permissions(permissions), )
[docs] @action def get_notifications(self, **server_args) -> list: """ Get the list of notifications for the logged user. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ return self._execute(server_args, 'get_notifications')
[docs] @action def delete_notifications(self, **server_args): """ Delete all notifications for the logged user. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute(server_args, 'delete_all_notifications')
[docs] @action def get_notification(self, notification_id: int, **server_args) -> Union[dict, str]: """ Get the content of a notification. :param notification_id: Notification ID. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ return self._execute(server_args, 'get_notification', notification_id)
[docs] @action def delete_notification(self, notification_id: int, **server_args): """ Delete a notification. :param notification_id: Notification ID. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute(server_args, 'delete_notification', notification_id)
[docs] @action def create_share( self, path: str, share_type: str, share_with: Optional[str] = None, public_upload: bool = False, password: Optional[str] = None, permissions: Optional[List[str]] = None, **server_args ) -> dict: """ Share a file/folder with a user/group or a public link. :param path: Path to the resource to be shared. :param share_type: Share type. Supported values: - ``user`` - ``group`` - ``public_link`` - ``email`` - ``federated_cloud_share`` - ``circle`` - ``talk_conversation`` :param share_with: User/group ID, email or conversation ID the resource should be shared with. :param public_upload: Whether public upload to the shared folder is allowed (default: False). :param password: Optional password to protect the share. :param permissions: Share permissions, as a list including any of the following (default: ``read``): - ``read`` - ``update`` - ``create`` - ``delete`` - ``share`` - ``all`` :param server_args: Override the default server settings (see :meth:`._get_client` arguments). :return: The details of the newly created share. Example: .. code-block:: json { "id": "4", "share_type": 3, "uid_owner": "your_uid", "displayname_owner": "Your Name", "permissions": 17, "can_edit": true, "can_delete": true, "stime": 1599691325, "parent": null, "expiration": null, "token": "AbCdEfG0123456789", "uid_file_owner": "your_uid", "note": "", "label": "", "displayname_file_owner": "Your Name", "path": "/Shared Path", "item_type": "folder", "mimetype": "httpd/unix-directory", "storage_id": "home::your-uid", "storage": 2, "item_source": 13960, "file_source": 13960, "file_parent": 6, "file_target": "/Shared Path", "share_with": null, "share_with_displayname": "(Shared link)", "password": null, "send_password_by_talk": false, "url": "https://your-domain/nextcloud/index.php/s/AbCdEfG0123456789", "mail_send": 1, "hide_download": 0 } """ share_type = self._get_share_type(share_type) permissions = self._get_permissions(permissions or ['read']) return self._execute( server_args, 'create_share', path, share_type=share_type, share_with=share_with, public_upload=public_upload, password=password, permissions=permissions, )
[docs] @action def get_shares(self, **server_args) -> List[dict]: """ Get the list of shares available on the server. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). :return: List of available shares. Example: .. code-block:: json [ { "id": "4", "share_type": 3, "uid_owner": "your_uid", "displayname_owner": "Your Name", "permissions": 17, "can_edit": true, "can_delete": true, "stime": 1599691325, "parent": null, "expiration": null, "token": "AbCdEfG0123456789", "uid_file_owner": "your_uid", "note": "", "label": "", "displayname_file_owner": "Your Name", "path": "/Shared Path", "item_type": "folder", "mimetype": "httpd/unix-directory", "storage_id": "home::your-uid", "storage": 2, "item_source": 13960, "file_source": 13960, "file_parent": 6, "file_target": "/Shared Path", "share_with": null, "share_with_displayname": "(Shared link)", "password": null, "send_password_by_talk": false, "url": "https://your-domain/nextcloud/index.php/s/AbCdEfG0123456789", "mail_send": 1, "hide_download": 0 } ] """ return self._execute(server_args, 'get_shares')
[docs] @action def delete_share(self, share_id: int, **server_args): """ Remove the shared state of a resource. :param share_id: Share ID. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute(server_args, 'delete_share', str(share_id))
[docs] @action def get_share(self, share_id: int, **server_args) -> dict: """ Get the information of a shared resource. :param share_id: Share ID. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ return self._execute(server_args, 'get_share_info', str(share_id))
[docs] @action def update_share( self, share_id: int, public_upload: Optional[bool] = None, password: Optional[str] = None, permissions: Optional[List[str]] = None, expire_date: Optional[str] = None, **server_args ): """ Update the permissions of a shared resource. :param share_id: Share ID. :param public_upload: Whether public upload to the shared folder is allowed (default: False). :param password: Optional password to protect the share. :param permissions: Share permissions, as a list including any of the following (default: ``read``): - ``read`` - ``update`` - ``create`` - ``delete`` - ``share`` - ``all`` :param expire_date: Share expiration date, in the format ``YYYY-MM-DD``. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ if permissions: permissions = self._get_permissions(permissions) self._execute( server_args, 'update_share', share_id, public_upload=public_upload, password=password, permissions=permissions, expire_date=expire_date, )
[docs] @action def create_user(self, user_id: str, password: str, **server_args): """ Create a user. :param user_id: User ID/name. :param password: User password :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute(server_args, 'add_user', user_id, password)
[docs] @action def edit_user(self, user_id: str, properties: Dict[str, str], **server_args): """ Update a set of properties of a user. :param user_id: User ID/name. :param properties: Key-value pair of user attributes to be edited. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ for k, v in properties.items(): self._execute(server_args, 'edit_user', user_id, k, v)
[docs] @action def get_user(self, user_id: str, **server_args) -> dict: """ Get the details of a user. :param user_id: User ID/name. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). :return: User details. Example: .. code-block:: json { "enabled": true, "storageLocation": "/mnt/hd/nextcloud/user", "id": "user", "lastLogin": 1599693750000, "backend": "Database", "subadmin": [], "quota": { "free": 6869434515456, "used": 1836924441, "total": 6871271439897, "relative": 0.03, "quota": -3 }, "email": "info@yourdomain.com", "displayname": "Your Name", "phone": "+1234567890", "address": "", "website": "https://yourdomain.com", "twitter": "@You", "groups": [ "admin" ], "language": "en", "locale": "", "backendCapabilities": { "setDisplayName": true, "setPassword": true } } """ return self._execute(server_args, 'get_user', user_id)
[docs] @action def get_users( self, search: Optional[str] = None, limit: Optional[int] = None, offset: Optional[int] = None, **server_args ) -> List[str]: """ Get the list of users matching some search criteria. :param search: Return users matching the provided string. :param limit: Maximum number of results to be returned (default: no limit). :param offset: Search results offset (default: None). :return: List of the matched user IDs. """ return self._execute( server_args, 'get_users', search=search, limit=limit, offset=offset )
[docs] @action def delete_user(self, user_id: str, **server_args): """ Delete a user. :param user_id: User ID/name. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute(server_args, 'delete_user', user_id)
[docs] @action def enable_user(self, user_id: str, **server_args): """ Enable a user. :param user_id: User ID/name. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute(server_args, 'enable_user', user_id)
[docs] @action def disable_user(self, user_id: str, **server_args): """ Disable a user. :param user_id: User ID/name. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute(server_args, 'disable_user', user_id)
[docs] @action def add_to_group(self, user_id: str, group_id: str, **server_args): """ Add a user to a group. :param user_id: User ID/name. :param group_id: Group ID. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute(server_args, 'add_to_group', user_id, group_id)
[docs] @action def remove_from_group(self, user_id: str, group_id: str, **server_args): """ Remove a user from a group. :param user_id: User ID/name. :param group_id: Group ID. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute(server_args, 'remove_from_group', user_id, group_id)
[docs] @action def create_subadmin(self, user_id: str, group_id: str, **server_args): """ Add a user as a subadmin for a group. :param user_id: User ID/name. :param group_id: Group ID. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute(server_args, 'create_subadmin', user_id, group_id)
[docs] @action def remove_subadmin(self, user_id: str, group_id: str, **server_args): """ Remove a user as a subadmin from a group. :param user_id: User ID/name. :param group_id: Group ID. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ self._execute(server_args, 'remove_subadmin', user_id, group_id)
[docs] @action def get_subadmin_groups(self, user_id: str, **server_args) -> List[str]: """ Get the groups where a given user is subadmin. :param user_id: User ID/name. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). :return: List of matched groups as strings. """ return self._execute(server_args, 'get_subadmin_groups', user_id)
[docs] @action def create_folder(self, path: str, user_id: Optional[str] = None, **server_args): """ Create a folder. :param path: Path to the folder. :param user_id: User ID associated to the folder (default: same as the configured user). :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ user_id = user_id or server_args.get('username', self.conf.username) self._execute(server_args, 'create_folder', user_id, path)
[docs] @action def delete_path(self, path: str, user_id: Optional[str] = None, **server_args): """ Delete a file or folder. :param path: Path to the resource. :param user_id: User ID associated to the resource (default: same as the configured user). :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ user_id = user_id or server_args.get('username', self.conf.username) self._execute(server_args, 'delete_path', user_id, path)
[docs] @action def upload_file( self, remote_path: str, local_path: Optional[str] = None, content: Optional[str] = None, user_id: Optional[str] = None, timestamp: Optional[Union[datetime, int, str]] = None, **server_args ): """ Upload a file. :param remote_path: Path to the remote resource. :param local_path: If set, identifies the path to the local file to be uploaded. :param content: If set, create a new file with this content instead of uploading an existing file. :param user_id: User ID associated to the resource (default: same as the configured user). :param timestamp: File timestamp. If not specified it will be retrieved from the file info or set to ``now`` if ``content`` is specified. :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ user_id = user_id or server_args.get('username', self.conf.username) if isinstance(timestamp, str): timestamp = datetime.fromisoformat(timestamp) if isinstance(timestamp, datetime): timestamp = int(timestamp.timestamp()) assert (local_path or content) and not ( local_path and content ), 'Please specify either local_path or content' if local_path: method = 'upload_file' local_path = os.path.abspath(os.path.expanduser(local_path)) else: method = 'upload_file_contents' return self._execute( server_args, method, user_id, local_path or content, remote_path, timestamp=timestamp, )
[docs] @action def download_file( self, remote_path: str, local_path: str, user_id: Optional[str] = None, **server_args ): """ Download a file. :param remote_path: Path to the remote resource. :param local_path: Path to the local folder. :param user_id: User ID associated to the resource (default: same as the configured user). :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ user_id = user_id or server_args.get('username', self.conf.username) local_path = os.path.abspath(os.path.expanduser(local_path)) cur_dir = os.getcwd() try: os.chdir(local_path) return self._execute(server_args, 'download_file', user_id, remote_path) finally: os.chdir(cur_dir)
[docs] @action def list( self, path: str, user_id: Optional[str] = None, depth: int = 1, all_properties: bool = False, **server_args ) -> List[dict]: """ List the content of a folder on the NextCloud instance. :param path: Remote path. :param user_id: User ID associated to the resource (default: same as the configured user). :param depth: Search depth (default: 1). :param all_properties: Return all the file properties available (default: ``False``). :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ user_id = user_id or server_args.get('username', self.conf.username) return self._execute( server_args, 'list_folders', user_id, path, depth=depth, all_properties=all_properties, )
[docs] @action def list_favorites( self, path: Optional[str] = None, user_id: Optional[str] = None, **server_args ) -> List[dict]: """ List the favorite items for a user. :param path: Return only the favorites under this path. :param user_id: User ID associated to the resource (default: same as the configured user). :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ user_id = user_id or server_args.get('username', self.conf.username) return self._execute(server_args, 'list_folders', user_id, path)
[docs] @action def mark_favorite( self, path: Optional[str] = None, user_id: Optional[str] = None, **server_args ): """ Add a path to a user's favorites. :param path: Resource path. :param user_id: User ID associated to the resource (default: same as the configured user). :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ user_id = user_id or server_args.get('username', self.conf.username) self._execute(server_args, 'set_favorites', user_id, path)
[docs] @action def copy( self, path: str, destination: str, user_id: Optional[str] = None, overwrite: bool = False, **server_args ): """ Copy a resource to another path. :param path: Resource path. :param destination: Destination path. :param user_id: User ID associated to the resource (default: same as the configured user). :param overwrite: Set to ``True`` if you want to overwrite any existing file (default: ``False``). :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ user_id = user_id or server_args.get('username', self.conf.username) self._execute( server_args, 'copy_path', user_id, path, destination, overwrite=overwrite )
[docs] @action def move( self, path: str, destination: str, user_id: Optional[str] = None, overwrite: bool = False, **server_args ): """ Move a resource to another path. :param path: Resource path. :param destination: Destination path. :param user_id: User ID associated to the resource (default: same as the configured user). :param overwrite: Set to ``True`` if you want to overwrite any existing file (default: ``False``). :param server_args: Override the default server settings (see :meth:`._get_client` arguments). """ user_id = user_id or server_args.get('username', self.conf.username) self._execute( server_args, 'move_path', user_id, path, destination, overwrite=overwrite )
# vim:sw=4:ts=4:et: