Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0277.py @ 3308:384283adcce1
plugins XEP-0059, XEP-0060, XEP-0277, XEP-0313: better serialisation:
`data_format.serialise` is now used for `mbGet`, and RSM/MAM values are not transtyped to
strings anymore. A serialised dict is now used, items are put in the `items` key.
Comments handling has been refactored to use a list for the potentially multiple comments
nodes.
`rsm` data are now in a `rsm` key of the dict, and `mam` data are merged with other
metadata.
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 16 Jul 2020 09:07:20 +0200 |
parents | 84a94b385760 |
children | d49607e3a066 |
comparison
equal
deleted
inserted
replaced
3307:9f0e28137cd0 | 3308:384283adcce1 |
---|---|
101 ) | 101 ) |
102 host.bridge.addMethod( | 102 host.bridge.addMethod( |
103 "mbGet", | 103 "mbGet", |
104 ".plugin", | 104 ".plugin", |
105 in_sign="ssiasa{ss}s", | 105 in_sign="ssiasa{ss}s", |
106 out_sign="(asa{ss})", | 106 out_sign="s", |
107 method=self._mbGet, | 107 method=self._mbGet, |
108 async_=True, | 108 async_=True, |
109 ) | 109 ) |
110 host.bridge.addMethod( | 110 host.bridge.addMethod( |
111 "mbSetAccess", | 111 "mbSetAccess", |
355 microblog_data["published"] = calendar.timegm( | 355 microblog_data["published"] = calendar.timegm( |
356 dateutil.parser.parse(str(published_elt)).utctimetuple() | 356 dateutil.parser.parse(str(published_elt)).utctimetuple() |
357 ) | 357 ) |
358 | 358 |
359 # links | 359 # links |
360 comments = microblog_data['comments'] = [] | |
360 for link_elt in entry_elt.elements(NS_ATOM, "link"): | 361 for link_elt in entry_elt.elements(NS_ATOM, "link"): |
361 if ( | 362 if ( |
362 link_elt.getAttribute("rel") == "replies" | 363 link_elt.getAttribute("rel") == "replies" |
363 and link_elt.getAttribute("title") == "comments" | 364 and link_elt.getAttribute("title") == "comments" |
364 ): | 365 ): |
365 key = check_conflict("comments", True) | 366 uri = link_elt["href"] |
366 microblog_data[key] = link_elt["href"] | 367 comments_data = { |
368 "uri": uri, | |
369 } | |
367 try: | 370 try: |
368 service, node = self.parseCommentUrl(microblog_data[key]) | 371 service, node = self.parseCommentUrl(uri) |
369 except Exception as e: | 372 except Exception as e: |
370 log.warning(f"Can't parse url {microblog_data[key]}: {e}") | 373 log.warning(f"Can't parse comments url: {e}") |
371 del microblog_data[key] | 374 continue |
372 else: | 375 else: |
373 microblog_data["{}_service".format(key)] = service.full() | 376 comments_data["service"] = service.full() |
374 microblog_data["{}_node".format(key)] = node | 377 comments_data["node"] = node |
378 comments.append(comments_data) | |
375 else: | 379 else: |
376 rel = link_elt.getAttribute("rel", "") | 380 rel = link_elt.getAttribute("rel", "") |
377 title = link_elt.getAttribute("title", "") | 381 title = link_elt.getAttribute("title", "") |
378 href = link_elt.getAttribute("href", "") | 382 href = link_elt.getAttribute("href", "") |
379 log.warning( | 383 log.warning( |
587 ), | 591 ), |
588 ) | 592 ) |
589 entry_elt.addElement("id", content=entry_id) # | 593 entry_elt.addElement("id", content=entry_id) # |
590 | 594 |
591 ## comments ## | 595 ## comments ## |
592 if "comments" in data: | 596 for comments_data in data.get('comments', []): |
593 link_elt = entry_elt.addElement("link") | 597 link_elt = entry_elt.addElement("link") |
594 link_elt["href"] = data["comments"] | 598 # XXX: "uri" is set in self._manageComments if not already existing |
599 link_elt["href"] = comments_data["uri"] | |
595 link_elt["rel"] = "replies" | 600 link_elt["rel"] = "replies" |
596 link_elt["title"] = "comments" | 601 link_elt["title"] = "comments" |
597 | 602 |
598 ## final item building ## | 603 ## final item building ## |
599 item_elt = pubsub.Item(id=item_id, payload=entry_elt) | 604 item_elt = pubsub.Item(id=item_id, payload=entry_elt) |
644 | 649 |
645 @defer.inlineCallbacks | 650 @defer.inlineCallbacks |
646 def _manageComments(self, client, mb_data, service, node, item_id, access=None): | 651 def _manageComments(self, client, mb_data, service, node, item_id, access=None): |
647 """Check comments keys in mb_data and create comments node if necessary | 652 """Check comments keys in mb_data and create comments node if necessary |
648 | 653 |
649 if mb_data['comments'] exists, it is used (or mb_data['comments_service'] and/or mb_data['comments_node']), | 654 if a comments node metadata is set in the mb_data['comments'] list, it is used |
650 else it is generated (if allow_comments is True). | 655 otherwise it is generated (if allow_comments is True). |
651 @param mb_data(dict): microblog mb_data | 656 @param mb_data(dict): microblog mb_data |
652 @param service(jid.JID, None): PubSub service of the parent item | 657 @param service(jid.JID, None): PubSub service of the parent item |
653 @param node(unicode): node of the parent item | 658 @param node(unicode): node of the parent item |
654 @param item_id(unicode): id of the parent item | 659 @param item_id(unicode): id of the parent item |
655 @param access(unicode, None): access model | 660 @param access(unicode, None): access model |
656 None to use same access model as parent item | 661 None to use same access model as parent item |
657 """ | 662 """ |
658 # FIXME: if 'comments' already exists in mb_data, | |
659 # it is not used to create the Node | |
660 allow_comments = mb_data.pop("allow_comments", None) | 663 allow_comments = mb_data.pop("allow_comments", None) |
661 if allow_comments is None: | 664 if allow_comments is None: |
662 return | 665 if "comments" in mb_data: |
666 mb_data["allow_comments"] = True | |
667 else: | |
668 # no comments set or requested, nothing to do | |
669 return | |
663 elif allow_comments == False: | 670 elif allow_comments == False: |
664 if "comments" in mb_data: | 671 if "comments" in mb_data: |
665 log.warning( | 672 log.warning( |
666 "comments are not allowed but there is already a comments node, " | 673 "comments are not allowed but there is already a comments node, " |
667 "it may be lost: {uri}".format( | 674 "it may be lost: {uri}".format( |
668 uri=mb_data["comments"] | 675 uri=mb_data["comments"] |
669 ) | 676 ) |
670 ) | 677 ) |
671 del mb_data["comments"] | 678 del mb_data["comments"] |
672 return | 679 return |
680 | |
681 # we have usually a single comment node, but the spec allow several, so we need to | |
682 # handle this in a list | |
683 if len(mb_data.setdefault('comments', [])) == 0: | |
684 # we need at least one comment node | |
685 comments_data = {} | |
686 mb_data['comments'].append({}) | |
673 | 687 |
674 if access is None: | 688 if access is None: |
675 # TODO: cache access models per service/node | 689 # TODO: cache access models per service/node |
676 parent_node_config = yield self._p.getConfiguration(client, service, node) | 690 parent_node_config = yield self._p.getConfiguration(client, service, node) |
677 access = parent_node_config.get(self._p.OPT_ACCESS_MODEL, self._p.ACCESS_OPEN) | 691 access = parent_node_config.get(self._p.OPT_ACCESS_MODEL, self._p.ACCESS_OPEN) |
687 } | 701 } |
688 | 702 |
689 # if other plugins need to change the options | 703 # if other plugins need to change the options |
690 yield self.host.trigger.point("XEP-0277_comments", client, mb_data, options) | 704 yield self.host.trigger.point("XEP-0277_comments", client, mb_data, options) |
691 | 705 |
692 try: | 706 for comments_data in mb_data['comments']: |
693 comments_node = mb_data["comments_node"] | 707 uri = comments_data.get('uri') |
694 except KeyError: | 708 comments_node = comments_data.get('node') |
695 comments_node = self.getCommentsNode(item_id) | 709 try: |
696 else: | 710 comments_service = jid.JID(comments_data["service"]) |
697 if not comments_node: | 711 except KeyError: |
698 raise exceptions.DataError( | 712 comments_service = None |
699 "if comments_node is present, it must not be empty" | 713 |
714 if uri: | |
715 uri_service, uri_node = self.parseCommentUrl(uri) | |
716 if ((comments_node is not None and comments_node!=uri_node) | |
717 or (comments_service is not None and comments_service!=uri_service)): | |
718 raise ValueError( | |
719 f"Incoherence between comments URI ({uri}) and comments_service " | |
720 f"({comments_service}) or comments_node ({comments_node})") | |
721 comments_data['service'] = comments_service = uri_service | |
722 comments_data['node'] = comments_node = uri_node | |
723 else: | |
724 if not comments_node: | |
725 comments_node = self.getCommentsNode(item_id) | |
726 comments_data['node'] = comments_node | |
727 if comments_service is None: | |
728 comments_service = yield self.getCommentsService(client, service) | |
729 if comments_service is None: | |
730 comments_service = client.jid.userhostJID() | |
731 comments_data['service'] = comments_service | |
732 | |
733 comments_data['uri'] = xmpp_uri.buildXMPPUri( | |
734 "pubsub", | |
735 path=comments_service.full(), | |
736 node=comments_node, | |
700 ) | 737 ) |
701 | 738 |
702 try: | 739 try: |
703 comments_service = jid.JID(mb_data["comments_service"]) | 740 yield self._p.createNode(client, comments_service, comments_node, options) |
704 except KeyError: | 741 except error.StanzaError as e: |
705 comments_service = yield self.getCommentsService(client, service) | 742 if e.condition == "conflict": |
706 | 743 log.info( |
707 try: | 744 "node {} already exists on service {}".format( |
708 yield self._p.createNode(client, comments_service, comments_node, options) | 745 comments_node, comments_service |
709 except error.StanzaError as e: | 746 ) |
710 if e.condition == "conflict": | 747 ) |
711 log.info( | 748 else: |
712 "node {} already exists on service {}".format( | 749 raise e |
713 comments_node, comments_service | |
714 ) | |
715 ) | |
716 else: | 750 else: |
717 raise e | 751 if access == self._p.ACCESS_WHITELIST: |
718 else: | 752 # for whitelist access we need to copy affiliations from parent item |
719 if access == self._p.ACCESS_WHITELIST: | 753 comments_affiliations = yield self._p.getNodeAffiliations( |
720 # for whitelist access we need to copy affiliations from parent item | 754 client, service, node |
721 comments_affiliations = yield self._p.getNodeAffiliations( | 755 ) |
722 client, service, node | 756 # …except for "member", that we transform to publisher |
723 ) | 757 # because we wants members to be able to write to comments |
724 # …except for "member", that we transform to publisher | 758 for jid_, affiliation in list(comments_affiliations.items()): |
725 # because we wants members to be able to write to comments | 759 if affiliation == "member": |
726 for jid_, affiliation in list(comments_affiliations.items()): | 760 comments_affiliations[jid_] == "publisher" |
727 if affiliation == "member": | 761 |
728 comments_affiliations[jid_] == "publisher" | 762 yield self._p.setNodeAffiliations( |
729 | 763 client, comments_service, comments_node, comments_affiliations |
730 yield self._p.setNodeAffiliations( | 764 ) |
731 client, comments_service, comments_node, comments_affiliations | |
732 ) | |
733 | |
734 if comments_service is None: | |
735 comments_service = client.jid.userhostJID() | |
736 | |
737 if "comments" in mb_data: | |
738 if not mb_data["comments"]: | |
739 raise exceptions.DataError( | |
740 "if comments is present, it must not be empty" | |
741 ) | |
742 if "comments_node" in mb_data or "comments_service" in mb_data: | |
743 raise exceptions.DataError( | |
744 "You can't use comments_service/comments_node and comments at the " | |
745 "same time" | |
746 ) | |
747 else: | |
748 mb_data["comments"] = self._p.getNodeURI(comments_service, comments_node) | |
749 | 765 |
750 def _mbSend(self, service, node, data, profile_key): | 766 def _mbSend(self, service, node, data, profile_key): |
751 service = jid.JID(service) if service else None | 767 service = jid.JID(service) if service else None |
752 node = node if node else NS_MICROBLOG | 768 node = node if node else NS_MICROBLOG |
753 client = self.host.getClient(profile_key) | 769 client = self.host.getClient(profile_key) |
794 | 810 |
795 ## get ## | 811 ## get ## |
796 | 812 |
797 def _mbGetSerialise(self, data): | 813 def _mbGetSerialise(self, data): |
798 items, metadata = data | 814 items, metadata = data |
799 items = [data_format.serialise(item) for item in items] | 815 metadata['items'] = items |
800 return items, metadata | 816 return data_format.serialise(metadata) |
801 | 817 |
802 def _mbGet(self, service="", node="", max_items=10, item_ids=None, extra_dict=None, | 818 def _mbGet(self, service="", node="", max_items=10, item_ids=None, extra_dict=None, |
803 profile_key=C.PROF_KEY_NONE): | 819 profile_key=C.PROF_KEY_NONE): |
804 """ | 820 """ |
805 @param max_items(int): maximum number of item to get, C.NO_LIMIT for no limit | 821 @param max_items(int): maximum number of item to get, C.NO_LIMIT for no limit |
838 max_items=max_items, | 854 max_items=max_items, |
839 item_ids=item_ids, | 855 item_ids=item_ids, |
840 rsm_request=rsm_request, | 856 rsm_request=rsm_request, |
841 extra=extra, | 857 extra=extra, |
842 ) | 858 ) |
843 mb_data = yield self._p.transItemsDataD(items_data, self.item2mbdata) | 859 mb_data = yield self._p.transItemsDataD( |
860 items_data, self.item2mbdata) | |
844 defer.returnValue(mb_data) | 861 defer.returnValue(mb_data) |
845 | 862 |
846 def parseCommentUrl(self, node_url): | 863 def parseCommentUrl(self, node_url): |
847 """Parse a XMPP URI | 864 """Parse a XMPP URI |
848 | 865 |
1257 for service, node in node_data: | 1274 for service, node in node_data: |
1258 d = deferreds[(service, node)] = self._p.getItems( | 1275 d = deferreds[(service, node)] = self._p.getItems( |
1259 client, service, node, max_items, rsm_request=rsm_request, extra=extra | 1276 client, service, node, max_items, rsm_request=rsm_request, extra=extra |
1260 ) | 1277 ) |
1261 d.addCallback( | 1278 d.addCallback( |
1262 lambda items_data: self._p.transItemsDataD(items_data, self.item2mbdata) | 1279 lambda items_data: self._p.transItemsDataD( |
1280 items_data, self.item2mbdata) | |
1263 ) | 1281 ) |
1264 d.addCallback(getComments) | 1282 d.addCallback(getComments) |
1265 d.addCallback(lambda items_comments_data: ("", items_comments_data)) | 1283 d.addCallback(lambda items_comments_data: ("", items_comments_data)) |
1266 d.addErrback(lambda failure: (str(failure.value), ([], {}))) | 1284 d.addErrback(lambda failure: (str(failure.value), ([], {}))) |
1267 | 1285 |