changeset 1233:0b87d029f0a3

plugin XEP-0277, groupblog: fixes namespace issue of the items that are received from an event + trap some errors
author souliane <souliane@mailoo.org>
date Mon, 06 Oct 2014 17:25:41 +0200
parents 6b10442e8920
children 9c17bd37e6e5
files src/plugins/plugin_misc_groupblog.py src/plugins/plugin_xep_0277.py
diffstat 2 files changed, 47 insertions(+), 53 deletions(-) [+]
line wrap: on
line diff
--- a/src/plugins/plugin_misc_groupblog.py	Tue Oct 07 10:19:01 2014 +0200
+++ b/src/plugins/plugin_misc_groupblog.py	Mon Oct 06 17:25:41 2014 +0200
@@ -212,25 +212,27 @@
             return False
         return True
 
-    @defer.inlineCallbacks
     def _handleCommentsItems(self, items, service, node_identifier):
         """ Convert comments items to groupblog data, and send them as signals
         @param items: comments items
         @param service: jid of the PubSub service used
         @param node_identifier: comments node
-        @return: list of group blog data
+        @return: deferred list of group blog data
         """
-        ret = []
-        for item in items:
+        d_list = []
+
+        def cb(microblog_data):
             publisher = "" # FIXME: publisher attribute for item in SàT pubsub is not managed yet, so
                            #        publisher is not checked and can be easily spoofed. This need to be fixed
                            #        quickly.
-            microblog_data = yield self.item2gbdata(item, "comment")
             microblog_data["service"] = service.userhost()
             microblog_data["node"] = node_identifier
             microblog_data["verified_publisher"] = "true" if publisher else "false"
-            ret.append(microblog_data)
-        defer.returnValue(ret)
+            return microblog_data
+
+        for item in items:
+            d_list.append(self.item2gbdata(item, "comment").addCallback(cb))
+        return defer.DeferredList(d_list, consumeErrors=True).addCallback(lambda result: [value for (success, value) in result if success])
 
     def _parseAccessData(self, microblog_data, item):
         P = self.host.plugins["XEP-0060"]
@@ -477,8 +479,6 @@
 
         return self._initialise(profile_key).addCallback(initialised)
 
-
-    @defer.inlineCallbacks
     def _itemsConstruction(self, items, pub_jid, client):
         """ Transforms items to group blog data and manage comments node
 
@@ -487,29 +487,33 @@
         @param client: SatXMPPClient instance
         @return: deferred which fire list of group blog data """
         # TODO: use items data when pub_jid is None
-        ret = []
-        for item in items:
-            gbdata = yield self.item2gbdata(item)
+        d_list = []
+
+        @defer.inlineCallbacks
+        def cb(gbdata):
             try:
                 gbdata['service'] = client.item_access_pubsub.full()
             except AttributeError:
-                pass
-            ret.append(gbdata)
+                log.warning(_("Pubsub service is unknown for blog entry %s") % gbdata['id'])
             # every comments node must be subscribed, except if we are the publisher (we are already subscribed in this case)
             if "comments_node" in gbdata and pub_jid.userhostJID() != client.jid.userhostJID():
                 try:
                     service = jid.JID(gbdata["comments_service"])
                     node = gbdata["comments_node"]
                 except KeyError:
-                    log.warning("Missing key for comments")
-                    continue
+                    log.error(_("Missing key for blog comment %s") % gbdata['id'])
+                    defer.returnValue(gbdata)
                 # TODO: see if it is really needed to check for not subscribing twice to the node
                 # It previously worked without this check, but the pubsub service logs were polluted
                 # or, if in debug mode, it made sat-pubsub very difficult to debug.
                 subscribed_nodes = yield self.host.plugins['XEP-0060'].listSubscribedNodes(service, profile=client.profile)
                 if node not in subscribed_nodes:  # avoid sat-pubsub "SubscriptionExists" error
                     self.host.plugins["XEP-0060"].subscribe(service, node, profile_key=client.profile)
-        defer.returnValue(ret)
+            defer.returnValue(gbdata)
+
+        for item in items:
+            d_list.append(self.item2gbdata(item).addCallback(cb))
+        return defer.DeferredList(d_list, consumeErrors=True).addCallback(lambda result: [value for (success, value) in result if success])
 
     def __getGroupBlogs(self, pub_jid_s, max_items=10, item_ids=None, profile_key=C.PROF_KEY_NONE):
         """Retrieve previously published items from a publish subscribe node.
--- a/src/plugins/plugin_xep_0277.py	Tue Oct 07 10:19:01 2014 +0200
+++ b/src/plugins/plugin_xep_0277.py	Mon Oct 06 17:25:41 2014 +0200
@@ -112,26 +112,30 @@
         @return: microblog data (dictionary)"""
 
         def xpath(elt, path):
