comparison src/plugins/plugin_xep_0277.py @ 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 301b342c697a
children bb30bf3ae932
comparison
equal deleted inserted replaced
1232:6b10442e8920 1233:0b87d029f0a3
110 """Convert an XML Item to microblog data used in bridge API 110 """Convert an XML Item to microblog data used in bridge API
111 @param item: domish.Element of microblog item 111 @param item: domish.Element of microblog item
112 @return: microblog data (dictionary)""" 112 @return: microblog data (dictionary)"""
113 113
114 def xpath(elt, path): 114 def xpath(elt, path):
115 """Return the XPATH result of an entry element or its descendance, works with both: 115 """Return the XPATH result of an entry element or its descendance."""
116 - no namespace, that means it is inherited from the parent item node --> NS_PUBSUB 116 # XXX: use a wildcard to work with all and even undefined namespaces
117 - empty namespace 117 return elt.xpath('/'.join(["*[local-name() = '%s']" % tag for tag in path.split('/')]))
118 XXX: check why the received entries have no namespace when they are retrieved 118
119 from self.host.plugins["XEP-0060"].getItems and they have an empty namespace 119 def date2float(elt, path):
120 when they are received with an event. 120 """Convert a date string to float without dealing with the date format."""
121 """ 121 return unicode(date.rfc3339.tf_from_timestamp(xpath(elt, path)[0].text))
122 result = elt.xpath(path)
123 if len(result) > 0:
124 return result
125 return elt.xpath('/'.join(['ns:%s' % tag for tag in path.split('/')]), namespaces={'ns': NS_PUBSUB})
126
127 # convert a date string to float without dealing with the date format
128 date2float = lambda elt, path: unicode(date.rfc3339.tf_from_timestamp(xpath(elt, path)[0].text))
129 122
130 item_elt = etree.fromstring(item.toXml().encode('utf-8')) 123 item_elt = etree.fromstring(item.toXml().encode('utf-8'))
124 item_id = item_elt.get('id', '')
125
126 # XXX: when you raise an exception from inline callbacks, do defer.returnValue(Exception())
127 # to make it catchable by an eventual errback. If you do raise Exception, raise Exception()
128 # or defer.returnValue(Exception), it will explode and then the normal callback is ran.
129
130 if item.uri not in (NS_PUBSUB, NS_PUBSUB + "#event"):
131 log.error(_("Unsupported namespace {ns} in pubsub item {id}").format(ns=item.uri, id=item_id))
132 defer.returnValue(exceptions.DataError())
133
131 try: 134 try:
132 entry_elt = xpath(item_elt, 'entry')[0] 135 entry_elt = xpath(item_elt, 'entry')[0]
133 except IndexError: 136 except IndexError:
134 raise exceptions.DataError(_('No entry found in the pubsub item %s') % item_elt.get('id', '')) 137 log.error(_('No atom entry found in the pubsub item %s') % item_id)
138 defer.returnValue(exceptions.DataError())
135 139
136 microblog_data = {} 140 microblog_data = {}
137 141
138 for key in ['title', 'content']: # process the textual elements 142 for key in ['title', 'content']: # process the textual elements
139 for attr_elt in xpath(entry_elt, key): 143 for attr_elt in xpath(entry_elt, key):
152 try: # check for mandatory elements 156 try: # check for mandatory elements
153 microblog_data['id'] = xpath(entry_elt, 'id')[0].text 157 microblog_data['id'] = xpath(entry_elt, 'id')[0].text
154 microblog_data['updated'] = date2float(entry_elt, 'updated') 158 microblog_data['updated'] = date2float(entry_elt, 'updated')
155 assert('title' in microblog_data) # has been processed already 159 assert('title' in microblog_data) # has been processed already
156 except IndexError: 160 except IndexError:
157 log.error(_("Atom entry %s misses a required element") % item_elt.get('id', '')) 161 log.error(_("Atom entry of pubsub item %s misses a required element") % item_id)
158 raise exceptions.DataError 162 defer.returnValue(exceptions.DataError())
159 163
160 if 'content' not in microblog_data: # use the atom title data as the microblog body content 164 if 'content' not in microblog_data: # use the atom title data as the microblog body content
161 microblog_data['content'] = microblog_data['title'] 165 microblog_data['content'] = microblog_data['title']
162 del microblog_data['title'] 166 del microblog_data['title']
163 if 'title_xhtml' in microblog_data: 167 if 'title_xhtml' in microblog_data:
178 microblog_data['comments'] = link_elt.attrib['href'] 182 microblog_data['comments'] = link_elt.attrib['href']
179 service, node = self.parseCommentUrl(microblog_data["comments"]) 183 service, node = self.parseCommentUrl(microblog_data["comments"])
180 microblog_data['comments_service'] = service.full() 184 microblog_data['comments_service'] = service.full()
181 microblog_data['comments_node'] = node 185 microblog_data['comments_node'] = node
182 except (exceptions.DataError, RuntimeError, KeyError): 186 except (exceptions.DataError, RuntimeError, KeyError):
183 log.warning(_("Can't parse the link element of pubsub entry %s") % microblog_data['id']) 187 log.warning(_("Can't parse the link element of atom entry %s") % microblog_data['id'])
184 except: 188 except:
185 pass 189 pass
186 try: 190 try:
187 microblog_data['author'] = xpath(entry_elt, 'author/name')[0].text 191 microblog_data['author'] = xpath(entry_elt, 'author/name')[0].text
188 except IndexError: 192 except IndexError:
189 try: # XXX: workaround for Jappix behaviour 193 try: # XXX: workaround for Jappix behaviour
190 microblog_data['author'] = xpath(entry_elt, 'author/nick')[0].text 194 microblog_data['author'] = xpath(entry_elt, 'author/nick')[0].text
191 except IndexError: 195 except IndexError:
192 log.warning(_("Can't find author element in pubsub entry %s") % microblog_data['id']) 196 log.warning(_("Can't find author element in atom entry %s") % microblog_data['id'])
193 197
194 defer.returnValue(microblog_data) 198 defer.returnValue(microblog_data)
195 199
196 def __getLXMLInnerContent(self, elt): 200 def __getLXMLInnerContent(self, elt):
197 """Return the inner content of a lxml.etree.Element. It is not 201 """Return the inner content of a lxml.etree.Element. It is not
219 if elt.uri != NS_XHTML: 223 if elt.uri != NS_XHTML:
220 raise exceptions.DataError(_('Content of type XHTML must declare its namespace!')) 224 raise exceptions.DataError(_('Content of type XHTML must declare its namespace!'))
221 return self.__getDomishInnerContent(elt) 225 return self.__getDomishInnerContent(elt)
222 226
223 def microblogCB(self, itemsEvent, profile): 227 def microblogCB(self, itemsEvent, profile):
224 d = defer.Deferred() 228 """Callback to "MICROBLOG" PEP event."""
225
226 def manageItem(microblog_data): 229 def manageItem(microblog_data):
227 self.host.bridge.personalEvent(itemsEvent.sender.full(), "MICROBLOG", microblog_data, profile) 230 self.host.bridge.personalEvent(itemsEvent.sender.full(), "MICROBLOG", microblog_data, profile)
228 231
229 for item in itemsEvent.items: 232 for item in itemsEvent.items:
230 d.addCallback(lambda ignore: self.item2mbdata(item)) 233 self.item2mbdata(item).addCallbacks(manageItem, lambda failure: None)
231 d.addCallback(manageItem)
232
233 d.callback(None)
234 return d
235 234
236 @defer.inlineCallbacks 235 @defer.inlineCallbacks
237 def data2entry(self, data, profile): 236 def data2entry(self, data, profile):
238 """Convert a data dict to en entry usable to create an item 237 """Convert a data dict to en entry usable to create an item
239 @param data: data dict as given by bridge method. 238 @param data: data dict as given by bridge method.
315 """Get the last published microblogs 314 """Get the last published microblogs
316 @param pub_jid: jid of the publisher 315 @param pub_jid: jid of the publisher
317 @param max_items: how many microblogs we want to get 316 @param max_items: how many microblogs we want to get
318 @param profile_key: profile key 317 @param profile_key: profile key
319 """ 318 """
320 def resultToArray(result):
321 ret = []
322 for (success, value) in result:
323 if success:
324 ret.append(value)
325 else:
326 log.error('Error while getting last microblog')
327 return ret
328
329 d = self.host.plugins["XEP-0060"].getItems(jid.JID(pub_jid), NS_MICROBLOG, max_items=max_items, profile_key=profile_key) 319 d = self.host.plugins["XEP-0060"].getItems(jid.JID(pub_jid), NS_MICROBLOG, max_items=max_items, profile_key=profile_key)
330 d.addCallback(lambda items: defer.DeferredList(map(self.item2mbdata, items))) 320 d.addCallback(lambda items: defer.DeferredList(map(self.item2mbdata, items), consumeErrors=True))
331 d.addCallback(resultToArray) 321 d.addCallback(lambda result: [value for (success, value) in result if success])
332 return d 322 return d
333 323
334 def setMicroblogAccess(self, access="presence", profile_key=C.PROF_KEY_NONE): 324 def setMicroblogAccess(self, access="presence", profile_key=C.PROF_KEY_NONE):
335 """Create a microblog node on PEP with given access 325 """Create a microblog node on PEP with given access
336 If the node already exists, it change options 326 If the node already exists, it change options