changeset 3314:5887fb414758

component file sharing: add/parse affiliation when possible
author Goffi <goffi@goffi.org>
date Fri, 17 Jul 2020 13:00:10 +0200
parents 624c60293deb
children 53b229761c9d
files sat/plugins/plugin_comp_file_sharing.py sat/plugins/plugin_exp_invitation_file.py sat/plugins/plugin_xep_0234.py sat/plugins/plugin_xep_0264.py sat/plugins/plugin_xep_0329.py
diffstat 5 files changed, 110 insertions(+), 39 deletions(-) [+]
line wrap: on
line diff
--- a/sat/plugins/plugin_comp_file_sharing.py	Fri Jul 17 12:58:57 2020 +0200
+++ b/sat/plugins/plugin_comp_file_sharing.py	Fri Jul 17 13:00:10 2020 +0200
@@ -23,7 +23,6 @@
 import shortuuid
 import unicodedata
 from urllib.parse import urljoin, urlparse, quote, unquote
-from dataclasses import dataclass
 from pathlib import Path
 from sat.core.i18n import _
 from sat.core.constants import Const as C
@@ -69,6 +68,7 @@
 
 HASH_ALGO = "sha-256"
 NS_COMMENTS = "org.salut-a-toi.comments"
+NS_FS_AFFILIATION = "org.salut-a-toi.file-sharing-affiliation"
 COMMENT_NODE_PREFIX = "org.salut-a-toi.file_comments/"
 # Directory used to buffer request body (i.e. file in case of PUT) we use more than one @
 # there, to be sure than it's not conflicting with a JID
@@ -270,7 +270,7 @@
             try:
                 upload_id, filename = self.upload_data
             except exceptions.DataError as e:
-                log.warning("Invalid PUT request, we stop here: {e}")
+                log.warning(f"Invalid PUT request, we stop here: {e}")
                 return self.refuseRequest()
             try:
                 client, upload_request, timer = self.file_sharing.expected_uploads.pop(upload_id)
@@ -343,9 +343,15 @@
         self.host.trigger.add(
             "XEP-0234_fileSendingRequest", self._fileSendingRequestTrigger, priority=1000
         )
-        self.host.trigger.add("XEP-0234_buildFileElement", self._addFileComments)
-        self.host.trigger.add("XEP-0234_parseFileElement", self._getFileComments)
-        self.host.trigger.add("XEP-0329_compGetFilesFromNode", self._addCommentsData)
+        self.host.trigger.add("XEP-0234_buildFileElement", self._addFileMetadataElts)
+        self.host.trigger.add("XEP-0234_parseFileElement", self._getFileMetadataElts)
+        self.host.trigger.add("XEP-0329_compGetFilesFromNode", self._addFileMetadata)
+        self.host.trigger.add(
+            "XEP-0329_compGetFilesFromNode_build_directory",
+            self._addDirectoryMetadataElts)
+        self.host.trigger.add(
+            "XEP-0329_parseResult_directory",
+            self._getDirectoryMetadataElts)
         self.files_path = self.host.getLocalPath(None, C.FILES_DIR, profile=False)
         self.http_port = self.host.memory.getConfig(
             'component file_sharing', 'http_upload_port', 8888)
@@ -353,19 +359,19 @@
             'component file_sharing', 'http_upload_connection_type', 'https')
         if connection_type not in ('http', 'https'):
             raise exceptions.ConfigError(
-                f'bad http_upload_connection_type, you must use one of "http" or "https"'
+                'bad http_upload_connection_type, you must use one of "http" or "https"'
             )
         self.server = FileSharingSite(self)
         self.expected_uploads = {}
         if connection_type == 'http':
             reactor.listenTCP(self.http_port, self.server)
         else:
-            options = tls.getOptionsFromConfig(self.host.memory.config, "component file_sharing")
+            options = tls.getOptionsFromConfig(
+                self.host.memory.config, "component file_sharing")
             tls.TLSOptionsCheck(options)
             context_factory = tls.getTLSContextFactory(options)
             reactor.listenSSL(self.http_port, self.server, context_factory)
 
-
     def getHandler(self, client):
         return Comments_handler(self)
 
@@ -581,9 +587,15 @@
         )
         return slot
 
