comparison src/plugins/plugin_xep_0277.py @ 1421:16b1ba7ccaaa

plugins xep-0277: some methods reordering to make the plugin more readable
author Goffi <goffi@goffi.org>
date Thu, 23 Apr 2015 10:25:47 +0200
parents be2df1ddea8e
children e8c8e467964b
comparison
equal deleted inserted replaced
1420:7c0acb966fd6 1421:16b1ba7ccaaa
73 host.bridge.addMethod("setMicroblogAccess", ".plugin", in_sign='ss', out_sign='', 73 host.bridge.addMethod("setMicroblogAccess", ".plugin", in_sign='ss', out_sign='',
74 method=self.setMicroblogAccess, 74 method=self.setMicroblogAccess,
75 async=True, 75 async=True,
76 doc={}) 76 doc={})
77 77
78 def parseCommentUrl(self, node_url): 78 ## plugin management methods ##
79 """Determine the fields comments_service and comments_node of a microblog data 79
80 from the href attribute of an entry's link element. For example this input: 80 def microblogCB(self, itemsEvent, profile):
81 xmpp:sat-pubsub.libervia.org?node=urn%3Axmpp%3Acomments%3A_c5c4a142-2279-4b2a-ba4c-1bc33aa87634__urn%3Axmpp%3Agroupblog%3Asouliane%40libervia.org 81 """Callback to "MICROBLOG" PEP event."""
82 will return (JID(u'sat-pubsub.libervia.org'), 'urn:xmpp:comments:_c5c4a142-2279-4b2a-ba4c-1bc33aa87634__urn:xmpp:groupblog:souliane@libervia.org') 82 def manageItem(microblog_data):
83 @return: a tuple (JID, str) 83 self.host.bridge.personalEvent(itemsEvent.sender.full(), "MICROBLOG", microblog_data, profile)
84 """ 84
85 parsed_url = urlparse.urlparse(node_url, 'xmpp') 85 for item in itemsEvent.items:
86 service = jid.JID(parsed_url.path) 86 self.item2mbdata(item).addCallbacks(manageItem, lambda failure: None)
87 queries = parsed_url.query.split(';') 87
88 parsed_queries = dict() 88 ## data/item transformation ##
89 for query in queries: 89
90 parsed_queries.update(urlparse.parse_qs(query)) 90 def _getDomishInnerContent(self, elt):
91 node = parsed_queries.get('node', [''])[0] 91 """Return the inner content of a domish.Element."""
92 92 result = ''
93 if not node: 93 for child in elt.children:
94 raise exceptions.DataError('Invalid comments link') 94 try:
95 95 result += child.toXml() # child id a domish.Element
96 return (service, node) 96 except AttributeError:
97 result += child # child is unicode
98 return result
97 99
98 def _removeXHTMLMarkups(self, xhtml): 100 def _removeXHTMLMarkups(self, xhtml):
99 """Remove XHTML markups from the given string. 101 """Remove XHTML markups from the given string.
100 102
101 @param xhtml: the XHTML string to be cleaned 103 @param xhtml: the XHTML string to be cleaned
107 False) 109 False)
108 110
109 @defer.inlineCallbacks 111 @defer.inlineCallbacks
110 def item2mbdata(self, item): 112 def item2mbdata(self, item):
111 """Convert an XML Item to microblog data used in bridge API 113 """Convert an XML Item to microblog data used in bridge API
114
112 @param item: domish.Element of microblog item 115 @param item: domish.Element of microblog item
113 @return: microblog data (dictionary)""" 116 @return: microblog data (dictionary)
117 """
114 118
115 def xpath(elt, path): 119 def xpath(elt, path):
116 """Return the XPATH result of an entry element or its descendance.""" 120 """Return the XPATH result of an entry element or its descendance."""
117 # XXX: use a wildcard to work with all and even undefined namespaces 121 # XXX: use a wildcard to work with all and even undefined namespaces
118 return elt.xpath('/'.join(["*[local-name() = '%s']" % tag for tag in path.split('/')])) 122 return elt.xpath('/'.join(["*[local-name() = '%s']" % tag for tag in path.split('/')]))
140 144
141 microblog_data = {} 145 microblog_data = {}
142 146
143 for key in ['title', 'content']: # process the textual elements 147 for key in ['title', 'content']: # process the textual elements
144 for attr_elt in xpath(entry_elt, key): 148 for attr_elt in xpath(entry_elt, key):
145 attr_content = self._getLXMLInnerContent(attr_elt) 149 # Return the inner content of a lxml.etree.Element. It is not
150 # trivial because the lxml tostring method would return the full
151 # content including elt's tag and attributes, and elt.getchildren()
152 # would skip a text value which is not within an element...
153 attr_content = self._getDomishInnerContent(ElementParser()(etree.tostring(attr_elt)))
146 if not attr_content.strip(): 154 if not attr_content.strip():
147 continue # element with empty value 155 continue # element with empty value
148 content_type = attr_elt.get('type', 'text').lower() 156 content_type = attr_elt.get('type', 'text').lower()
149 if content_type == 'xhtml': 157 if content_type == 'xhtml':
150 text = self._decapsulateExtraNS(attr_content) 158 # Check for XHTML namespace and decapsulate the content so the user
159 # who wants to modify an entry will see the text that he entered. Also
160 # this avoids successive encapsulation with a new <div>...</div> at
161 # each modification (encapsulation is done in self.data2entry)
162 elt = ElementParser()(attr_content)
163 if elt.uri != NS_XHTML:
164 raise exceptions.DataError(_('Content of type XHTML must declare its namespace!'))
165 text = self._getDomishInnerContent(elt)
151 microblog_data['%s_xhtml' % key] = yield self.host.plugins["TEXT-SYNTAXES"].clean_xhtml(text) 166 microblog_data['%s_xhtml' % key] = yield self.host.plugins["TEXT-SYNTAXES"].clean_xhtml(text)
152 else: 167 else:
153 microblog_data[key] = attr_content 168 microblog_data[key] = attr_content
154 if key not in microblog_data and ('%s_xhtml' % key) in microblog_data: 169 if key not in microblog_data and ('%s_xhtml' % key) in microblog_data:
155 microblog_data[key] = yield self._removeXHTMLMarkups(microblog_data['%s_xhtml' % key]) 170 microblog_data[key] = yield self._removeXHTMLMarkups(microblog_data['%s_xhtml' % key])
195 microblog_data['author'] = xpath(entry_elt, 'author/nick')[0].text 210 microblog_data['author'] = xpath(entry_elt, 'author/nick')[0].text
196 except IndexError: 211 except IndexError:
197 log.warning(_(u"Can't find author element in atom entry %s") % microblog_data['id']) 212 log.warning(_(u"Can't find author element in atom entry %s") % microblog_data['id'])
198 213
199 defer.returnValue(microblog_data) 214 defer.returnValue(microblog_data)
200
201 def _getLXMLInnerContent(self, elt):
202 """Return the inner content of a lxml.etree.Element. It is not
203 trivial because the lxml tostring method would return the full
204 content including elt's tag and attributes, and elt.getchildren()
205 would skip a text value which is not within an element..."""
206 return self._getDomishInnerContent(ElementParser()(etree.tostring(elt)))
207
208 def _getDomishInnerContent(self, elt):
209 """Return the inner content of a domish.Element."""
210 result = ''
211 for child in elt.children:
212 try:
213 result += child.toXml() # child id a domish.Element
214 except AttributeError:
215 result += child # child is unicode
216 return result
217
218 def _decapsulateExtraNS(self, text):
219 """Check for XHTML namespace and decapsulate the content so the user
220 who wants to modify an entry will see the text that he entered. Also
221 this avoids successive encapsulation with a new <div>...</div> at
222 each modification (encapsulation is done in self.data2entry)"""
223 elt = ElementParser()(text)
224 if elt.uri != NS_XHTML:
225 raise exceptions.DataError(_('Content of type XHTML must declare its namespace!'))
226 return self._getDomishInnerContent(elt)
227
228 def microblogCB(self, itemsEvent, profile):
229 """Callback to "MICROBLOG" PEP event."""
230 def manageItem(microblog_data):
231 self.host.bridge.personalEvent(itemsEvent.sender.full(), "MICROBLOG", microblog_data, profile)
232
233 for item in itemsEvent.items:
234 self.item2mbdata(item).addCallbacks(manageItem, lambda failure: None)
235 215
236 @defer.inlineCallbacks 216 @defer.inlineCallbacks
237 def data2entry(self, data, profile): 217 def data2entry(self, data, profile):
238 """Convert a data dict to en entry usable to create an item 218 """Convert a data dict to en entry usable to create an item
239 219
295 _entry.links.append(link) 275 _entry.links.append(link)
296 _entry_elt = ElementParser()(str(_entry).decode('utf-8')) 276 _entry_elt = ElementParser()(str(_entry).decode('utf-8'))
297 item = pubsub.Item(id=entry_id, payload=_entry_elt) 277 item = pubsub.Item(id=entry_id, payload=_entry_elt)
298 defer.returnValue(item) 278 defer.returnValue(item)
299 279
280 ## publish ##
281
300 @defer.inlineCallbacks 282 @defer.inlineCallbacks
301 def sendMicroblog(self, data, profile): 283 def sendMicroblog(self, data, profile):
302 """Send XEP-0277's microblog data 284 """Send XEP-0277's microblog data
303 285
304 @param data: must include content 286 @param data: must include content
312 raise exceptions.DataError('empty content') 294 raise exceptions.DataError('empty content')
313 item = yield self.data2entry(data, profile) 295 item = yield self.data2entry(data, profile)
314 ret = yield self.host.plugins["XEP-0060"].publish(None, NS_MICROBLOG, [item], profile_key=profile) 296 ret = yield self.host.plugins["XEP-0060"].publish(None, NS_MICROBLOG, [item], profile_key=profile)
315 defer.returnValue(ret) 297 defer.returnValue(ret)
316 298
299 ## get ##
300
317 def getLastMicroblogs(self, pub_jid, max_items=10, profile_key=C.PROF_KEY_NONE): 301 def getLastMicroblogs(self, pub_jid, max_items=10, profile_key=C.PROF_KEY_NONE):
318 """Get the last published microblogs 302 """Get the last published microblogs
319 303
320 @param pub_jid: jid of the publisher 304 @param pub_jid: jid of the publisher
321 @param max_items: how many microblogs we want to get 305 @param max_items: how many microblogs we want to get
325 """ 309 """
326 d = self.host.plugins["XEP-0060"].getItems(jid.JID(pub_jid), NS_MICROBLOG, max_items=max_items, profile_key=profile_key) 310 d = self.host.plugins["XEP-0060"].getItems(jid.JID(pub_jid), NS_MICROBLOG, max_items=max_items, profile_key=profile_key)
327 d.addCallback(lambda res: (defer.DeferredList(map(self.item2mbdata, res[0]), consumeErrors=True), res[1])) 311 d.addCallback(lambda res: (defer.DeferredList(map(self.item2mbdata, res[0]), consumeErrors=True), res[1]))
328 d.addCallback(lambda res: ([value for (success, value) in res[0] if success], res[1])) 312 d.addCallback(lambda res: ([value for (success, value) in res[0] if success], res[1]))
329 return d 313 return d
314
315 def parseCommentUrl(self, node_url):
316 """Determine the fields comments_service and comments_node of a microblog data
317 from the href attribute of an entry's link element. For example this input:
318 xmpp:sat-pubsub.libervia.org?node=urn%3Axmpp%3Acomments%3A_c5c4a142-2279-4b2a-ba4c-1bc33aa87634__urn%3Axmpp%3Agroupblog%3Asouliane%40libervia.org
319 will return (JID(u'sat-pubsub.libervia.org'), 'urn:xmpp:comments:_c5c4a142-2279-4b2a-ba4c-1bc33aa87634__urn:xmpp:groupblog:souliane@libervia.org')
320 @return: a tuple (JID, str)
321 """
322 parsed_url = urlparse.urlparse(node_url, 'xmpp')
323 service = jid.JID(parsed_url.path)
324 queries = parsed_url.query.split(';')
325 parsed_queries = dict()
326 for query in queries:
327 parsed_queries.update(urlparse.parse_qs(query))
328 node = parsed_queries.get('node', [''])[0]
329
330 if not node:
331 raise exceptions.DataError('Invalid comments link')
332
333 return (service, node)
334
335 ## configure ##
330 336
331 def setMicroblogAccess(self, access="presence", profile_key=C.PROF_KEY_NONE): 337 def setMicroblogAccess(self, access="presence", profile_key=C.PROF_KEY_NONE):
332 """Create a microblog node on PEP with given access 338 """Create a microblog node on PEP with given access
333 339
334 If the node already exists, it change options 340 If the node already exists, it change options