diff sat/plugins/plugin_exp_events.py @ 2912:a3faf1c86596

plugin events: refactored invitation and personal lists logic: - invitation logic has been moved to a new generic "plugin_exp_invitation" plugin - plugin_misc_invitations has be rename "plugin_exp_email_invitation" to avoid confusion - personal event list has be refactored to use a new experimental "list of interest", which regroup all interestings items, events or other ones
author Goffi <goffi@goffi.org>
date Sun, 14 Apr 2019 08:21:51 +0200
parents 003b8b4b56a7
children b256e90612d0
line wrap: on
line diff
--- a/sat/plugins/plugin_exp_events.py	Sun Apr 14 08:21:51 2019 +0200
+++ b/sat/plugins/plugin_exp_events.py	Sun Apr 14 08:21:51 2019 +0200
@@ -17,12 +17,11 @@
 # 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/>.
 
+import shortuuid
 from sat.core.i18n import _
 from sat.core import exceptions
 from sat.core.constants import Const as C
 from sat.core.log import getLogger
-
-log = getLogger(__name__)
 from sat.tools import utils
 from sat.tools.common import uri as xmpp_uri
 from sat.tools.common import date_utils
@@ -32,29 +31,24 @@
 from wokkel import disco, iwokkel
 from zope.interface import implements
 from twisted.words.protocols.jabber.xmlstream import XMPPHandler
+from wokkel import pubsub
 
-from wokkel import pubsub
-import shortuuid
+log = getLogger(__name__)
 
 
 PLUGIN_INFO = {
-    C.PI_NAME: "Event plugin",
+    C.PI_NAME: "Events",
     C.PI_IMPORT_NAME: "EVENTS",
     C.PI_TYPE: "EXP",
     C.PI_PROTOCOLS: [],
-    C.PI_DEPENDENCIES: ["XEP-0060"],
-    C.PI_RECOMMENDATIONS: ["INVITATIONS", "XEP-0277"],
+    C.PI_DEPENDENCIES: [u"XEP-0060", u"INVITATION", u"LIST_INTEREST"],
+    C.PI_RECOMMENDATIONS: ["XEP-0277", "EMAIL_INVITATION"],
     C.PI_MAIN: "Events",
     C.PI_HANDLER: "yes",
-    C.PI_DESCRIPTION: _("""Experimental implementation of XMPP events management"""),
+    C.PI_DESCRIPTION: _(u"""Experimental implementation of XMPP events management"""),
 }
 
 NS_EVENT = "org.salut-a-toi.event:0"
-NS_EVENT_LIST = NS_EVENT + "#list"
-NS_EVENT_INVIT = NS_EVENT + "#invitation"
-INVITATION = '/message[@type="chat"]/invitation[@xmlns="{ns_invit}"]'.format(
-    ns_invit=NS_EVENT_INVIT
-)
 
 
 class Events(object):
@@ -64,8 +58,10 @@
         log.info(_(u"Event plugin initialization"))
         self.host = host
         self._p = self.host.plugins["XEP-0060"]
-        self._i = self.host.plugins.get("INVITATIONS")
+        self._i = self.host.plugins.get("EMAIL_INVITATION")
         self._b = self.host.plugins.get("XEP-0277")
