Mercurial > libervia-backend
view src/plugins/plugin_exp_events.py @ 2243:5e12fc5ae52a
plugin events: separation of event node and invitees node
- event node is handling the main metadata of the event, and invitees node handle the invitations/invitees answers
- invitees and blog node are automatically created and associated to the event, except if they are specified (in which cas the existing one are used and attached to the event node)
- extra metadata are added to <meta> elements
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 19 May 2017 12:43:41 +0200 |
parents | 230fc5b609a8 |
children | e8641b7718dc |
line wrap: on
line source
#!/usr/bin/env python2 # -*- coding: utf-8 -*- # SAT plugin to detect language (experimental) # Copyright (C) 2009-2016 Jérôme Poisson (goffi@goffi.org) # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # 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 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, error from twisted.words.xish import domish from wokkel import pubsub PLUGIN_INFO = { C.PI_NAME: "Event plugin", C.PI_IMPORT_NAME: "events", C.PI_TYPE: "EXP", C.PI_PROTOCOLS: [], C.PI_DEPENDENCIES: ["XEP-0060"], C.PI_MAIN: "Events", C.PI_HANDLER: "no", C.PI_DESCRIPTION: _("""Experimental implementation of XMPP events management""") } NS_EVENT = 'org.salut-a-toi.event:0' class Events(object): """Q&D module to handle event attendance answer, experimentation only""" def __init__(self, host): log.info(_(u"Event plugin initialization")) self.host = host self._p = self.host.plugins["XEP-0060"] host.bridge.addMethod("eventGet", ".plugin", in_sign='ssss', out_sign='(ia{ss})', method=self._eventGet, async=True) 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._eventInviteeSet, async=True) 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, id_) @defer.inlineCallbacks 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 <meta> 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 @param node(unicode): PubSub node of the event @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(service, node, item_ids=[client.jid.userhost()], profile_key=client.profile) try: event_elt = next(items[0].elements(NS_EVENT, u'invitee')) except IndexError: # no item found, event data are not set yet defer.returnValue({}) data = {} for key in (u'attend', u'guests'): try: data[key] = event_elt[key] except KeyError: continue defer.returnValue(data) 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.eventInviteeSet(client, service, node, event_data) def eventInviteeSet(self, client, service, node, data): """Set or update attendance data in event node @param service(unicode, None): PubSub service @param node(unicode): PubSub node of the event @param data(dict[unicode, unicode]): data to update key can be: attend: one of "yes", "no", "maybe" guests: an int """ event_elt = domish.Element((NS_EVENT, 'invitee')) for key in (u'attend', u'guests'): try: event_elt[key] = data.pop(key) except KeyError: pass item_elt = pubsub.Item(id=client.jid.userhost(), payload=event_elt) return self._p.publish(service, node, items=[item_elt], profile_key=client.profile)