comparison src/plugins/plugin_misc_groupblog.py @ 858:660b3f5b6c78

plugins groupblog, XEP-0277: attempt to clarify the code for the comments handling: - add or update some methods docstrings - add GroupBlog.__fillCommentsElement to factorize the code responsible to build the comments link - do not mix up attributes names between the item own service/node and the comments service/node
author souliane <souliane@mailoo.org>
date Fri, 14 Feb 2014 21:24:31 +0100
parents 071524bfcc7d
children 2b98f5631fba
comparison
equal deleted inserted replaced
857:3c270d691e56 858:660b3f5b6c78
134 def getHandler(self, profile): 134 def getHandler(self, profile):
135 return GroupBlog_handler() 135 return GroupBlog_handler()
136 136
137 @defer.inlineCallbacks 137 @defer.inlineCallbacks
138 def _initialise(self, profile_key): 138 def _initialise(self, profile_key):
139 """Check that this data for this profile are initialised, and do it else 139 """Check that the data for this profile are initialised, and do it else
140 @param client: client of the profile 140 @param profile_key: %(doc_profile)s"""
141 @profile_key: %(doc_profile)s"""
142 profile = self.host.memory.getProfileName(profile_key) 141 profile = self.host.memory.getProfileName(profile_key)
143 if not profile: 142 if not profile:
144 raise exceptions.ProfileUnknownError 143 raise exceptions.ProfileUnknownError
145 144
146 client = self.host.getClient(profile) 145 client = self.host.getClient(profile)
264 return NS_NODE_PREFIX + publisher.userhost() 263 return NS_NODE_PREFIX + publisher.userhost()
265 264
266 def _publishMblog(self, service, client, access_type, access_list, message, extra): 265 def _publishMblog(self, service, client, access_type, access_list, message, extra):
267 """Actually publish the message on the group blog 266 """Actually publish the message on the group blog
268 @param service: jid of the item-access pubsub service 267 @param service: jid of the item-access pubsub service
269 @param client: SatXMPPClient of the published 268 @param client: SatXMPPClient of the publisher
270 @param access_type: one of "PUBLIC", "GROUP", "JID" 269 @param access_type: one of "PUBLIC", "GROUP", "JID"
271 @param access_list: set of entities (empty list for all, groups or jids) allowed to see the item 270 @param access_list: set of entities (empty list for all, groups or jids) allowed to see the item
272 @param message: message to publish 271 @param message: message to publish
273 @param extra: dict which option name as key, which can be: 272 @param extra: dict which option name as key, which can be:
274 - allow_comments: True to accept comments, False else (default: False) 273 - allow_comments: True to accept comments, False else (default: False)
283 P = self.host.plugins["XEP-0060"] 282 P = self.host.plugins["XEP-0060"]
284 access_model_value = ACCESS_TYPE_MAP[access_type] 283 access_model_value = ACCESS_TYPE_MAP[access_type]
285 284
286 if extra.get('allow_comments', 'False').lower() == 'true': 285 if extra.get('allow_comments', 'False').lower() == 'true':
287 # XXX: use the item identifier? http://bugs.goffi.org/show_bug.cgi?id=63 286 # XXX: use the item identifier? http://bugs.goffi.org/show_bug.cgi?id=63
288 comments_node = "%s_%s__%s" % (NS_COMMENT_PREFIX, str(uuid.uuid4()), node_name) 287 comments_node = self.__fillCommentsElement(mblog_data, None, node_name, service)
289 mblog_data['comments'] = "xmpp:%(service)s?%(query)s" % {'service': service.userhost(),
290 'query': urllib.urlencode([('node', comments_node.encode('utf-8'))])}
291 _options = {P.OPT_ACCESS_MODEL: access_model_value, 288 _options = {P.OPT_ACCESS_MODEL: access_model_value,
292 P.OPT_PERSIST_ITEMS: 1, 289 P.OPT_PERSIST_ITEMS: 1,
293 P.OPT_MAX_ITEMS: -1, 290 P.OPT_MAX_ITEMS: -1,
294 P.OPT_DELIVER_PAYLOADS: 1, 291 P.OPT_DELIVER_PAYLOADS: 1,
295 P.OPT_SEND_ITEM_SUBSCRIBE: 1, 292 P.OPT_SEND_ITEM_SUBSCRIBE: 1,
329 326
330 entry_d = self.host.plugins["XEP-0277"].data2entry(mblog_data, client.profile) 327 entry_d = self.host.plugins["XEP-0277"].data2entry(mblog_data, client.profile)
331 entry_d.addCallback(itemCreated) 328 entry_d.addCallback(itemCreated)
332 return entry_d 329 return entry_d
333 330
331 def __fillCommentsElement(self, mblog_data, entry_id, node_name, service_jid):
332 """
333 @param mblog_data: dict containing the microblog data
334 @param entry_id: unique identifier of the entry
335 @param node_name: the pubsub node name
336 @param service_jid: the JID of the pubsub service
337 @return: the comments node string
338 """
339 if entry_id is None:
340 entry_id = unicode(uuid.uuid4())
341 comments_node = "%s_%s__%s" % (NS_COMMENT_PREFIX, entry_id, node_name)
342 mblog_data['comments'] = "xmpp:%(service)s?%(query)s" % {'service': service_jid.userhost(),
343 'query': urllib.urlencode([('node', comments_node.encode('utf-8'))])}
344 return comments_node
345
334 def _mblogPublicationFailed(self, failure): 346 def _mblogPublicationFailed(self, failure):
335 #TODO 347 #TODO
336 return failure 348 return failure
337 349
338 def sendGroupBlog(self, access_type, access_list, message, extra, profile_key='@NONE@'): 350 def sendGroupBlog(self, access_type, access_list, message, extra, profile_key='@NONE@'):
366 378
367 return self._initialise(profile_key).addCallback(initialised) 379 return self._initialise(profile_key).addCallback(initialised)
368 380
369 def deleteGroupBlog(self, pub_data, comments, profile_key='@NONE@'): 381 def deleteGroupBlog(self, pub_data, comments, profile_key='@NONE@'):
370 """Delete a microblog item from a node. 382 """Delete a microblog item from a node.
371 @param pub_data: a tuple (service, comment node identifier, item identifier) 383 @param pub_data: a tuple (service, node identifier, item identifier)
372 @param comments: comments node identifier (for main item) or empty string 384 @param comments: comments node identifier (for main item) or empty string
373 @param profile_key: %(doc_profile_key)s 385 @param profile_key: %(doc_profile_key)s
374 """ 386 """
375 387
376 def initialised(result): 388 def initialised(result):
377 profile, client = result 389 profile, client = result
378 service, node, item_id = pub_data 390 service, node, item_id = pub_data
391 service_jid = jid.JID(service) if service else client.item_access_pubsub
392 if comments or not node: # main item
393 node = self.getNodeName(client.jid)
379 if comments: 394 if comments:
380 # remove the associated comments node 395 # remove the associated comments node
381 d = self.host.plugins["XEP-0060"].deleteNode(jid.JID(service), node, profile_key=profile) 396 comments_service, comments_node = self.host.plugins["XEP-0277"].parseCommentUrl(comments)
382 d.addErrback(lambda failure: error("Deletion of node %s failed: %s" % (node, failure.getErrorMessage()))) 397 d = self.host.plugins["XEP-0060"].deleteNode(comments_service, comments_node, profile_key=profile)
383 node = self.getNodeName(client.jid) 398 d.addErrback(lambda failure: error("Deletion of node %s failed: %s" % (comments_node, failure.getErrorMessage())))
384 # remove the item itself 399 # remove the item itself
385 d = self.host.plugins["XEP-0060"].retractItems(jid.JID(service), node, [item_id], profile_key=profile) 400 d = self.host.plugins["XEP-0060"].retractItems(service_jid, node, [item_id], profile_key=profile)
386 d.addErrback(lambda failure: error("Deletion of item %s from %s failed: %s" % (item_id, node, failure.getErrorMessage()))) 401 d.addErrback(lambda failure: error("Deletion of item %s from %s failed: %s" % (item_id, node, failure.getErrorMessage())))
387 return d 402 return d
388 403
389 def notify(d): 404 def notify(d):
390 # TODO: this works only on the same host, and notifications for item deletion should be 405 # TODO: this works only on the same host, and notifications for item deletion should be
392 # instead. The notification mechanisms implemented in sat_pubsub and wokkel seem not compatible, 407 # instead. The notification mechanisms implemented in sat_pubsub and wokkel seem not compatible,
393 # see wokkel.pubsub.PubSubClient._onEvent_items and sat_pubsub.backend._doNotifyRetraction 408 # see wokkel.pubsub.PubSubClient._onEvent_items and sat_pubsub.backend._doNotifyRetraction
394 service, node, item_id = pub_data 409 service, node, item_id = pub_data
395 publisher = self.host.getJidNStream(profile_key)[0] 410 publisher = self.host.getJidNStream(profile_key)[0]
396 profile = self.host.memory.getProfileName(profile_key) 411 profile = self.host.memory.getProfileName(profile_key)
397 gbdatum = {'id': item_id, 'type': 'main_item' if comments else 'comment'} 412 gbdatum = {'id': item_id, 'type': 'main_item' if (comments or not node) else 'comment'}
398 self.host.bridge.personalEvent(publisher.full(), "MICROBLOG_DELETE", gbdatum, profile) 413 self.host.bridge.personalEvent(publisher.full(), "MICROBLOG_DELETE", gbdatum, profile)
399 return d 414 return d
400 415
401 return self._initialise(profile_key).addCallback(initialised).addCallback(notify) 416 return self._initialise(profile_key).addCallback(initialised).addCallback(notify)
402 417
403 def updateGroupBlog(self, pub_data, comments, message, extra, profile_key='@NONE@'): 418 def updateGroupBlog(self, pub_data, comments, message, extra, profile_key='@NONE@'):
404 """Modify a microblog node 419 """Modify a microblog node
405 @param pub_data: a tuple (service, comment node identifier, item identifier) 420 @param pub_data: a tuple (service, node identifier, item identifier)
406 @param comments: comments node identifier (for main item) or empty string 421 @param comments: comments node identifier (for main item) or empty string
407 @param message: new message 422 @param message: new message
408 @param extra: dict which option name as key, which can be: 423 @param extra: dict which option name as key, which can be:
409 - allow_comments: True to accept an other level of comments, False else (default: False) 424 - allow_comments: True to accept an other level of comments, False else (default: False)
410 - rich: if present, contain rich text in currently selected syntax 425 - rich: if present, contain rich text in currently selected syntax
416 mblog_data = {'content': message} 431 mblog_data = {'content': message}
417 for attr in ['content_rich', 'title', 'title_rich']: 432 for attr in ['content_rich', 'title', 'title_rich']:
418 if attr in extra and extra[attr]: 433 if attr in extra and extra[attr]:
419 mblog_data[attr] = extra[attr] 434 mblog_data[attr] = extra[attr]
420 service, node, item_id = pub_data 435 service, node, item_id = pub_data
421 if comments: 436 service_jid = jid.JID(service) if service else client.item_access_pubsub
437 if comments or not node: # main item
422 node = self.getNodeName(client.jid) 438 node = self.getNodeName(client.jid)
423 mblog_data['id'] = str(item_id) 439 mblog_data['id'] = unicode(item_id)
424 if 'published' in extra: 440 if 'published' in extra:
425 mblog_data['published'] = extra['published'] 441 mblog_data['published'] = extra['published']
426 if extra.get('allow_comments', 'False').lower() == 'true': 442 if extra.get('allow_comments', 'False').lower() == 'true':
427 comments_node = self.host.plugins["XEP-0277"].parseCommentUrl(comments)[1] 443 comments_service, comments_node = self.host.plugins["XEP-0277"].parseCommentUrl(comments)
428 # we could use comments_node directly but it's safer to rebuild it 444 # we could use comments_node directly but it's safer to rebuild it
429 # XXX: use the item identifier? http://bugs.goffi.org/show_bug.cgi?id=63 445 # XXX: use the item identifier? http://bugs.goffi.org/show_bug.cgi?id=63
430 hash_ = comments_node.split('_')[1].split('__')[0] 446 entry_id = comments_node.split('_')[1].split('__')[0]
431 comments_node = "%s_%s__%s" % (NS_COMMENT_PREFIX, hash_, node) 447 self.__fillCommentsElement(mblog_data, entry_id, node, service_jid)
432 mblog_data['comments'] = "xmpp:%(service)s?%(query)s" % {'service': jid.JID(service).userhost(),
433 'query': urllib.urlencode([('node', comments_node.encode('utf-8'))])}
434 entry_d = self.host.plugins["XEP-0277"].data2entry(mblog_data, profile) 448 entry_d = self.host.plugins["XEP-0277"].data2entry(mblog_data, profile)
435 entry_d.addCallback(lambda mblog_item: self.host.plugins["XEP-0060"].publish(jid.JID(service), node, items=[mblog_item], profile_key=profile)) 449 entry_d.addCallback(lambda mblog_item: self.host.plugins["XEP-0060"].publish(service_jid, node, items=[mblog_item], profile_key=profile))
436 entry_d.addErrback(lambda failure: error("Modification of %s failed: %s" % (pub_data, failure.getErrorMessage()))) 450 entry_d.addErrback(lambda failure: error("Modification of %s failed: %s" % (pub_data, failure.getErrorMessage())))
437 return entry_d 451 return entry_d
438 452
439 return self._initialise(profile_key).addCallback(initialised) 453 return self._initialise(profile_key).addCallback(initialised)
440 454
472 @return: deferred which fire list of group blog data """ 486 @return: deferred which fire list of group blog data """
473 # TODO: use items data when pub_jid is None 487 # TODO: use items data when pub_jid is None
474 ret = [] 488 ret = []
475 for item in items: 489 for item in items:
476 gbdata = yield self.item2gbdata(item) 490 gbdata = yield self.item2gbdata(item)
491 try:
492 gbdata['service'] = client.item_access_pubsub.full()
493 except AttributeError:
494 pass
477 ret.append(gbdata) 495 ret.append(gbdata)
478 # if there is a comments node, we subscribe to it 496 # if there is a comments node, we subscribe to it
479 if "comments_node" in gbdata: 497 if "comments_node" in gbdata:
480 try: 498 try:
481 # every comments node must be subscribed, except if we are the publisher (we are already subscribed in this case) 499 # every comments node must be subscribed, except if we are the publisher (we are already subscribed in this case)