-            """Return the XPATH result of an entry element or its descendance, works with both:
-            - no namespace, that means it is inherited from the parent item node --> NS_PUBSUB
-            - empty namespace
-            XXX: check why the received entries have no namespace when they are retrieved
-            from self.host.plugins["XEP-0060"].getItems and they have an empty namespace
-            when they are received with an event.
-            """
-            result = elt.xpath(path)
-            if len(result) > 0:
-                return result
-            return elt.xpath('/'.join(['ns:%s' % tag for tag in path.split('/')]), namespaces={'ns': NS_PUBSUB})
+            """Return the XPATH result of an entry element or its descendance."""
+            # XXX: use a wildcard to work with all and even undefined namespaces
+            return elt.xpath('/'.join(["*[local-name() = '%s']" % tag for tag in path.split('/')]))
 
-        # convert a date string to float without dealing with the date format
-        date2float = lambda elt, path: unicode(date.rfc3339.tf_from_timestamp(xpath(elt, path)[0].text))
+        def date2float(elt, path):
+            """Convert a date string to float without dealing with the date format."""
+            return unicode(date.rfc3339.tf_from_timestamp(xpath(elt, path)[0].text))
 
         item_elt = etree.fromstring(item.toXml().encode('utf-8'))
+        item_id = item_elt.get('id', '')
+
+        # XXX: when you raise an exception from inline callbacks, do defer.returnValue(Exception())
+        # to make it catchable by an eventual errback. If you do raise Exception, raise Exception()
+        # or defer.returnValue(Exception), it will explode and then the normal callback is ran.
+
+        if item.uri not in (NS_PUBSUB, NS_PUBSUB + "#event"):
+            log.error(_("Unsupported namespace {ns} in pubsub item {id}").format(ns=item.uri, id=item_id))
+            defer.returnValue(exceptions.DataError())
+
         try:
             entry_elt = xpath(item_elt, 'entry')[0]
         except IndexError:
-            raise exceptions.DataError(_('No entry found in the pubsub item %s') % item_elt.get('id', ''))
+            log.error(_('No atom entry found in the pubsub item %s') % item_id)
+            defer.returnValue(exceptions.DataError())
 
         microblog_data = {}
 
@@ -154,8 +158,8 @@
             microblog_data['updated'] = date2float(entry_elt, 'updated')
             assert('title' in microblog_data)  # has been processed already
         except IndexError:
-            log.error(_("Atom entry %s misses a required element") % item_elt.get('id', ''))
-            raise exceptions.DataError
+            log.error(_("Atom entry of pubsub item %s misses a required element") % item_id)
+            defer.returnValue(exceptions.DataError())
 
         if 'content' not in microblog_data:  # use the atom title data as the microblog body content
             microblog_data['content'] = microblog_data['title']
@@ -180,7 +184,7 @@
                 microblog_data['comments_service'] = service.full()
                 microblog_data['comments_node'] = node
             except (exceptions.DataError, RuntimeError, KeyError):
-                log.warning(_("Can't parse the link element of pubsub entry %s") % microblog_data['id'])
+                log.warning(_("Can't parse the link element of atom entry %s") % microblog_data['id'])
         except:
             pass
         try:
@@ -189,7 +193,7 @@
             try:  # XXX: workaround for Jappix behaviour
                 microblog_data['author'] = xpath(entry_elt, 'author/nick')[0].text
             except IndexError:
-                log.warning(_("Can't find author element in pubsub entry %s") % microblog_data['id'])
+                log.warning(_("Can't find author element in atom entry %s") % microblog_data['id'])
 
         defer.returnValue(microblog_data)
 
@@ -221,17 +225,12 @@
         return self.__getDomishInnerContent(elt)
 
     def microblogCB(self, itemsEvent, profile):
-        d = defer.Deferred()
-
+        """Callback to "MICROBLOG" PEP event."""
         def manageItem(microblog_data):
             self.host.bridge.personalEvent(itemsEvent.sender.full(), "MICROBLOG", microblog_data, profile)
 
         for item in itemsEvent.items:
-            d.addCallback(lambda ignore: self.item2mbdata(item))
-            d.addCallback(manageItem)
-
-        d.callback(None)
-        return d
+            self.item2mbdata(item).addCallbacks(manageItem, lambda failure: None)
 
     @defer.inlineCallbacks
     def data2entry(self, data, profile):
@@ -317,18 +316,9 @@
         @param max_items: how many microblogs we want to get
         @param profile_key: profile key
         """
-        def resultToArray(result):
-            ret = []
-            for (success, value) in result:
-                if success:
-                    ret.append(value)
-                else:
-                    log.error('Error while getting last microblog')
-            return ret
-
         d = self.host.plugins["XEP-0060"].getItems(jid.JID(pub_jid), NS_MICROBLOG, max_items=max_items, profile_key=profile_key)
-        d.addCallback(lambda items: defer.DeferredList(map(self.item2mbdata, items)))
-        d.addCallback(resultToArray)
+        d.addCallback(lambda items: defer.DeferredList(map(self.item2mbdata, items), consumeErrors=True))
+        d.addCallback(lambda result: [value for (success, value) in result if success])
         return d
 
     def setMicroblogAccess(self, access="presence", profile_key=C.PROF_KEY_NONE):