-    ## comments triggers ##
+    ## metadata triggers ##
 
-    def _addFileComments(self, file_elt, extra_args):
+    def _addFileMetadataElts(self, client, file_elt, extra_args):
+        # affiliation
+        affiliation = extra_args.get('affiliation')
+        if affiliation is not None:
+            file_elt.addElement((NS_FS_AFFILIATION, "affiliation"), content=affiliation)
+
+        # comments
         try:
             comments_url = extra_args.pop("comments_url")
         except KeyError:
@@ -599,16 +611,27 @@
         comment_elt["count"] = str(count)
         return True
 
-    def _getFileComments(self, file_elt, file_data):
+    def _getFileMetadataElts(self, client, file_elt, file_data):
+        # affiliation
+        try:
+            affiliation_elt = next(file_elt.elements(NS_FS_AFFILIATION, "affiliation"))
+        except StopIteration:
+            pass
+        else:
+            file_data["affiliation"] = str(affiliation_elt)
+
+        # comments
         try:
             comments_elt = next(file_elt.elements(NS_COMMENTS, "comments"))
         except StopIteration:
-            return
-        file_data["comments_url"] = str(comments_elt)
-        file_data["comments_count"] = comments_elt["count"]
+            pass
+        else:
+            file_data["comments_url"] = str(comments_elt)
+            file_data["comments_count"] = comments_elt["count"]
         return True
 
-    def _addCommentsData(self, client, iq_elt, owner, node_path, files_data):
+    def _addFileMetadata(
+            self, client, iq_elt, iq_result_elt, owner, node_path, files_data):
         for file_data in files_data:
             file_data["comments_url"] = uri.buildXMPPUri(
                 "pubsub",
@@ -617,12 +640,30 @@
             )
         return True
 
+    def _addDirectoryMetadataElts(
+            self, client, file_data, directory_elt, owner, node_path):
+        affiliation = file_data.get('affiliation')
+        if affiliation is not None:
+            directory_elt.addElement(
+                (NS_FS_AFFILIATION, "affiliation"),
+                content=affiliation
+            )
+
+    def _getDirectoryMetadataElts(
+            self, client, elt, file_data):
+        try:
+            affiliation_elt = next(elt.elements((NS_FS_AFFILIATION, "affiliation")))
+        except StopIteration:
+            pass
+        else:
+            file_data['affiliation'] = str(affiliation_elt)
+
 
 class Comments_handler(pubsub.PubSubService):
     """This class is a minimal Pubsub service handling virtual nodes for comments"""
 
     def __init__(self, plugin_parent):
