diff --git a/src/plugins/plugin_exp_events.py b/src/plugins/plugin_exp_events.py
index dc5ba733..69d0ec5e 100755
--- a/src/plugins/plugin_exp_events.py
+++ b/src/plugins/plugin_exp_events.py
@@ -18,11 +18,14 @@
# along with this program. If not, see .
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 uri_parse
from twisted.internet import defer
-from twisted.words.protocols.jabber import jid
+from twisted.words.protocols.jabber import jid, error
from twisted.words.xish import domish
from wokkel import pubsub
@@ -49,22 +52,218 @@ class Events(object):
self.host = host
self._p = self.host.plugins["XEP-0060"]
host.bridge.addMethod("eventGet", ".plugin",
- in_sign='sss', out_sign='a{ss}',
+ in_sign='ssss', out_sign='(ia{ss})',
method=self._eventGet,
async=True)
- host.bridge.addMethod("eventSet", ".plugin",
+ host.bridge.addMethod("eventCreate", ".plugin",
+ in_sign='ia{ss}ssss', out_sign='s',
+ method=self._eventCreate,
+ async=True)
+ host.bridge.addMethod("eventModify", ".plugin",
+ in_sign='sssia{ss}s', out_sign='',
+ method=self._eventModify,
+ async=True)
+ host.bridge.addMethod("eventInviteeGet", ".plugin",
+ in_sign='sss', out_sign='a{ss}',
+ method=self._eventInviteeGet,
+ async=True)
+ host.bridge.addMethod("eventInviteeSet", ".plugin",
in_sign='ssa{ss}s', out_sign='',
- method=self._eventSet,
+ method=self._eventInviteeSet,
async=True)
- def _eventGet(self, service, node, profile_key):
+ def _eventGet(self, service, node, id_=u'', profile_key=C.PROF_KEY_NONE):
service = jid.JID(service) if service else None
node = node if node else NS_EVENT
client = self.host.getClient(profile_key)
- return self.eventGet(client, service, node)
+ return self.eventGet(client, service, node, id_)
@defer.inlineCallbacks
- def eventGet(self, client, service, node):
+ def eventGet(self, client, service, node, id_=NS_EVENT):
+ """Retrieve event data
+
+ @param service(unicode, None): PubSub service
+ @param node(unicode): PubSub node of the event
+ @param id_(unicode): id_ with even data
+ @return (tuple[int, dict[unicode, unicode]): event data:
+ - timestamp of the event
+ - event metadata where key can be:
+ location: location of the event
+ image: URL of a picture to use to represent event
+ background-image: URL of a picture to use in background
+ an empty dict is returned if nothing has been answered yed
+ """
+ if not id_:
+ id_ = NS_EVENT
+ items, metadata = yield self._p.getItems(service, node, item_ids=[id_], profile_key=client.profile)
+ try:
+ event_elt = next(items[0].elements(NS_EVENT, u'event'))
+ except IndexError:
+ raise exceptions.NotFound(_(u"No event with this id has been found"))
+
+ try:
+ timestamp = utils.date_parse(next(event_elt.elements(NS_EVENT, "date")))
+ except StopIteration:
+ timestamp = -1
+
+ data = {}
+
+ for key in (u'name',):
+ try:
+ data[key] = event_elt[key]
+ except KeyError:
+ continue
+
+ for elt_name in (u'description',):
+ try:
+ elt = next(event_elt.elements(NS_EVENT, elt_name))
+ except StopIteration:
+ continue
+ else:
+ data[elt_name] = unicode(elt)
+
+ for elt_name in (u'image', 'background-image'):
+ try:
+ image_elt = next(event_elt.elements(NS_EVENT, elt_name))
+ data[elt_name] = image_elt['src']
+ except StopIteration:
+ continue
+ except KeyError:
+ log.warning(_(u'no src found for image'))
+
+ for uri_type in (u'invitees', u'blog'):
+ try:
+ elt = next(event_elt.elements(NS_EVENT, 'invitees'))
+ uri = data[uri_type + u'_uri'] = elt['uri']
+ uri_data = uri_parse.parseXMPPUri(uri)
+ if uri_data[u'type'] != u'pubsub':
+ raise ValueError
+ except StopIteration:
+ log.warning(_(u"no {uri_type} element found!").format(uri_type=uri_type))
+ except KeyError:
+ log.warning(_(u"incomplete {uri_type} element").format(uri_type=uri_type))
+ except ValueError:
+ log.warning(_(u"bad {uri_type} element").format(uri_type=uri_type))
+ else:
+ data[uri_type + u'_service'] = uri_data[u'path']
+ data[uri_type + u'_node'] = uri_data[u'node']
+
+ for meta_elt in event_elt.elements(NS_EVENT, 'meta'):
+ key = meta_elt[u'name']
+ if key in data:
+ log.warning(u'Ignoring conflicting meta element: {xml}'.format(xml=meta_elt.toXml()))
+ continue
+ data[key] = unicode(meta_elt)
+
+ defer.returnValue((timestamp, data))
+
+ def _eventCreate(self, timestamp, data, service, node, id_=u'', profile_key=C.PROF_KEY_NONE):
+ service = jid.JID(service) if service else None
+ node = node if node else NS_EVENT
+ client = self.host.getClient(profile_key)
+ return self.eventCreate(client, timestamp, data, service, node, id_ or NS_EVENT)
+
+ @defer.inlineCallbacks
+ def eventCreate(self, client, timestamp, data, service, node=None, item_id=NS_EVENT):
+ """Create or replace an event
+
+ @param service(jid.JID, None): PubSub service
+ @param node(unicode, None): PubSub node of the event
+ None will create instant node.
+ @param item_id(unicode): ID of the item to create.
+ @param timestamp(timestamp, None)
+ @param data(dict[unicode, unicode]): data to update
+ dict will be cleared, do a copy if data are still needed
+ key can be:
+ - name: name of the event
+ - description: details
+ - image: main picture of the event
+ - background-image: image to use as background
+ @return (unicode): created node
+ """
+ if not item_id:
+ raise ValueError(_(u"item_id must be set"))
+ if not service:
+ service = client.jid.userhostJID()
+ event_elt = domish.Element((NS_EVENT, 'event'))
+ if timestamp is not None and timestamp != -1:
+ formatted_date = utils.xmpp_date(timestamp)
+ event_elt.addElement((NS_EVENT, 'date'), content=formatted_date)
+ for key in (u'name',):
+ if key in data:
+ event_elt[key] = data.pop(key)
+ for key in (u'description',):
+ if key in data:
+ event_elt.addElement((NS_EVENT, key), content=data.pop(key))
+ for key in (u'image', u'background-image'):
+ if key in data:
+ elt = event_elt.addElement((NS_EVENT, key))
+ elt['src'] = data.pop(key)
+
+ # we first create the invitees and blog nodes (if not specified in data)
+ for uri_type in (u'invitees', u'blog'):
+ key = uri_type + u'_uri'
+ if key not in data:
+ # FIXME: affiliate invitees
+ uri_node = yield self._p.createNode(client, service)
+ yield self._p.setConfiguration(client, service, uri_node, {self._p.OPT_ACCESS_MODEL: self._p.ACCESS_WHITELIST})
+ uri_service = service
+ else:
+ # we suppose that *_service and *_node are present
+ # FIXME: handle cases when they are not
+ uri_service = data.pop(uri_type + u'_service')
+ uri_node = data.pop(uri_type + u'_node')
+ del data[key]
+
+ elt = event_elt.addElement((NS_EVENT, uri_type))
+ elt['uri'] = uri_parse.buildXMPPUri('pubsub', path=uri_service.full(), node=uri_node)
+
+ # remaining data are put in elements
+ for key in data.keys():
+ elt = event_elt.addElement((NS_EVENT, 'meta'), content = data.pop(key))
+ elt['name'] = key
+
+ item_elt = pubsub.Item(id=item_id, payload=event_elt)
+ try:
+ # TODO: check auto-create, no need to create node first if available
+ node = yield self._p.createNode(client, service, nodeIdentifier=node)
+ except error.StanzaError as e:
+ if e.condition == u'conflict':
+ log.debug(_(u"requested node already exists"))
+
+ yield self._p.publish(service, node, items=[item_elt], profile_key=client.profile)
+
+ defer.returnValue(node)
+
+ 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
+ client = self.host.getClient(profile_key)
+ return self.eventModify(client, service, node, id_ or NS_EVENT, timestamp_update or None, data_update)
+
+ @defer.inlineCallbacks
+ def eventModify(self, client, service, node, id_=NS_EVENT, timestamp_update=None, data_update=None):
+ """Update an event
+
+ Similar as create instead that it update existing item instead of
+ creating or replacing it. Params are the same as for [eventCreate].
+ """
+ event_timestamp, event_metadata = yield self.eventGet(client, service, node, id_)
+ new_timestamp = event_timestamp if timestamp_update is None else timestamp_update
+ new_data = event_metadata
+ if data_update:
+ for k, v in data_update.iteritems():
+ new_data[k] = v
+ yield self.eventCreate(client, new_timestamp, new_data, service, node, id_)
+
+ def _eventInviteeGet(self, service, node, profile_key):
+ service = jid.JID(service) if service else None
+ node = node if node else NS_EVENT
+ client = self.host.getClient(profile_key)
+ return self.eventInviteeGet(client, service, node)
+
+ @defer.inlineCallbacks
+ def eventInviteeGet(self, client, service, node):
"""Retrieve attendance from event node
@param service(unicode, None): PubSub service
@@ -74,7 +273,7 @@ class Events(object):
"""
items, metadata = yield self._p.getItems(service, node, item_ids=[client.jid.userhost()], profile_key=client.profile)
try:
- event_elt = next(items[0].elements((NS_EVENT, u'event')))
+ event_elt = next(items[0].elements(NS_EVENT, u'invitee'))
except IndexError:
# no item found, event data are not set yet
defer.returnValue({})
@@ -86,13 +285,13 @@ class Events(object):
continue
defer.returnValue(data)
- def _eventSet(self, service, node, event_data, profile_key):
+ def _eventInviteeSet(self, service, node, event_data, profile_key):
service = jid.JID(service) if service else None
node = node if node else NS_EVENT
client = self.host.getClient(profile_key)
- return self.eventSet(client, service, node, event_data)
+ return self.eventInviteeSet(client, service, node, event_data)
- def eventSet(self, client, service, node, data):
+ def eventInviteeSet(self, client, service, node, data):
"""Set or update attendance data in event node
@param service(unicode, None): PubSub service
@@ -102,7 +301,7 @@ class Events(object):
attend: one of "yes", "no", "maybe"
guests: an int
"""
- event_elt = domish.Element((NS_EVENT, 'event'))
+ event_elt = domish.Element((NS_EVENT, 'invitee'))
for key in (u'attend', u'guests'):
try:
event_elt[key] = data.pop(key)
diff --git a/src/plugins/plugin_xep_0277.py b/src/plugins/plugin_xep_0277.py
index 68a3d558..34073546 100755
--- a/src/plugins/plugin_xep_0277.py
+++ b/src/plugins/plugin_xep_0277.py
@@ -31,7 +31,7 @@ from sat.tools import sat_defer
from sat.tools import utils
from sat.tools.common import data_format
-# XXX: tmp.pubsub is actually use instead of wokkel version
+# XXX: tmp.pubsub is actually used instead of wokkel version
from wokkel import pubsub
import uuid
import time