From a1c439e6ff0c286cc92a7712560792055b630839 Mon Sep 17 00:00:00 2001 From: luffah Date: Wed, 5 May 2021 17:49:14 +0200 Subject: [PATCH] [IMP] File object methods : get_tags, add_tag, remove_tag ; Tag object methods : get_related_files --- src/nextcloud/api_wrappers/activity.py | 3 +- src/nextcloud/api_wrappers/apps.py | 4 +- src/nextcloud/api_wrappers/capabilities.py | 3 +- src/nextcloud/api_wrappers/group.py | 4 +- src/nextcloud/api_wrappers/group_folders.py | 3 +- src/nextcloud/api_wrappers/notifications.py | 5 +- src/nextcloud/api_wrappers/share.py | 2 +- src/nextcloud/api_wrappers/systemtags.py | 149 +++++++++++++++++--- src/nextcloud/api_wrappers/user.py | 4 +- src/nextcloud/api_wrappers/user_ldap.py | 3 +- src/nextcloud/api_wrappers/webdav.py | 17 +-- src/nextcloud/common/collections.py | 8 +- 12 files changed, 170 insertions(+), 35 deletions(-) diff --git a/src/nextcloud/api_wrappers/activity.py b/src/nextcloud/api_wrappers/activity.py index 141b3fe..8cd930e 100644 --- a/src/nextcloud/api_wrappers/activity.py +++ b/src/nextcloud/api_wrappers/activity.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- """ Activity API wrapper -See https://doc.owncloud.com/server/user_manual/apps/activity.html +See https://github.com/nextcloud/activity + https://doc.owncloud.com/server/user_manual/apps/activity.html https://doc.owncloud.com/server/developer_manual/core/apis/ """ from nextcloud import base diff --git a/src/nextcloud/api_wrappers/apps.py b/src/nextcloud/api_wrappers/apps.py index e3ac3be..8684006 100644 --- a/src/nextcloud/api_wrappers/apps.py +++ b/src/nextcloud/api_wrappers/apps.py @@ -1,7 +1,9 @@ # -*- coding: utf-8 -*- """ Apps API wrapper -See https://doc.owncloud.com/server/developer_manual/core/apis/provisioning-api.html +See https://docs.nextcloud.com/server/14/admin_manual/configuration_user/instruction_set_for_users.html + https://docs.nextcloud.com/server/14/admin_manual/configuration_user/user_provisioning_api.html + https://doc.owncloud.com/server/developer_manual/core/apis/provisioning-api.html """ from nextcloud import base diff --git a/src/nextcloud/api_wrappers/capabilities.py b/src/nextcloud/api_wrappers/capabilities.py index c77a97e..d790247 100644 --- a/src/nextcloud/api_wrappers/capabilities.py +++ b/src/nextcloud/api_wrappers/capabilities.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- """ Capabilities API wrapper -See https://doc.owncloud.com/server/developer_manual/core/apis/ocs-capabilities.html +See https://docs.nextcloud.com/server/14/developer_manual/client_apis/OCS/index.html#capabilities-api + https://doc.owncloud.com/server/developer_manual/core/apis/ocs-capabilities.html """ from nextcloud import base diff --git a/src/nextcloud/api_wrappers/group.py b/src/nextcloud/api_wrappers/group.py index d2fe59f..836197a 100644 --- a/src/nextcloud/api_wrappers/group.py +++ b/src/nextcloud/api_wrappers/group.py @@ -1,7 +1,9 @@ # -*- coding: utf-8 -*- """ Group API wrapper -See https://doc.owncloud.com/server/developer_manual/core/apis/provisioning-api.html +See https://docs.nextcloud.com/server/14/admin_manual/configuration_user/instruction_set_for_groups.html + https://docs.nextcloud.com/server/14/admin_manual/configuration_user/user_provisioning_api.html + https://doc.owncloud.com/server/developer_manual/core/apis/provisioning-api.html """ from nextcloud.base import ProvisioningApiWrapper diff --git a/src/nextcloud/api_wrappers/group_folders.py b/src/nextcloud/api_wrappers/group_folders.py index 8b3fb28..ef7d641 100644 --- a/src/nextcloud/api_wrappers/group_folders.py +++ b/src/nextcloud/api_wrappers/group_folders.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- """ GroupFolders API wrapper -See https://apps.nextcloud.com/apps/groupfolders +See https://github.com/nextcloud/groupfolders + https://apps.nextcloud.com/apps/groupfolders """ from nextcloud import base diff --git a/src/nextcloud/api_wrappers/notifications.py b/src/nextcloud/api_wrappers/notifications.py index c110f2d..f4974ff 100644 --- a/src/nextcloud/api_wrappers/notifications.py +++ b/src/nextcloud/api_wrappers/notifications.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- """ Notification API wrapper -See https://doc.owncloud.com/server/developer_manual/core/apis/ocs-notification-endpoint-v1.html -See https://docs.nextcloud.com/server/latest/developer_manual/client_apis/OCS/ocs-api-overview.html?highlight=notification#notifications +See https://github.com/nextcloud/notifications/ + https://doc.owncloud.com/server/developer_manual/core/apis/ocs-notification-endpoint-v1.html + https://docs.nextcloud.com/server/latest/developer_manual/client_apis/OCS/ocs-api-overview.html?highlight=notification#notifications """ from nextcloud import base diff --git a/src/nextcloud/api_wrappers/share.py b/src/nextcloud/api_wrappers/share.py index 0c91348..a295ccc 100644 --- a/src/nextcloud/api_wrappers/share.py +++ b/src/nextcloud/api_wrappers/share.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- """ Share API wrapper -See https://doc.owncloud.com/server/developer_manual/core/apis/ocs-share-api.html See https://docs.nextcloud.com/server/latest/developer_manual/client_apis/OCS/ocs-share-api.html + https://doc.owncloud.com/server/developer_manual/core/apis/ocs-share-api.html """ from nextcloud import base from nextcloud.codes import ShareType diff --git a/src/nextcloud/api_wrappers/systemtags.py b/src/nextcloud/api_wrappers/systemtags.py index 6dc1eba..55289e7 100644 --- a/src/nextcloud/api_wrappers/systemtags.py +++ b/src/nextcloud/api_wrappers/systemtags.py @@ -7,6 +7,7 @@ from nextcloud.base import WebDAVApiWrapper from nextcloud.common.collections import PropertySet from nextcloud.common.properties import Property as Prop +from nextcloud.api_wrappers import webdav class Tag(PropertySet): @@ -19,18 +20,111 @@ class Tag(PropertySet): Prop('oc:user-assignable', json='userAssignable', default=True) ] + def __repr__(self): + add_info = (' %s' % repr(self.display_name)) if hasattr( + self, 'display_name') else '' + return super(Tag, self).__repr__(add_info=add_info) + + def get_related_files(self, path=''): + """ + Get files related to current tag + :param path: (optionnal) a path to search in + """ + _id = int(self.id) + ret = self._wrapper.client.fetch_files_with_filter( + path=path, + filter_rules={'oc': {'systemtag': _id}} + ) + return ret.data or [] + + +class File(webdav.File): + + def _get_file_kwargs(self): + kwargs = {} + if not getattr(self, 'file_id', False): + kwargs['path'] = self._get_remote_path() + else: + kwargs['file_id'] = self.file_id + return kwargs + + def get_tags(self): + """ + Get tags related to current file + :returns : list + """ + kwargs = self._get_file_kwargs() + return self._wrapper.client.get_systemtags_relation(**kwargs) + + def add_tag(self, **kwargs): + """ + Assign tag to the current file + :param tag_id: tag id + :param tag_name: tag name (if tag_id in not provided) + :returns : False if failure + """ + kwargs.update(self._get_file_kwargs()) + resp = self._wrapper.client.add_systemtags_relation(**kwargs) + return resp.is_ok + + def remove_tag(self, **kwargs): + """ + Unassign tag to the current file + :param tag_id: tag id + :param tag_name: tag name (if tag_id in not provided) + :returns : False if failure + """ + kwargs.update(self._get_file_kwargs()) + resp = self._wrapper.client.remove_systemtags_relation(**kwargs) + return resp.is_ok + + +webdav.File = File class SystemTags(WebDAVApiWrapper): """ SystemTags API wrapper """ API_URL = '/remote.php/dav/systemtags' - def get_sytemtag(self, name, fields=None, json_output=None): + @classmethod + def _get_tags_from_response(cls, ret, one=False): + if ret.data: + ret = ret.data + if ret[0].href.endswith('/'): + ret = ret[1:] + else: + ret = [] + if one: + return ret[0] if ret else None + return ret + + def get_systemtags(self): + """ + Get list of all tags + + :returns: list + """ + return self._get_tags_from_response( + self.fetch_systemtags(json_output=False) + ) + + def get_systemtag(self, name): + """ + Return a nammed tag + + :returns: Tag + """ + return self._get_tags_from_response( + self.fetch_sytemtag(name, json_output=False), + one=True + ) + + def fetch_sytemtag(self, name, fields=None, json_output=None): """ Get attributes of a nammed tag :param name (str): tag name :param fields (str): field names - :returns: requester response with Tag in data + :returns: requester response with list in data """ if not fields: fields = Tag._fields @@ -40,21 +134,24 @@ def get_sytemtag(self, name, fields=None, json_output=None): })) if json_output is None: json_output = self.json_output - return Tag.from_response(resp, + return Tag.from_response(resp, wrapper=self, json_output=json_output, init_attrs=True, filtered=lambda t: t.display_name == name) - def get_systemtags(self): + def fetch_systemtags(self, json_output=None): """ - Get list of all tags + List of all tags - :returns: requester response with Tag in data + :returns: requester response with list in data """ resp = self.requester.propfind( data=Tag.build_xml_propfind(use_default=True) ) - return Tag.from_response(resp, json_output=self.json_output) + if json_output is None: + json_output = self.json_output + return Tag.from_response(resp, wrapper=self, + json_output=json_output) def create_systemtag(self, name, **kwargs): """ @@ -100,13 +197,13 @@ class SystemTagsRelation(WebDAVApiWrapper): def _get_fileid_from_path(self, path): """ Tricky function to fetch file """ resp = self.client.get_file_property(path, 'fileid') - id_ = None + _id = None if resp.data: - id_ = int(resp.data) - return id_ + _id = int(resp.data) + return _id def _get_systemtag_id_from_name(self, name): - resp = self.client.get_sytemtag(name, ['id'], json_output=False) + resp = self.client.fetch_sytemtag(name, ['id'], json_output=False) tag_id = None if resp.data: tag_id = int(resp.data[0].id) @@ -131,13 +228,31 @@ def get_systemtags_relation(self, file_id=None, **kwargs): :param file_id (int): file id found from file object :param path (str): if no file_id provided, path to file/folder - :returns: requester response with Tag in data + :returns: requester response with list in data + """ + return SystemTags._get_tags_from_response( + self.fetch_systemtags_relation(file_id=file_id, + json_output=False, **kwargs) + ) + + def fetch_systemtags_relation(self, file_id=None, json_output=None, **kwargs): + """ + Get all tags from a given file/folder + + :param file_id (int): file id found from file object + :param path (str): if no file_id provided, path to file/folder + + :returns: requester response with list in data """ file_id, = self._arguments_get(['file_id'], dict(file_id=file_id, **kwargs)) - data = Tag.build_xml_propfind() + data = Tag.build_xml_propfind(use_default=True) resp = self.requester.propfind(additional_url=file_id, data=data) - return Tag.from_response(resp, json_output=(self.json_output)) + return Tag.from_response(resp, + json_output=( + self.json_output if + json_output is None else json_output) + ) def remove_systemtags_relation(self, file_id=None, tag_id=None, **kwargs): """ @@ -155,7 +270,8 @@ def remove_systemtags_relation(self, file_id=None, tag_id=None, **kwargs): if not file_id: raise ValueError('No file found') if not tag_id: - raise ValueError('No tag found (%s)' % kwargs.get('tag_name', None)) + raise ValueError('No tag found (%s)' % + kwargs.get('tag_name', None)) resp = self.requester.delete(url=('{}/{}'.format(file_id, tag_id))) return resp @@ -175,7 +291,8 @@ def add_systemtags_relation(self, file_id=None, tag_id=None, **kwargs): if not file_id: raise ValueError('No file found') if not tag_id: - data = Tag.default_get(display_name=kwargs.get('tag_name'), **kwargs) + data = Tag.default_get( + display_name=kwargs.get('tag_name'), **kwargs) resp = self.requester.post( url=file_id, data=json.dumps(data), diff --git a/src/nextcloud/api_wrappers/user.py b/src/nextcloud/api_wrappers/user.py index 4071816..3458623 100644 --- a/src/nextcloud/api_wrappers/user.py +++ b/src/nextcloud/api_wrappers/user.py @@ -1,7 +1,9 @@ # -*- coding: utf-8 -*- """ User API wrapper -See https://doc.owncloud.com/server/developer_manual/core/apis/provisioning-api.html +See https://docs.nextcloud.com/server/14/admin_manual/configuration_user/instruction_set_for_users.html + https://docs.nextcloud.com/server/14/admin_manual/configuration_user/user_provisioning_api.html + https://doc.owncloud.com/server/developer_manual/core/apis/provisioning-api.html """ from nextcloud import base diff --git a/src/nextcloud/api_wrappers/user_ldap.py b/src/nextcloud/api_wrappers/user_ldap.py index 982233a..e8314f5 100644 --- a/src/nextcloud/api_wrappers/user_ldap.py +++ b/src/nextcloud/api_wrappers/user_ldap.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- """ User LDAP wrapper -See https://doc.owncloud.com/server/10.7/admin_manual/configuration/server/occ_commands/app_commands/ldap_integration_commands.html +See https://docs.nextcloud.com/server/14/admin_manual/configuration_user/user_auth_ldap_api.html + https://doc.owncloud.com/server/10.7/admin_manual/configuration/server/occ_commands/app_commands/ldap_integration_commands.html """ import re from nextcloud import base diff --git a/src/nextcloud/api_wrappers/webdav.py b/src/nextcloud/api_wrappers/webdav.py index 99f0349..bd84bbc 100644 --- a/src/nextcloud/api_wrappers/webdav.py +++ b/src/nextcloud/api_wrappers/webdav.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- """ WebDav API wrapper -See https://doc.owncloud.com/server/developer_manual/webdav_api/tags.html +See https://docs.nextcloud.com/server/14/developer_manual/client_apis/WebDAV/index.html + https://doc.owncloud.com/server/developer_manual/webdav_api/tags.html """ import re import os @@ -55,7 +56,7 @@ def _extract_resource_type(file_property): Prop('d:getlastmodified'), Prop('d:getetag'), Prop('d:getcontenttype'), - Prop('d:resourcetype', parse_xml_value=File._extract_resource_type), + Prop('d:resourcetype', parse_xml_value=(lambda p: File._extract_resource_type(p))), Prop('d:getcontentlength'), Prop('oc:id'), Prop('oc:fileid'), @@ -127,7 +128,7 @@ def list(self, subpath='', filter_rules=None): :returns: list of Files """ if filter_rules: - resp = self._wrapper.list_files_with_filter( + resp = self._wrapper.fetch_files_with_filter( path=self._get_remote_path(subpath), filter_rules=filter_rules ) @@ -387,7 +388,7 @@ def set_file_property(self, path, update_rules): update_rules : a dict { namespace: {key : value } } Returns: - requester response with File in data + requester response with list in data Note : check keys in nextcloud.common.properties.NAMESPACES_MAP for namespace codes @@ -396,7 +397,7 @@ def set_file_property(self, path, update_rules): data = File.build_xml_propupdate(update_rules) return self.requester.proppatch(additional_url=self._get_path(path), data=data) - def list_files_with_filter(self, path='', filter_rules=''): + def fetch_files_with_filter(self, path='', filter_rules=''): """ List files according to a filter @@ -405,7 +406,7 @@ def list_files_with_filter(self, path='', filter_rules=''): filter_rules : a dict { namespace: {key : value } } Returns: - requester response with File in data + requester response with list in data Note : check keys in nextcloud.common.properties.NAMESPACES_MAP for namespace codes @@ -438,9 +439,9 @@ def list_favorites(self, path=''): path (str): file or folder path to search favorite Returns: - requester response with File in data + requester response with list in data """ - return self.list_files_with_filter(path, {'oc': {'favorite': 1}}) + return self.fetch_files_with_filter(path, {'oc': {'favorite': 1}}) def get_file_property(self, path, field, ns='oc'): """ diff --git a/src/nextcloud/common/collections.py b/src/nextcloud/common/collections.py index 1212a3c..8eaf0c0 100644 --- a/src/nextcloud/common/collections.py +++ b/src/nextcloud/common/collections.py @@ -34,6 +34,9 @@ def _fetch_property(cls, key, attr='xml_key'): if getattr(k, attr) == key: return k + def __repr__(self, add_info=''): + return "<%s %s%s>" % (self.__class__.__name__, self.href, add_info) + def __init__(self, xml_data, init_attrs=False, wrapper=None): if init_attrs: for attr in self._attrs: @@ -101,7 +104,10 @@ def from_response(cls, resp, json_output=None, filtered=None, if filtered: if callable(filtered): attr_datas = [ - attr_data for attr_data in attr_datas if filtered(attr_data)] + attr_data + for attr_data in attr_datas + if filtered(attr_data) + ] resp.data = attr_datas if not json_output else [ attr_data.as_dict() for attr_data in attr_datas] return resp