+        self.host.plugins[u"INVITATION"].registerNamespace(NS_EVENT,
+                                                           self.register)
         host.bridge.addMethod(
             "eventGet",
             ".plugin",
@@ -229,11 +225,12 @@
         items, metadata = yield self._p.getItems(client, service, node, item_ids=[id_])
         try:
             event_elt = next(items[0].elements(NS_EVENT, u"event"))
+        except StopIteration:
+            raise exceptions.NotFound(_(u"No event element has been found"))
         except IndexError:
             raise exceptions.NotFound(_(u"No event with this id has been found"))
         defer.returnValue(event_elt)
 
-    @defer.inlineCallbacks
     def register(self, client, service, node, event_id, event_elt, creator=False):
         """register evenement in personal events list
 
@@ -244,32 +241,13 @@
             note that this element will be modified in place
         @param creator(bool): True if client's profile is the creator of the node
         """
-        # we save a link to the event in our local list
-        try:
-            # TODO: check auto-create, no need to create node first if available
-            options = {self._p.OPT_ACCESS_MODEL: self._p.ACCESS_WHITELIST}
-            yield self._p.createNode(
-                client,
-                client.jid.userhostJID(),
-                nodeIdentifier=NS_EVENT_LIST,
-                options=options,
-            )
-        except error.StanzaError as e:
-            if e.condition == u"conflict":
-                log.debug(_(u"requested node already exists"))
-        link_elt = event_elt.addElement((NS_EVENT_LIST, "link"))
+        link_elt = event_elt.addElement("link")
         link_elt["service"] = service.full()
         link_elt["node"] = node
         link_elt["item"] = event_id
-        item_id = xmpp_uri.buildXMPPUri(
-            u"pubsub", path=service.full(), node=node, item=event_id
-        )
-        if creator:
-            event_elt["creator"] = "true"
-        item_elt = pubsub.Item(id=item_id, payload=event_elt)
-        yield self._p.publish(
-            client, client.jid.userhostJID(), NS_EVENT_LIST, items=[item_elt]
-        )
+        return self.host.plugins[u'LIST_INTEREST'].registerPubsub(
+            client, NS_EVENT, service, node, event_id, creator,
+            element=event_elt)
 
     def _eventGet(self, service, node, id_=u"", profile_key=C.PROF_KEY_NONE):
         service = jid.JID(service) if service else None
@@ -396,17 +374,11 @@
             yield self.register(client, service, node, event_id, event_elt, creator=True)
         defer.returnValue(node)
 
-    def _eventModify(
-        self,
-        service,
-        node,
-        id_,
-        timestamp_update,
-        data_update,
-        profile_key=C.PROF_KEY_NONE,
-    ):
+    def _eventModify(self, service, node, id_, timestamp_update, data_update,
+                     profile_key=C.PROF_KEY_NONE):
         service = jid.JID(service) if service else None
-        node = node if node else NS_EVENT
+        if not node:
+            raise ValueError(_(u"missing node"))
         client = self.host.getClient(profile_key)
         return self.eventModify(
             client, service, node, id_ or NS_EVENT, timestamp_update or None, data_update
@@ -444,24 +416,24 @@
         return d
 
     @defer.inlineCallbacks
-    def eventsList(self, client, service, node):
+    def eventsList(self, client, service, node=None):
         """Retrieve list of registered events
 
         @return list(tuple(int, dict)): list of events (timestamp + metadata)
         """
-        if not node:
-            node = NS_EVENT_LIST
-        items = yield self._p.getItems(client, service, node)
+        items, metadata = yield self.host.plugins[u'LIST_INTEREST'].listInterests(
+            client, service, node, namespace=NS_EVENT)
         events = []
-        for item in items[0]:
+        for item in items:
             try:
-                event_elt = next(item.elements(NS_EVENT, u"event"))
+                event_elt = next(item.interest.pubsub.elements(NS_EVENT, u"event"))
             except IndexError:
-                log.error(
+                log.warning(
                     _(u"No event found in item {item_id}").format(item_id=item["id"])
                 )
-            timestamp, data = self._parseEventElt(event_elt)
-            events.append((timestamp, data))
+            else:
+                timestamp, data = self._parseEventElt(event_elt)
+                events.append((timestamp, data))
         defer.returnValue(events)
 
     def _eventInviteeGet(self, service, node, profile_key):
@@ -479,12 +451,12 @@
         @return (dict): a dict with current attendance status,
             an empty dict is returned if nothing has been answered yed
         """
-        items, metadata = yield self._p.getItems(
-            client, service, node, item_ids=[client.jid.userhost()]
-        )
         try:
+            items, metadata = yield self._p.getItems(
+                client, service, node, item_ids=[client.jid.userhost()]
+            )
             event_elt = next(items[0].elements(NS_EVENT, u"invitee"))
-        except IndexError:
+        except (exceptions.NotFound, IndexError):
             # no item found, event data are not set yet
             defer.returnValue({})
         data = {}
@@ -542,13 +514,9 @@
                 event_elt = next(item.elements(NS_EVENT, u"invitee"))
             except StopIteration:
                 # no item found, event data are not set yet
-                log.warning(
-                    _(
-                        u"no data found for {item_id} (service: {service}, node: {node})".format(
-                            item_id=item["id"], service=service, node=node
-                        )
-                    )
-                )
+                log.warning(_(
+                    u"no data found for {item_id} (service: {service}, node: {node})"
+                    .format(item_id=item["id"], service=service, node=node)))
             else:
                 data = {}
                 for key in (u"attend", u"guests"):
@@ -559,30 +527,6 @@
                 invitees[item["id"]] = data
         defer.returnValue(invitees)
 
-    def sendMessageInvitation(self, client, invitee_jid, service, node, item_id):
-        """Send an invitation in a <message> stanza
-
-        @param invitee_jid(jid.JID): entitee to send invitation to
-        @param service(jid.JID): pubsub service of the event
-        @param node(unicode): node of the event
-        @param item_id(unicode): id of the event
-        """
-        mess_data = {
-            "from": client.jid,
-            "to": invitee_jid,
-            "uid": "",
-            "message": {},
-            "type": C.MESS_TYPE_CHAT,
-            "subject": {},
-            "extra": {},
-        }
-        client.generateMessageXML(mess_data)
-        event_elt = mess_data["xml"].addElement("invitation", NS_EVENT_INVIT)
-        event_elt["service"] = service.full()
-        event_elt["node"] = node
-        event_elt["item"] = item_id
-        client.send(mess_data["xml"])
-
     def _invite(self, invitee_jid, service, node, item_id, profile):
         client = self.host.getClient(profile)
         service = jid.JID(service) if service else None
@@ -644,23 +588,14 @@
         log.debug(_(u"affiliation set on blog and comments nodes"))
 
         # now we send the invitation
-        self.sendMessageInvitation(client, invitee_jid, service, node, item_id)
+        pubsub_invitation = self.host.plugins[u'PUBSUB_INVITATION']
+        pubsub_invitation.sendPubsubInvitation(client, invitee_jid, service, node,
+                                                item_id)
 
-    def _inviteByEmail(
-        self,
-        service,
-        node,
-        id_=NS_EVENT,
-        email=u"",
-        emails_extra=None,
-        name=u"",
-        host_name=u"",
-        language=u"",
-        url_template=u"",
-        message_subject=u"",
-        message_body=u"",
-        profile_key=C.PROF_KEY_NONE,
-    ):
+    def _inviteByEmail(self, service, node, id_=NS_EVENT, email=u"", emails_extra=None,
+                       name=u"", host_name=u"", language=u"", url_template=u"",
+                       message_subject=u"", message_body=u"",
+                       profile_key=C.PROF_KEY_NONE):
         client = self.host.getClient(profile_key)
         kwargs = {
             u"profile": client.profile,
@@ -708,19 +643,6 @@
         # now that we have a jid, we can send normal invitation
         yield self.invite(client, invitee_jid, service, node, id_)
 
-    @defer.inlineCallbacks
-    def onInvitation(self, message_elt, client):
-        invitation_elt = message_elt.invitation
-        try:
-            service = jid.JID(invitation_elt["service"])
-            node = invitation_elt["node"]
-            event_id = invitation_elt["item"]
-        except (RuntimeError, KeyError):
-            log.warning(_(u"Bad invitation: {xml}").format(xml=message_elt.toXml()))
-
-        event_elt = yield self.getEventElement(client, service, node, event_id)
-        yield self.register(client, service, node, event_id, event_elt, creator=False)
-
 
 class EventsHandler(XMPPHandler):
     implements(iwokkel.IDisco)
@@ -728,20 +650,9 @@
     def __init__(self, plugin_parent):
         self.plugin_parent = plugin_parent
 
-    @property
-    def host(self):
-        return self.plugin_parent.host
-
-    def connectionInitialized(self):
-        self.xmlstream.addObserver(
-            INVITATION, self.plugin_parent.onInvitation, client=self.parent
-        )
-
     def getDiscoInfo(self, requestor, target, nodeIdentifier=""):
         return [
             disco.DiscoFeature(NS_EVENT),
-            disco.DiscoFeature(NS_EVENT_LIST),
-            disco.DiscoFeature(NS_EVENT_INVIT),
         ]
 
     def getDiscoItems(self, requestor, target, nodeIdentifier=""):