Mercurial > libervia-backend
diff sat/plugins/plugin_exp_invitation.py @ 3462:12dc234f698c
plugin invitation: pubsub invitations:
- new Pubsub invitation plugin, to have a generic way to manage invitation on Pubsub based
features
- `invitePreflight` and `onInvitationPreflight` method can be implemented to customise
invitation for a namespace
- refactored events invitations to use the new plugin
- a Pubsub invitation can now be for a whole node instead of a specific item
- if invitation is for a node, a namespace can be specified to indicate what this node is
about. It is then added in `extra` data
- an element (domish.Element) can be added in `extra` data, it will then be added in the
invitation
- some code modernisation
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 19 Feb 2021 15:50:22 +0100 |
parents | 000b6722bd35 |
children | be6d91572633 |
line wrap: on
line diff
--- a/sat/plugins/plugin_exp_invitation.py Fri Feb 19 15:49:59 2021 +0100 +++ b/sat/plugins/plugin_exp_invitation.py Fri Feb 19 15:50:22 2021 +0100 @@ -16,15 +16,18 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +from typing import Optional +from zope.interface import implementer +from twisted.internet import defer +from twisted.words.protocols.jabber import jid +from twisted.words.protocols.jabber.xmlstream import XMPPHandler +from wokkel import disco, iwokkel from sat.core.i18n import _ from sat.core import exceptions from sat.core.constants import Const as C from sat.core.log import getLogger -from twisted.internet import defer -from twisted.words.protocols.jabber import jid -from wokkel import disco, iwokkel -from zope.interface import implementer -from twisted.words.protocols.jabber.xmlstream import XMPPHandler +from sat.core.xmpp import SatXMPPEntity +from sat.tools import utils log = getLogger(__name__) @@ -123,16 +126,25 @@ invitation_elt['thumb_url'] = thumb_url return mess_data, invitation_elt - def sendPubsubInvitation(self, client, invitee_jid, service, node, - item_id, name, extra): + def sendPubsubInvitation( + self, + client: SatXMPPEntity, + invitee_jid: jid.JID, + service: jid.JID, + node: str, + item_id: Optional[str], + name: Optional[str], + extra: Optional[dict] + ) -> None: """Send an pubsub invitation in a <message> stanza - @param invitee_jid(jid.JID): entitee to send invitation to - @param service(jid.JID): pubsub service - @param node(unicode): pubsub node - @param item_id(unicode): pubsub id - @param name(unicode, None): see [_generateBaseInvitation] - @param extra(dict, None): see [_generateBaseInvitation] + @param invitee_jid: entitee to send invitation to + @param service: pubsub service + @param node: pubsub node + @param item_id: pubsub id + None when the invitation is for a whole node + @param name: see [_generateBaseInvitation] + @param extra: see [_generateBaseInvitation] """ if extra is None: extra = {} @@ -141,8 +153,22 @@ pubsub_elt = invitation_elt.addElement("pubsub") pubsub_elt["service"] = service.full() pubsub_elt["node"] = node - pubsub_elt["item"] = item_id - return client.send(mess_data["xml"]) + if item_id is None: + try: + namespace = extra.pop("namespace") + except KeyError: + raise exceptions.DataError('"namespace" key is missing in "extra" data') + node_data_elt = pubsub_elt.addElement("node_data") + node_data_elt["namespace"] = namespace + try: + node_data_elt.addChild(extra["element"]) + except KeyError: + pass + else: + pubsub_elt["item"] = item_id + if "element" in extra: + invitation_elt.addChild(extra.pop("element")) + client.send(mess_data["xml"]) async def sendFileSharingInvitation( self, client, invitee_jid, service, repos_type=None, namespace=None, path=None, @@ -207,53 +233,61 @@ file_sharing_elt["path"] = path client.send(mess_data["xml"]) - @defer.inlineCallbacks - def _parsePubsubElt(self, client, pubsub_elt): + async def _parsePubsubElt(self, client, pubsub_elt): try: service = jid.JID(pubsub_elt["service"]) node = pubsub_elt["node"] - item_id = pubsub_elt.getAttribute("item") except (RuntimeError, KeyError): - log.warning(_("Bad invitation, ignoring")) - raise exceptions.DataError + raise exceptions.DataError("Bad invitation, ignoring") + + item_id = pubsub_elt.getAttribute("item") + + if item_id is not None: + try: + items, metadata = await self._p.getItems( + client, service, node, item_ids=[item_id] + ) + except Exception as e: + log.warning(_("Can't get item linked with invitation: {reason}").format( + reason=e)) + try: + item_elt = items[0] + except IndexError: + log.warning(_("Invitation was linking to a non existing item")) + raise exceptions.DataError - try: - items, metadata = yield self._p.getItems(client, service, node, - item_ids=[item_id]) - except Exception as e: - log.warning(_("Can't get item linked with invitation: {reason}").format( - reason=e)) - try: - item_elt = items[0] - except IndexError: - log.warning(_("Invitation was linking to a non existing item")) - raise exceptions.DataError + try: + namespace = item_elt.firstChildElement().uri + except Exception as e: + log.warning(_("Can't retrieve namespace of invitation: {reason}").format( + reason = e)) + raise exceptions.DataError - try: - namespace = item_elt.firstChildElement().uri - except Exception as e: - log.warning(_("Can't retrieve namespace of invitation: {reason}").format( - reason = e)) - raise exceptions.DataError + args = [service, node, item_id, item_elt] + else: + try: + node_data_elt = next(pubsub_elt.elements((NS_INVITATION, "node_data"))) + except StopIteration: + raise exceptions.DataError("Bad invitation, ignoring") + namespace = node_data_elt['namespace'] + args = [service, node, None, node_data_elt] - args = [service, node, item_id, item_elt] - defer.returnValue((namespace, args)) + return namespace, args - def _parseFileSharingElt(self, client, file_sharing_elt): + async def _parseFileSharingElt(self, client, file_sharing_elt): try: service = jid.JID(file_sharing_elt["service"]) except (RuntimeError, KeyError): log.warning(_("Bad invitation, ignoring")) raise exceptions.DataError repos_type = file_sharing_elt.getAttribute("type", "files") - namespace = file_sharing_elt.getAttribute("namespace") + sharing_ns = file_sharing_elt.getAttribute("namespace") path = file_sharing_elt.getAttribute("path") - args = [service, repos_type, namespace, path] + args = [service, repos_type, sharing_ns, path] ns_fis = self.host.getNamespace("fis") return ns_fis, args - @defer.inlineCallbacks - def onInvitation(self, message_elt, client): + async def onInvitation(self, message_elt, client): log.debug("invitation received [{profile}]".format(profile=client.profile)) invitation_elt = message_elt.invitation @@ -275,7 +309,7 @@ xml = elt.toXml())) continue try: - namespace, args = yield method(client, elt) + namespace, args = await method(client, elt) except exceptions.DataError: log.warning("Can't parse invitation element: {xml}".format( xml = elt.toXml())) @@ -288,7 +322,7 @@ 'No handler for namespace "{namespace}", invitation ignored') .format(namespace=namespace)) else: - cb(client, name, extra, *args) + await utils.asDeferred(cb, client, namespace, name, extra, *args) @implementer(iwokkel.IDisco) @@ -299,7 +333,10 @@ def connectionInitialized(self): self.xmlstream.addObserver( - INVITATION, self.plugin_parent.onInvitation, client=self.parent + INVITATION, + lambda message_elt: defer.ensureDeferred( + self.plugin_parent.onInvitation(message_elt, client=self.parent) + ), ) def getDiscoInfo(self, requestor, target, nodeIdentifier=""):