# HG changeset patch # User souliane # Date 1412609141 -7200 # Node ID 0b87d029f0a3878784112f61f98dfe2d92ee838e # Parent 6b10442e89209362c1e3b60604e031596dd1736f plugin XEP-0277, groupblog: fixes namespace issue of the items that are received from an event + trap some errors diff -r 6b10442e8920 -r 0b87d029f0a3 src/plugins/plugin_misc_groupblog.py --- 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. diff -r 6b10442e8920 -r 0b87d029f0a3 src/plugins/plugin_xep_0277.py --- 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):