Mercurial > libervia-backend
comparison src/plugins/plugin_misc_groupblog.py @ 1268:bb30bf3ae932
plugins XEP-0060, XEP-0277, groupblog: make use of RSM (XEP-0059)
author | souliane <souliane@mailoo.org> |
---|---|
date | Mon, 15 Dec 2014 14:04:19 +0100 |
parents | b4a264915ea9 |
children | 1c90a913fbb9 |
comparison
equal
deleted
inserted
replaced
1267:ea692d51a0ee | 1268:bb30bf3ae932 |
---|---|
24 from twisted.internet import defer | 24 from twisted.internet import defer |
25 from twisted.words.protocols.jabber import jid | 25 from twisted.words.protocols.jabber import jid |
26 from twisted.words.xish.domish import Element, generateElementsNamed | 26 from twisted.words.xish.domish import Element, generateElementsNamed |
27 from sat.core import exceptions | 27 from sat.core import exceptions |
28 from wokkel import disco, data_form, iwokkel | 28 from wokkel import disco, data_form, iwokkel |
29 from wokkel import rsm as wokkel_rsm | |
29 from zope.interface import implements | 30 from zope.interface import implements |
30 from feed import date | 31 from feed import date |
31 import uuid | 32 import uuid |
32 import urllib | 33 import urllib |
33 | 34 |
50 ACCESS_TYPE_MAP = { 'PUBLIC': 'open', | 51 ACCESS_TYPE_MAP = { 'PUBLIC': 'open', |
51 'GROUP': 'roster', | 52 'GROUP': 'roster', |
52 'JID': None, #JID is not yet managed | 53 'JID': None, #JID is not yet managed |
53 } | 54 } |
54 | 55 |
56 MAX_ITEMS = 5 | |
57 MAX_COMMENTS = 5 | |
58 DO_NOT_COUNT_COMMENTS = -1 # must be lower than 0 | |
59 | |
55 PLUGIN_INFO = { | 60 PLUGIN_INFO = { |
56 "name": "Group blogging throught collections", | 61 "name": "Group blogging throught collections", |
57 "import_name": "GROUPBLOG", | 62 "import_name": "GROUPBLOG", |
58 "type": "MISC", | 63 "type": "MISC", |
59 "protocols": [], | 64 "protocols": [], |
60 "dependencies": ["XEP-0277"], | 65 "dependencies": ["XEP-0277"], |
66 "recommendations": ["XEP-0059"], | |
61 "main": "GroupBlog", | 67 "main": "GroupBlog", |
62 "handler": "yes", | 68 "handler": "yes", |
63 "description": _("""Implementation of microblogging with roster access""") | 69 "description": _("""Implementation of microblogging with roster access""") |
64 } | 70 } |
65 | 71 |
77 | 83 |
78 | 84 |
79 class UnknownType(Exception): | 85 class UnknownType(Exception): |
80 pass | 86 pass |
81 | 87 |
82 | |
83 class GroupBlog(object): | 88 class GroupBlog(object): |
84 """This class use a SàT PubSub Service to manage access on microblog""" | 89 """This class use a SàT PubSub Service to manage access on microblog""" |
85 | 90 |
86 def __init__(self, host): | 91 def __init__(self, host): |
87 log.info(_("Group blog plugin initialization")) | 92 log.info(_("Group blog plugin initialization")) |
102 host.bridge.addMethod("sendGroupBlogComment", ".plugin", in_sign='ssa{ss}s', out_sign='', | 107 host.bridge.addMethod("sendGroupBlogComment", ".plugin", in_sign='ssa{ss}s', out_sign='', |
103 method=self.sendGroupBlogComment, | 108 method=self.sendGroupBlogComment, |
104 async=True) | 109 async=True) |
105 | 110 |
106 host.bridge.addMethod("getGroupBlogs", ".plugin", | 111 host.bridge.addMethod("getGroupBlogs", ".plugin", |
107 in_sign='sass', out_sign='aa{ss}', | 112 in_sign='sasa{ss}bs', out_sign='(aa{ss}a{ss})', |
108 method=self.getGroupBlogs, | 113 method=self.getGroupBlogs, |
109 async=True) | 114 async=True) |
110 | 115 |
111 host.bridge.addMethod("getGroupBlogsWithComments", ".plugin", | 116 host.bridge.addMethod("getGroupBlogsWithComments", ".plugin", |
112 in_sign='sass', out_sign='a(a{ss}aa{ss})', | 117 in_sign='sasa{ss}is', out_sign='(a(a{ss}(aa{ss}a{ss}))a{ss})', |
113 method=self.getGroupBlogsWithComments, | 118 method=self.getGroupBlogsWithComments, |
114 async=True) | 119 async=True) |
115 | 120 |
116 host.bridge.addMethod("getLastGroupBlogs", ".plugin", | 121 host.bridge.addMethod("getGroupBlogsAtom", ".plugin", |
117 in_sign='sis', out_sign='aa{ss}', | 122 in_sign='sa{ss}s', out_sign='s', |
118 method=self.getLastGroupBlogs, | 123 method=self.getGroupBlogsAtom, |
119 async=True) | 124 async=True) |
120 | 125 |
121 host.bridge.addMethod("getLastGroupBlogsAtom", ".plugin", | 126 host.bridge.addMethod("getMassiveGroupBlogs", ".plugin", |
122 in_sign='sis', out_sign='s', | 127 in_sign='sasa{ss}s', out_sign='a{s(aa{ss}a{ss})}', |
123 method=self.getLastGroupBlogsAtom, | 128 method=self._getMassiveGroupBlogs, |
124 async=True) | |
125 | |
126 host.bridge.addMethod("getMassiveLastGroupBlogs", ".plugin", | |
127 in_sign='sasis', out_sign='a{saa{ss}}', | |
128 method=self._getMassiveLastGroupBlogs, | |
129 async=True) | 129 async=True) |
130 | 130 |
131 host.bridge.addMethod("getGroupBlogComments", ".plugin", | 131 host.bridge.addMethod("getGroupBlogComments", ".plugin", |
132 in_sign='sss', out_sign='aa{ss}', | 132 in_sign='ssa{ss}s', out_sign='(aa{ss}a{ss})', |
133 method=self.getGroupBlogComments, | 133 method=self.getGroupBlogComments, |
134 async=True) | 134 async=True) |
135 | 135 |
136 host.bridge.addMethod("subscribeGroupBlog", ".plugin", in_sign='ss', out_sign='', | 136 host.bridge.addMethod("subscribeGroupBlog", ".plugin", in_sign='ss', out_sign='', |
137 method=self.subscribeGroupBlog, | 137 method=self.subscribeGroupBlog, |
513 | 513 |
514 for item in items: | 514 for item in items: |
515 d_list.append(self.item2gbdata(item).addCallback(cb)) | 515 d_list.append(self.item2gbdata(item).addCallback(cb)) |
516 return defer.DeferredList(d_list, consumeErrors=True).addCallback(lambda result: [value for (success, value) in result if success]) | 516 return defer.DeferredList(d_list, consumeErrors=True).addCallback(lambda result: [value for (success, value) in result if success]) |
517 | 517 |
518 def __getGroupBlogs(self, pub_jid_s, max_items=10, item_ids=None, profile_key=C.PROF_KEY_NONE): | 518 def _getOrCountComments(self, items, max=0, profile_key=C.PROF_KEY_NONE): |
519 """Get and/or count the comments of the given items. | |
520 | |
521 @param items (list): items to consider. | |
522 @param max (int): maximum number of comments to get, if 0 only count | |
523 them. The count is set to the item data of key "comments_count". | |
524 @param profile_key (str): %(doc_profile_key)s | |
525 @return: a deferred list of: | |
526 - if max == 0: microblog data | |
527 - else: couple (dict, (list[dict], dict)) containing: | |
528 - microblog data (main item) | |
529 - couple (comments data, RSM response data for the comments) | |
530 """ | |
531 def comments_cb(comments_data, entry): | |
532 entry['comments_count'] = comments_data[1]['count'] | |
533 return (entry, comments_data) if max > 0 else entry | |
534 | |
535 assert(max >= 0) | |
536 d_list = [] | |
537 for entry in items: | |
538 if entry.get('comments', False): | |
539 comments_rsm = {'max': max} | |
540 d = self.getGroupBlogComments(entry['comments_service'], entry['comments_node'], rsm=comments_rsm, profile_key=profile_key) | |
541 d.addCallback(comments_cb, entry) | |
542 d_list.append(d) | |
543 else: | |
544 if max > 0: | |
545 d_list.append(defer.succeed((entry, ([], {})))) | |
546 else: | |
547 d_list.append(defer.succeed(entry)) | |
548 deferred_list = defer.DeferredList(d_list) | |
549 deferred_list.addCallback(lambda result: [value for (success, value) in result if success]) | |
550 return deferred_list | |
551 | |
552 def __getGroupBlogs(self, pub_jid_s, item_ids=None, rsm=None, max_comments=0, profile_key=C.PROF_KEY_NONE): | |
519 """Retrieve previously published items from a publish subscribe node. | 553 """Retrieve previously published items from a publish subscribe node. |
520 @param pub_jid_s: jid of the publisher | 554 |
521 @param max_items: how many microblogs we want to get (see XEP-0060 #6.5.7) | |
522 @param item_ids: list of microblogs items IDs | |
523 @param profile_key: profile key | |
524 @return: list of microblog data (dict) | |
525 """ | |
526 pub_jid = jid.JID(pub_jid_s) | |
527 | |
528 def initialised(result): | |
529 profile, client = result | |
530 d = self.host.plugins["XEP-0060"].getItems(client.item_access_pubsub, self.getNodeName(pub_jid), | |
531 max_items=max_items, item_ids=item_ids, profile_key=profile_key) | |
532 d.addCallback(self._itemsConstruction, pub_jid, client) | |
533 d.addErrback(lambda ignore: {}) # TODO: more complete error management (log !) | |
534 return d | |
535 | |
536 #TODO: we need to use the server corresponding the the host of the jid | |
537 return self._initialise(profile_key).addCallback(initialised) | |
538 | |
539 def getGroupBlogs(self, pub_jid_s, item_ids=None, profile_key=C.PROF_KEY_NONE): | |
540 """Get the published microblogs of the specified IDs. If item_ids is | |
541 None, the result would be the same than calling getLastGroupBlogs | |
542 with the default value for the attribute max_items. | |
543 @param pub_jid_s: jid of the publisher | 555 @param pub_jid_s: jid of the publisher |
544 @param item_ids: list of microblogs items IDs | 556 @param item_ids: list of microblogs items IDs |
545 @param profile_key: profile key | 557 @param rsm (dict): RSM request data |
546 @return: list of microblog data (dict) | 558 @param max_comments (int): maximum number of comments to retrieve |
547 """ | 559 @param profile_key (str): %(doc_profile_key)s |
548 return self.__getGroupBlogs(pub_jid_s, item_ids=item_ids, profile_key=profile_key) | 560 @return: a deferred couple (list, dict) containing: |
549 | 561 - list of: |
550 def getGroupBlogsWithComments(self, pub_jid_s, item_ids=None, profile_key=C.PROF_KEY_NONE): | 562 - if max_comments == 0: microblog data |
563 - else: couple (dict, (list[dict], dict)) containing: | |
564 - microblog data (main item) | |
565 - couple (comments data, RSM response data for the comments) | |
566 - RSM response data | |
567 """ | |
568 pub_jid = jid.JID(pub_jid_s) | |
569 | |
570 def cb(items, client): | |
571 d = self._itemsConstruction(items, pub_jid, client) | |
572 if max_comments == DO_NOT_COUNT_COMMENTS: | |
573 return d | |
574 return d.addCallback(self._getOrCountComments, max_comments, profile_key) | |
575 | |
576 return DeferredItems(self, cb, None, profile_key).get(self.getNodeName(pub_jid), item_ids, rsm=rsm) | |
577 | |
578 def getGroupBlogs(self, pub_jid_s, item_ids=None, rsm=None, count_comments=True, profile_key=C.PROF_KEY_NONE): | |
579 """Get the published microblogs of the specified IDs. If item_ids is | |
580 None, the result would be the same than calling getGroupBlogs | |
581 with the default value for the attribute max_items. | |
582 | |
583 @param pub_jid_s: jid of the publisher | |
584 @param item_ids: list of microblogs items IDs | |
585 @param rsm (dict): RSM request data | |
586 @param count_comments (bool): also count the comments if True | |
587 @param profile_key (str): %(doc_profile_key)s | |
588 @return: a deferred couple (list, dict) containing: | |
589 - list of microblog data | |
590 - RSM response data | |
591 """ | |
592 max_comments = 0 if count_comments else DO_NOT_COUNT_COMMENTS | |
593 return self.__getGroupBlogs(pub_jid_s, item_ids=item_ids, rsm=rsm, max_comments=max_comments, profile_key=profile_key) | |
594 | |
595 def getGroupBlogsWithComments(self, pub_jid_s, item_ids=None, rsm=None, max_comments=None, profile_key=C.PROF_KEY_NONE): | |
551 """Get the published microblogs of the specified IDs and their comments. If | 596 """Get the published microblogs of the specified IDs and their comments. If |
552 item_ids is None, returns the last published microblogs and their comments. | 597 item_ids is None, returns the last published microblogs and their comments. |
598 | |
553 @param pub_jid_s: jid of the publisher | 599 @param pub_jid_s: jid of the publisher |
554 @param item_ids: list of microblogs items IDs | 600 @param item_ids: list of microblogs items IDs |
555 @param profile_key: profile key | 601 @param rsm (dict): RSM request data |
556 @return: list of couple (microblog data, list of microblog data) | 602 @param max_comments (int): maximum number of comments to retrieve |
557 """ | 603 @param profile_key (str): %(doc_profile_key)s |
558 def get_comments(data): | 604 @return: a deferred couple (list, dict) containing: |
559 d_list = [] | 605 - list of couple (dict, (list[dict], dict)) containing: |
560 for entry in data: | 606 - microblog data (main item) |
561 if entry.get('comments', False): | 607 - couple (comments data, RSM response data for the comments) |
562 d = self.getGroupBlogComments(entry['comments_service'], entry['comments_node'], profile_key=profile_key) | 608 - RSM response data |
563 d.addCallback(lambda data: (entry, data)) | 609 """ |
564 d_list.append(d) | 610 if max_comments is None: |
565 else: | 611 max_comments = MAX_COMMENTS |
566 d_list.append(defer.succeed((entry, []))) | 612 assert(max_comments > 0) # otherwise the return signature is not the same |
567 deferred_list = defer.DeferredList(d_list) | 613 return self.__getGroupBlogs(pub_jid_s, item_ids=item_ids, rsm=rsm, max_comments=max_comments, profile_key=profile_key) |
568 deferred_list.addCallback(lambda result: [value for (success, value) in result if success]) | 614 |
569 return deferred_list | 615 def getGroupBlogsAtom(self, pub_jid_s, rsm=None, profile_key=C.PROF_KEY_NONE): |
570 | |
571 d = self.__getGroupBlogs(pub_jid_s, item_ids=item_ids, profile_key=profile_key) | |
572 d.addCallback(get_comments) | |
573 return d | |
574 | |
575 def getLastGroupBlogs(self, pub_jid_s, max_items=10, profile_key=C.PROF_KEY_NONE): | |
576 """Get the last published microblogs | |
577 @param pub_jid_s: jid of the publisher | |
578 @param max_items: how many microblogs we want to get (see XEP-0060 #6.5.7) | |
579 @param profile_key: profile key | |
580 @return: list of microblog data (dict) | |
581 """ | |
582 return self.__getGroupBlogs(pub_jid_s, max_items=max_items, profile_key=profile_key) | |
583 | |
584 def getLastGroupBlogsAtom(self, pub_jid_s, max_items=10, profile_key=C.PROF_KEY_NONE): | |
585 """Get the atom feed of the last published microblogs | 616 """Get the atom feed of the last published microblogs |
586 @param pub_jid: jid of the publisher | 617 @param pub_jid: jid of the publisher |
587 @param max_items: how many microblogs we want to get (see XEP-0060 #6.5.7) | |
588 @param profile_key: profile key | 618 @param profile_key: profile key |
589 @return: atom XML feed (unicode) | 619 @return: a deferred unicode (atom XML feed) |
590 """ | 620 """ |
591 pub_jid = jid.JID(pub_jid_s) | 621 pub_jid = jid.JID(pub_jid_s) |
592 | 622 |
593 def removeAllURIs(element): | 623 def removeAllURIs(element): |
594 """Recursively remove the URIs of the element and its children. | 624 """Recursively remove the URIs of the element and its children. |
615 entry = item.firstChildElement() | 645 entry = item.firstChildElement() |
616 removeAllURIs(entry) | 646 removeAllURIs(entry) |
617 feed += " " + entry.toXml() + "\n" | 647 feed += " " + entry.toXml() + "\n" |
618 return feed + "</feed>" | 648 return feed + "</feed>" |
619 | 649 |
620 def initialised(result): | 650 def cb(items, client): |
621 profile, client = result | 651 return items2feed(items, pub_jid, client) |
622 d = self.host.plugins["XEP-0060"].getItems(client.item_access_pubsub, self.getNodeName(pub_jid), | 652 |
623 max_items=max_items, profile_key=profile_key) | 653 d = DeferredItems(self, cb, lambda dummy: '', profile_key).get(self.getNodeName(pub_jid), rsm=rsm) |
624 d.addCallback(items2feed, pub_jid, client) | 654 return d.addCallback(lambda res: res[0]) |
625 d.addErrback(lambda ignore: '') # TODO: more complete error management (log !) | 655 |
626 return d | 656 def getGroupBlogComments(self, service_s, node, rsm=None, profile_key=C.PROF_KEY_NONE): |
627 | |
628 #TODO: we need to use the server corresponding the the host of the jid | |
629 return self._initialise(profile_key).addCallback(initialised) | |
630 | |
631 def getGroupBlogComments(self, service_s, node, profile_key=C.PROF_KEY_NONE): | |
632 """Get all comments of given node | 657 """Get all comments of given node |
633 @param service_s: service hosting the node | 658 @param service_s: service hosting the node |
634 @param node: comments node | 659 @param node: comments node |
635 @param profile_key: profile key | 660 @param profile_key: profile key |
636 @return: list of microblog data (dict) | 661 @return: a deferred couple (list, dict) containing: |
662 - list of microblog data | |
663 - RSM response data | |
637 """ | 664 """ |
638 service = jid.JID(service_s) | 665 service = jid.JID(service_s) |
639 | 666 |
640 def initialised(result): | 667 def cb(items, client): |
641 profile, client = result | 668 return self._handleCommentsItems(items, service, node) |
642 d = self.host.plugins["XEP-0060"].getItems(service, node, | 669 |
643 profile_key=profile_key) | 670 return DeferredItems(self, cb, None, profile_key).get(node, rsm=rsm) |
644 d.addCallback(self._handleCommentsItems, service, node) | 671 |
645 d.addErrback(lambda ignore: {}) # TODO: more complete error management (log !) | 672 def _getMassiveGroupBlogs(self, publishers_type, publishers, rsm=None, profile_key=C.PROF_KEY_NONE): |
646 return d | |
647 | |
648 #TODO: we need to use the server corresponding the the host of the jid | |
649 return self._initialise(profile_key).addCallback(initialised) | |
650 | |
651 def _getMassiveLastGroupBlogs(self, publishers_type, publishers, max_items=10, profile_key=C.PROF_KEY_NONE): | |
652 if publishers_type == 'JID': | 673 if publishers_type == 'JID': |
653 publishers_jids = [jid.JID(publisher) for publisher in publishers] | 674 publishers_jids = [jid.JID(publisher) for publisher in publishers] |
654 else: | 675 else: |
655 publishers_jids = publishers | 676 publishers_jids = publishers |
656 return self.getMassiveLastGroupBlogs(publishers_type, publishers_jids, max_items, profile_key) | 677 return self.getMassiveGroupBlogs(publishers_type, publishers_jids, rsm, profile_key) |
657 | 678 |
658 @defer.inlineCallbacks | 679 def _getPublishersJIDs(self, publishers_type, publishers, client): |
659 def getMassiveLastGroupBlogs(self, publishers_type, publishers, max_items=10, profile_key=C.PROF_KEY_NONE): | |
660 """Get the last published microblogs for a list of groups or jids | |
661 @param publishers_type: type of the list of publishers (one of "GROUP" or "JID" or "ALL") | |
662 @param publishers: list of publishers, according to "publishers_type" (list of groups or list of jids) | |
663 @param max_items: how many microblogs we want to get | |
664 @param profile_key: profile key | |
665 """ | |
666 #TODO: custom exception | 680 #TODO: custom exception |
667 if publishers_type not in ["GROUP", "JID", "ALL"]: | 681 if publishers_type not in ["GROUP", "JID", "ALL"]: |
668 raise Exception("Bad call, unknown publishers_type") | 682 raise Exception("Bad call, unknown publishers_type") |
669 if publishers_type == "ALL" and publishers: | 683 if publishers_type == "ALL" and publishers: |
670 raise Exception("Publishers list must be empty when getting microblogs for all contacts") | 684 raise Exception("Publishers list must be empty when getting microblogs for all contacts") |
671 profile, client = yield self._initialise(profile_key) | |
672 #TODO: we need to use the server corresponding the the host of the jid | |
673 | 685 |
674 if publishers_type == "ALL": | 686 if publishers_type == "ALL": |
675 contacts = client.roster.getItems() | 687 contacts = client.roster.getItems() |
676 jids = [contact.jid.userhostJID() for contact in contacts] | 688 jids = [contact.jid.userhostJID() for contact in contacts] |
677 elif publishers_type == "GROUP": | 689 elif publishers_type == "GROUP": |
680 jids.extend(client.roster.getJidsFromGroup(_group)) | 692 jids.extend(client.roster.getJidsFromGroup(_group)) |
681 elif publishers_type == 'JID': | 693 elif publishers_type == 'JID': |
682 jids = publishers | 694 jids = publishers |
683 else: | 695 else: |
684 raise UnknownType | 696 raise UnknownType |
685 | 697 return jids |
686 data = {publisher: self.getNodeName(publisher) for publisher in jids} | 698 |
687 d_dict = yield self.host.plugins["XEP-0060"].getItemsFromMany(client.item_access_pubsub, data, max_items=max_items, profile_key=profile) | 699 def getMassiveGroupBlogs(self, publishers_type, publishers, rsm=None, profile_key=C.PROF_KEY_NONE): |
688 | 700 """Get the last published microblogs for a list of groups or jids |
689 def cb(jid): | 701 @param publishers_type (str): type of the list of publishers (one of "GROUP" or "JID" or "ALL") |
690 def res(gbdata): | 702 @param publishers (list): list of publishers, according to publishers_type (list of groups or list of jids) |
691 return (jid.full(), gbdata) | 703 @param rsm (dict): RSM request data, common to all publishers |
692 return res | 704 @param profile_key: profile key |
693 | 705 @return: a deferred dict with: |
694 for publisher, d in d_dict.items(): | 706 - key: publisher (unicode) |
695 d.addCallback(self._itemsConstruction, publisher, client) | 707 - value: couple (list[dict], dict) with: |
696 d.addCallback(cb(publisher)) | 708 - the microblogs data |
697 result = yield defer.DeferredList(d_dict.values(), consumeErrors=False) | 709 - RSM response data |
698 defer.returnValue({value[0]: value[1] for success, value in result if success}) | 710 """ |
711 def cb(items, publisher, client): | |
712 d = self._itemsConstruction(items, publisher, client) | |
713 return d.addCallback(self._getOrCountComments, False, profile_key) | |
714 | |
715 #TODO: we need to use the server corresponding to the host of the jid | |
716 return DeferredItemsFromMany(self, cb, profile_key).get(publishers_type, publishers, rsm=rsm) | |
699 | 717 |
700 def subscribeGroupBlog(self, pub_jid, profile_key=C.PROF_KEY_NONE): | 718 def subscribeGroupBlog(self, pub_jid, profile_key=C.PROF_KEY_NONE): |
701 def initialised(result): | 719 def initialised(result): |
702 profile, client = result | 720 profile, client = result |
703 d = self.host.plugins["XEP-0060"].subscribe(client.item_access_pubsub, self.getNodeName(jid.JID(pub_jid)), | 721 d = self.host.plugins["XEP-0060"].subscribe(client.item_access_pubsub, self.getNodeName(jid.JID(pub_jid)), |
719 """Subscribe microblogs for a list of groups or jids | 737 """Subscribe microblogs for a list of groups or jids |
720 @param publishers_type: type of the list of publishers (one of "GROUP" or "JID" or "ALL") | 738 @param publishers_type: type of the list of publishers (one of "GROUP" or "JID" or "ALL") |
721 @param publishers: list of publishers, according to "publishers_type" (list of groups or list of jids) | 739 @param publishers: list of publishers, according to "publishers_type" (list of groups or list of jids) |
722 @param profile_key: profile key | 740 @param profile_key: profile key |
723 """ | 741 """ |
724 #TODO: custom exception | |
725 if publishers_type not in ["GROUP", "JID", "ALL"]: | |
726 raise Exception("Bad call, unknown publishers_type") | |
727 if publishers_type == "ALL" and publishers: | |
728 raise Exception("Publishers list must be empty when getting microblogs for all contacts") | |
729 profile, client = yield self._initialise(profile_key) | 742 profile, client = yield self._initialise(profile_key) |
730 #TODO: we need to use the server corresponding the the host of the jid | 743 #TODO: we need to use the server corresponding the the host of the jid |
731 | 744 |
732 if publishers_type == "ALL": | 745 jids = self._getPublishersJIDs(publishers_type, publishers, client) |
733 contacts = client.roster.getItems() | |
734 jids = [contact.jid.userhostJID() for contact in contacts] | |
735 elif publishers_type == "GROUP": | |
736 jids = [] | |
737 for _group in publishers: | |
738 jids.extend(client.roster.getJidsFromGroup(_group)) | |
739 elif publishers_type == 'JID': | |
740 jids = publishers | |
741 else: | |
742 raise UnknownType | |
743 | |
744 node_ids = [self.getNodeName(publisher) for publisher in jids] | 746 node_ids = [self.getNodeName(publisher) for publisher in jids] |
745 d_list = yield self.host.plugins["XEP-0060"].subscribeToMany(client.item_access_pubsub, node_ids, profile_key=profile_key) | 747 d_list = yield self.host.plugins["XEP-0060"].subscribeToMany(client.item_access_pubsub, node_ids, profile_key=profile_key) |
746 result = yield defer.DeferredList(d_list, consumeErrors=False) | 748 result = yield defer.DeferredList(d_list, consumeErrors=False) |
747 defer.returnValue(result) | 749 defer.returnValue(None) |
748 | 750 |
749 def deleteAllGroupBlogsAndComments(self, profile_key=C.PROF_KEY_NONE): | 751 def deleteAllGroupBlogsAndComments(self, profile_key=C.PROF_KEY_NONE): |
750 """Delete absolutely all the microblog data that the user has posted""" | 752 """Delete absolutely all the microblog data that the user has posted""" |
751 calls = [self.deleteAllGroupBlogs(profile_key), self.deleteAllGroupBlogsComments(profile_key)] | 753 calls = [self.deleteAllGroupBlogs(profile_key), self.deleteAllGroupBlogsComments(profile_key)] |
752 return defer.DeferredList(calls) | 754 return defer.DeferredList(calls) |
753 | 755 |
754 def deleteAllGroupBlogs(self, profile_key=C.PROF_KEY_NONE): | 756 def deleteAllGroupBlogs(self, profile_key=C.PROF_KEY_NONE): |
755 """Delete all the main items and their comments that the user has posted | 757 """Delete all the main items that the user has posted and their comments. |
756 """ | 758 """ |
757 def initialised(result): | 759 def initialised(result): |
758 profile, client = result | 760 profile, client = result |
759 service = client.item_access_pubsub | 761 service = client.item_access_pubsub |
760 jid_ = client.jid | 762 jid_ = client.jid |
761 | |
762 main_node = self.getNodeName(jid_) | 763 main_node = self.getNodeName(jid_) |
763 d = self.host.plugins["XEP-0060"].deleteNode(service, main_node, profile_key=profile) | 764 |
765 def cb(nodes): | |
766 d_list = [] | |
767 for node in [node for node in nodes if node.endswith(main_node)]: | |
768 d = self.host.plugins["XEP-0060"].deleteNode(service, node, profile_key=profile) | |
769 d.addErrback(lambda failure: log.error(_("Deletion of node %(node)s failed: %(message)s") % | |
770 {'node': node, 'message': failure.getErrorMessage()})) | |
771 d_list.append(d) | |
772 return defer.DeferredList(d_list) | |
773 | |
774 d = self.host.plugins["XEP-0060"].listNodes(service, profile=profile) | |
775 d.addCallback(cb) | |
764 d.addCallback(lambda dummy: log.info(_("All microblog's main items from %s have been deleted!") % jid_.userhost())) | 776 d.addCallback(lambda dummy: log.info(_("All microblog's main items from %s have been deleted!") % jid_.userhost())) |
765 d.addErrback(lambda failure: log.error(_("Deletion of node %(node)s failed: %(message)s") % | |
766 {'node': main_node, 'message': failure.getErrorMessage()})) | |
767 return d | 777 return d |
768 | 778 |
769 return self._initialise(profile_key).addCallback(initialised) | 779 return self._initialise(profile_key).addCallback(initialised) |
770 | 780 |
771 def deleteAllGroupBlogsComments(self, profile_key=C.PROF_KEY_NONE): | 781 def deleteAllGroupBlogsComments(self, profile_key=C.PROF_KEY_NONE): |
780 profile, client = result | 790 profile, client = result |
781 service = client.item_access_pubsub | 791 service = client.item_access_pubsub |
782 jids = [contact.jid.userhostJID() for contact in client.roster.getItems()] | 792 jids = [contact.jid.userhostJID() for contact in client.roster.getItems()] |
783 blogs = [] | 793 blogs = [] |
784 for jid_ in jids: | 794 for jid_ in jids: |
795 if jid_ == client.jid.userhostJID(): | |
796 continue # do not remove the comments on our own node | |
785 main_node = self.getNodeName(jid_) | 797 main_node = self.getNodeName(jid_) |
786 d = self.host.plugins["XEP-0060"].getItems(service, main_node, profile_key=profile) | 798 d = self.host.plugins["XEP-0060"].getItems(service, main_node, profile_key=profile) |
787 d.addCallback(getComments, client) | 799 d.addCallback(lambda res: getComments(res[0], client)) |
788 d.addErrback(lambda failure, main_node: log.error(_("Retrieval of items for node %(node)s failed: %(message)s") % | 800 d.addErrback(lambda failure, main_node: log.error(_("Retrieval of items for node %(node)s failed: %(message)s") % |
789 {'node': main_node, 'message': failure.getErrorMessage()}), main_node) | 801 {'node': main_node, 'message': failure.getErrorMessage()}), main_node) |
790 blogs.append(d) | 802 blogs.append(d) |
791 | 803 |
792 return defer.DeferredList(blogs) | 804 return defer.DeferredList(blogs) |
805 except StopIteration: | 817 except StopIteration: |
806 continue | 818 continue |
807 href = link.getAttribute('href') | 819 href = link.getAttribute('href') |
808 service, node = self.host.plugins['XEP-0277'].parseCommentUrl(href) | 820 service, node = self.host.plugins['XEP-0277'].parseCommentUrl(href) |
809 d = self.host.plugins["XEP-0060"].getItems(service, node, profile_key=profile_key) | 821 d = self.host.plugins["XEP-0060"].getItems(service, node, profile_key=profile_key) |
810 d.addCallback(lambda items, service, node: (service, node, items), service, node) | 822 d.addCallback(lambda items: (service, node, items[0])) |
811 d.addErrback(lambda failure, node: log.error(_("Retrieval of comments for node %(node)s failed: %(message)s") % | 823 d.addErrback(lambda failure, node: log.error(_("Retrieval of comments for node %(node)s failed: %(message)s") % |
812 {'node': node, 'message': failure.getErrorMessage()}), node) | 824 {'node': node, 'message': failure.getErrorMessage()}), node) |
813 comments.append(d) | 825 comments.append(d) |
814 dlist = defer.DeferredList(comments) | 826 dlist = defer.DeferredList(comments) |
815 dlist.addCallback(deleteComments, client) | 827 dlist.addCallback(deleteComments, client) |
848 return defer.DeferredList(deletions) | 860 return defer.DeferredList(deletions) |
849 | 861 |
850 return self._initialise(profile_key).addCallback(initialised) | 862 return self._initialise(profile_key).addCallback(initialised) |
851 | 863 |
852 | 864 |
865 class DeferredItems(): | |
866 """Helper class to retrieve items using XEP-0060""" | |
867 | |
868 def __init__(self, parent, cb, eb=None, profile_key=C.PROF_KEY_NONE): | |
869 """ | |
870 @param parent (GroupBlog): GroupBlog instance | |
871 @param cb (callable): callback method to be applied on items | |
872 @param eb (callable): errback method to be applied on items | |
873 @param profile_key (str): %(doc_profile_key)s | |
874 """ | |
875 self.parent = parent | |
876 self.cb = cb | |
877 self.eb = (lambda dummy: []) if eb is None else eb | |
878 self.profile_key = profile_key | |
879 | |
880 def get(self, node, item_ids=None, sub_id=None, rsm=None): | |
881 """ | |
882 @param node (str): node identifier. | |
883 @param item_ids (list[str]): list of items identifiers. | |
884 @param sub_id (str): optional subscription identifier. | |
885 @param rsm (dict): RSM request data | |
886 @return: a deferred couple (list, dict) containing: | |
887 - list of microblog data | |
888 - RSM response data | |
889 """ | |
890 if rsm is None: | |
891 rsm = {'max': (len(item_ids) if item_ids else MAX_ITEMS)} | |
892 | |
893 def initialised(result): | |
894 profile, client = result | |
895 rsm_ = wokkel_rsm.RSMRequest(**rsm) | |
896 d = self.parent.host.plugins["XEP-0060"].getItems(client.item_access_pubsub, | |
897 node, rsm_.max, | |
898 item_ids, sub_id, rsm_, | |
899 profile_key=profile) | |
900 | |
901 def cb(result): | |
902 d = defer.maybeDeferred(self.cb, result[0], client) | |
903 return d.addCallback(lambda items: (items, result[1])) | |
904 | |
905 d.addCallbacks(cb, self.eb) | |
906 return d | |
907 | |
908 #TODO: we need to use the server corresponding to the host of the jid | |
909 return self.parent._initialise(self.profile_key).addCallback(initialised) | |
910 | |
911 | |
912 class DeferredItemsFromMany(): | |
913 def __init__(self, parent, cb, profile_key=C.PROF_KEY_NONE): | |
914 """ | |
915 @param parent (GroupBlog): GroupBlog instance | |
916 @param cb (callable): callback method to be applied on items | |
917 @param profile_key (str): %(doc_profile_key)s | |
918 """ | |
919 self.parent = parent | |
920 self.cb = cb | |
921 self.profile_key = profile_key | |
922 | |
923 def __buildData(self, publishers_type, publishers, client): | |
924 jids = self.parent._getPublishersJIDs(publishers_type, publishers, client) | |
925 return {publisher: self.parent.getNodeName(publisher) for publisher in jids} | |
926 | |
927 def get(self, publishers_type, publishers, sub_id=None, rsm=None): | |
928 """ | |
929 @param publishers_type (str): type of the list of publishers (one of "GROUP" or "JID" or "ALL") | |
930 @param publishers (list): list of publishers, according to publishers_type (list of groups or list of jids) | |
931 @param sub_id (str): optional subscription identifier. | |
932 @param rsm (dict): RSM request data | |
933 @return: a deferred dict with: | |
934 - key: publisher (unicode) | |
935 - value: couple (list[dict], dict) with: | |
936 - the microblogs data | |
937 - RSM response data | |
938 """ | |
939 if rsm is None: | |
940 rsm = {'max': MAX_ITEMS} | |
941 | |
942 def initialised(result): | |
943 profile, client = result | |
944 | |
945 data = self.__buildData(publishers_type, publishers, client) | |
946 rsm_ = wokkel_rsm.RSMRequest(**rsm) | |
947 d = self.parent.host.plugins["XEP-0060"].getItemsFromMany(client.item_access_pubsub, | |
948 data, rsm_.max, sub_id, | |
949 rsm_, profile_key=profile) | |
950 | |
951 def cb(publisher): | |
952 def callback(result): | |
953 d = defer.maybeDeferred(self.cb, result[0], publisher, client) | |
954 d.addCallback(lambda items: (publisher.full(), (items, result[1]))) | |
955 return d | |
956 return callback | |
957 | |
958 def cb_list(result): | |
959 return {value[0]: value[1] for success, value in result if success} | |
960 | |
961 def main_cb(result): | |
962 d_list = [] | |
963 for publisher, d_items in result.items(): | |
964 # XXX: trick needed as publisher is a loop variable | |
965 d_list.append(d_items.addCallback(cb(publisher))) | |
966 return defer.DeferredList(d_list, consumeErrors=False).addCallback(cb_list) | |
967 | |
968 d.addCallback(main_cb) | |
969 return d | |
970 | |
971 #TODO: we need to use the server corresponding to the host of the jid | |
972 return self.parent._initialise(self.profile_key).addCallback(initialised) | |
973 | |
974 | |
853 class GroupBlog_handler(XMPPHandler): | 975 class GroupBlog_handler(XMPPHandler): |
854 implements(iwokkel.IDisco) | 976 implements(iwokkel.IDisco) |
855 | 977 |
856 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): | 978 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): |
857 return [disco.DiscoFeature(NS_GROUPBLOG)] | 979 return [disco.DiscoFeature(NS_GROUPBLOG)] |