-        super(Comments_handler, self).__init__()  # PubsubVirtualResource())
+        super(Comments_handler, self).__init__()
         self.host = plugin_parent.host
         self.plugin_parent = plugin_parent
         self.discoIdentity = {
--- a/sat/plugins/plugin_exp_invitation_file.py	Fri Jul 17 12:58:57 2020 +0200
+++ b/sat/plugins/plugin_exp_invitation_file.py	Fri Jul 17 13:00:10 2020 +0200
@@ -79,7 +79,7 @@
             type_human = _("file sharing")
         log.info(_(
             '{profile} has received an invitation for a files repository ({type_human}) '
-            'with namespace "{namespace}" at path [{path}]').format(
+            'with namespace {namespace!r} at path [{path}]').format(
             profile=client.profile, type_human=type_human, namespace=namespace, path=path)
             )
         return defer.ensureDeferred(
--- a/sat/plugins/plugin_xep_0234.py	Fri Jul 17 12:58:57 2020 +0200
+++ b/sat/plugins/plugin_xep_0234.py	Fri Jul 17 13:00:10 2020 +0200
@@ -105,7 +105,8 @@
 
     # generic methods
 
-    def buildFileElement(self, name=None, file_hash=None, hash_algo=None, size=None,
+    def buildFileElement(
+        self, client, name=None, file_hash=None, hash_algo=None, size=None,
         mime_type=None, desc=None, modified=None, transfer_range=None, path=None,
         namespace=None, file_elt=None, **kwargs):
         """Generate a <file> element with available metadata
@@ -113,17 +114,20 @@
         @param file_hash(unicode, None): hash of the file
             empty string to set <hash-used/> element
         @param hash_algo(unicode, None): hash algorithm used
-            if file_hash is None and hash_algo is set, a <hash-used/> element will be generated
+            if file_hash is None and hash_algo is set, a <hash-used/> element will be
+            generated
         @param transfer_range(Range, None): where transfer must start/stop
         @param modified(int, unicode, None): date of last modification
             0 to use current date
             int to use an unix timestamp
-            else must be an unicode string which will be used as it (it must be an XMPP time)
+            else must be an unicode string which will be used as it (it must be an XMPP
+            time)
         @param file_elt(domish.Element, None): element to use
             None to create a new one
         @param **kwargs: data for plugin extension (ignored by default)
         @return (domish.Element): generated element
-        @trigger XEP-0234_buildFileElement(file_elt, extra_args): can be used to extend elements to add
+        @trigger XEP-0234_buildFileElement(file_elt, extra_args): can be used to extend
+            elements to add
         """
         if file_elt is None:
             file_elt = domish.Element((NS_JINGLE_FT, "file"))
@@ -159,13 +163,14 @@
                 file_elt.addChild(self._hash.buildHashElt(file_hash, hash_algo))
         elif hash_algo is not None:
             file_elt.addChild(self._hash.buildHashUsedElt(hash_algo))
-        self.host.trigger.point("XEP-0234_buildFileElement", file_elt, extra_args=kwargs)
+        self.host.trigger.point(
+            "XEP-0234_buildFileElement", client, file_elt, extra_args=kwargs)
         if kwargs:
             for kw in kwargs:
                 log.debug("ignored keyword: {}".format(kw))
         return file_elt
 
-    def buildFileElementFromDict(self, file_data, **kwargs):
+    def buildFileElementFromDict(self, client, file_data, **kwargs):
         """like buildFileElement but get values from a file_data dict
 
         @param file_data(dict): metadata to use
@@ -174,10 +179,11 @@
         if kwargs:
             file_data = file_data.copy()
             file_data.update(kwargs)
-        return self.buildFileElement(**file_data)
+        return self.buildFileElement(client, **file_data)
 
-    def parseFileElement(self, file_elt, file_data=None, given=False, parent_elt=None,
-                         keep_empty_range=False,):
+    def parseFileElement(
+            self, client, file_elt, file_data=None, given=False, parent_elt=None,
+            keep_empty_range=False):
         """Parse a <file> element and file dictionary accordingly
 
         @param file_data(dict, None): dict where the data will be set
@@ -268,7 +274,7 @@
         except exceptions.NotFound:
             pass
 
-        self.host.trigger.point("XEP-0234_parseFileElement", file_elt, file_data)
+        self.host.trigger.point("XEP-0234_parseFileElement", client, file_elt, file_data)
 
         return file_data
 
@@ -423,7 +429,8 @@
                 file_data["namespace"] = extra["namespace"]
             if "path" in extra:
                 file_data["path"] = extra["path"]
-            self.buildFileElementFromDict(file_data, file_elt=file_elt, file_hash="")
+            self.buildFileElementFromDict(
+                client, file_data, file_elt=file_elt, file_hash="")
         else:
             # we request a file
             file_hash = extra.pop("file_hash", "")
@@ -440,7 +447,7 @@
                 file_data["namespace"] = extra["namespace"]
             if "path" in extra:
                 file_data["path"] = extra["path"]
-            self.buildFileElementFromDict(file_data, file_elt=file_elt)
+            self.buildFileElementFromDict(client, file_data, file_elt=file_elt)
 
         return desc_elt
 
@@ -474,7 +481,7 @@
         self, client, session, content_data, content_name, file_data, file_elt
     ):
         """parse file_elt, and handle file retrieving/permission checking"""
-        self.parseFileElement(file_elt, file_data)
+        self.parseFileElement(client, file_elt, file_data)
         content_data["application_data"]["file_data"] = file_data
         finished_d = content_data["finished_d"] = defer.Deferred()
 
@@ -504,7 +511,7 @@
         self, client, session, content_data, content_name, file_data, file_elt
     ):
         """parse file_elt, and handle user permission/file opening"""
-        self.parseFileElement(file_elt, file_data, given=True)
+        self.parseFileElement(client, file_elt, file_data, given=True)
         try:
             hash_algo, file_data["given_file_hash"] = self._hash.parseHashElt(file_elt)
         except exceptions.NotFound:
--- a/sat/plugins/plugin_xep_0264.py	Fri Jul 17 12:58:57 2020 +0200
+++ b/sat/plugins/plugin_xep_0264.py	Fri Jul 17 13:00:10 2020 +0200
@@ -84,7 +84,7 @@
 
     ## triggers ##
 
-    def _addFileThumbnails(self, file_elt, extra_args):
+    def _addFileThumbnails(self, client, file_elt, extra_args):
         try:
             thumbnails = extra_args["extra"][C.KEY_THUMBNAILS]
         except KeyError:
@@ -98,7 +98,7 @@
             thumbnail_elt["height"] = str(height)
         return True
 
-    def _getFileThumbnails(self, file_elt, file_data):
+    def _getFileThumbnails(self, client, file_elt, file_data):
         thumbnails = []
         for thumbnail_elt in file_elt.elements(NS_THUMBS, "thumbnail"):
             uri = thumbnail_elt["uri"]
--- a/sat/plugins/plugin_xep_0329.py	Fri Jul 17 12:58:57 2020 +0200
+++ b/sat/plugins/plugin_xep_0329.py	Fri Jul 17 13:00:10 2020 +0200
@@ -417,7 +417,8 @@
             size = os.path.getsize(path)
             mime_type = mimetypes.guess_type(path, strict=False)[0]
             file_elt = self._jf.buildFileElement(
-                name=name, size=size, mime_type=mime_type, modified=os.path.getmtime(path)
+                client=client, name=name, size=size, mime_type=mime_type,
+                modified=os.path.getmtime(path)
             )
 
             query_elt.addChild(file_elt)
@@ -590,16 +591,32 @@
         query_elt = iq_result_elt.addElement((NS_FIS, "query"))
         query_elt["node"] = node_path
         if not self.host.trigger.point(
-            "XEP-0329_compGetFilesFromNode", client, iq_elt, owner, node_path, files_data
+            "XEP-0329_compGetFilesFromNode",
+            client,
+            iq_elt,
+            iq_result_elt,
+            owner,
+            node_path,
+            files_data
         ):
             return
         for file_data in files_data:
             if file_data['type'] == C.FILE_TYPE_DIRECTORY:
                 directory_elt = query_elt.addElement("directory")
                 directory_elt['name'] = file_data['name']
+                self.host.trigger.point(
+                    "XEP-0329_compGetFilesFromNode_build_directory",
+                    client,
+                    file_data,
+                    directory_elt,
+                    owner,
+                    node_path,
+                )
             else:
                 file_elt = self._jf.buildFileElementFromDict(
-                    file_data, modified=file_data.get("modified", file_data["created"])
+                    client,
+                    file_data,
+                    modified=file_data.get("modified", file_data["created"])
                 )
                 query_elt.addChild(file_elt)
         client.send(iq_result_elt)
@@ -609,7 +626,7 @@
             client, iq_elt, self._compGetRootNodesCb, self._compGetFilesFromNodeCb
         )
 
-    def _parseResult(self, iq_elt):
+    def _parseResult(self, iq_elt, client):
         query_elt = next(iq_elt.elements(NS_FIS, "query"))
         files = []
 
@@ -617,7 +634,7 @@
             if elt.name == "file":
                 # we have a file
                 try:
-                    file_data = self._jf.parseFileElement(elt)
+                    file_data = self._jf.parseFileElement(client, elt)
                 except exceptions.DataError:
                     continue
                 file_data["type"] = C.FILE_TYPE_FILE
@@ -625,6 +642,12 @@
                 # we have a directory
 
                 file_data = {"name": elt["name"], "type": C.FILE_TYPE_DIRECTORY}
+                self.host.trigger.point(
+                    "XEP-0329_parseResult_directory",
+                    client,
+                    elt,
+                    file_data,
+                )
             else:
                 log.warning(
                     _(f"unexpected element, ignoring: {elt.toXml()}")
@@ -665,7 +688,7 @@
         if path:
             query_elt["node"] = path
         d = iq_elt.send()
-        d.addCallback(self._parseResult)
+        d.addCallback(self._parseResult, client)
         return d
 
     def _localSharesGet(self, profile):