Mercurial > libervia-backend
comparison sat/plugins/plugin_exp_events.py @ 2616:1cc88adb5142
plugin events: invitations improvments + personal list
- added invitation by <message> mechanism
- added a personal list in PEP which keep events created by user, or in which she has been invited
- new bridge methods: eventsList, and eventInviteByEmail. eventInvite is now used to invite a entity by jid (not email like before)
- on invite, an invitation message is send to invitee's jid
- when an invitation message is received, event is automatically linked in personal list
- when creating a new event, event is automatically linked in personal list
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 21 Jun 2018 01:21:44 +0200 |
parents | 3e4e78de9cca |
children | 56f94936df1e |
comparison
equal
deleted
inserted
replaced
2615:b4ecbcc2fd08 | 2616:1cc88adb5142 |
---|---|
21 from sat.core import exceptions | 21 from sat.core import exceptions |
22 from sat.core.constants import Const as C | 22 from sat.core.constants import Const as C |
23 from sat.core.log import getLogger | 23 from sat.core.log import getLogger |
24 log = getLogger(__name__) | 24 log = getLogger(__name__) |
25 from sat.tools import utils | 25 from sat.tools import utils |
26 from sat.tools.common import uri as uri_parse | 26 from sat.tools.common import uri as xmpp_uri |
27 from sat.tools.common import date_utils | 27 from sat.tools.common import date_utils |
28 from twisted.internet import defer | 28 from twisted.internet import defer |
29 from twisted.words.protocols.jabber import jid, error | 29 from twisted.words.protocols.jabber import jid, error |
30 from twisted.words.xish import domish | 30 from twisted.words.xish import domish |
31 from wokkel import disco, iwokkel | |
32 from zope.interface import implements | |
33 from twisted.words.protocols.jabber.xmlstream import XMPPHandler | |
34 | |
31 from wokkel import pubsub | 35 from wokkel import pubsub |
36 import shortuuid | |
32 | 37 |
33 | 38 |
34 PLUGIN_INFO = { | 39 PLUGIN_INFO = { |
35 C.PI_NAME: "Event plugin", | 40 C.PI_NAME: "Event plugin", |
36 C.PI_IMPORT_NAME: "EVENTS", | 41 C.PI_IMPORT_NAME: "EVENTS", |
37 C.PI_TYPE: "EXP", | 42 C.PI_TYPE: "EXP", |
38 C.PI_PROTOCOLS: [], | 43 C.PI_PROTOCOLS: [], |
39 C.PI_DEPENDENCIES: ["XEP-0060"], | 44 C.PI_DEPENDENCIES: ["XEP-0060"], |
40 C.PI_RECOMMENDATIONS: ["INVITATIONS", "XEP-0277"], | 45 C.PI_RECOMMENDATIONS: ["INVITATIONS", "XEP-0277"], |
41 C.PI_MAIN: "Events", | 46 C.PI_MAIN: "Events", |
42 C.PI_HANDLER: "no", | 47 C.PI_HANDLER: "yes", |
43 C.PI_DESCRIPTION: _("""Experimental implementation of XMPP events management""") | 48 C.PI_DESCRIPTION: _("""Experimental implementation of XMPP events management""") |
44 } | 49 } |
45 | 50 |
46 NS_EVENT = 'org.salut-a-toi.event:0' | 51 NS_EVENT = 'org.salut-a-toi.event:0' |
52 NS_EVENT_LIST = NS_EVENT + '#list' | |
53 NS_EVENT_INVIT = NS_EVENT + '#invitation' | |
54 INVITATION = '/message[@type="chat"]/invitation[@xmlns="{ns_invit}"]'.format( | |
55 ns_invit=NS_EVENT_INVIT) | |
47 | 56 |
48 | 57 |
49 class Events(object): | 58 class Events(object): |
50 """Q&D module to handle event attendance answer, experimentation only""" | 59 """Q&D module to handle event attendance answer, experimentation only""" |
51 | 60 |
65 async=True) | 74 async=True) |
66 host.bridge.addMethod("eventModify", ".plugin", | 75 host.bridge.addMethod("eventModify", ".plugin", |
67 in_sign='sssia{ss}s', out_sign='', | 76 in_sign='sssia{ss}s', out_sign='', |
68 method=self._eventModify, | 77 method=self._eventModify, |
69 async=True) | 78 async=True) |
79 host.bridge.addMethod("eventsList", ".plugin", | |
80 in_sign='sss', out_sign='aa{ss}', | |
81 method=self._eventsList, | |
82 async=True) | |
70 host.bridge.addMethod("eventInviteeGet", ".plugin", | 83 host.bridge.addMethod("eventInviteeGet", ".plugin", |
71 in_sign='sss', out_sign='a{ss}', | 84 in_sign='sss', out_sign='a{ss}', |
72 method=self._eventInviteeGet, | 85 method=self._eventInviteeGet, |
73 async=True) | 86 async=True) |
74 host.bridge.addMethod("eventInviteeSet", ".plugin", | 87 host.bridge.addMethod("eventInviteeSet", ".plugin", |
77 async=True) | 90 async=True) |
78 host.bridge.addMethod("eventInviteesList", ".plugin", | 91 host.bridge.addMethod("eventInviteesList", ".plugin", |
79 in_sign='sss', out_sign='a{sa{ss}}', | 92 in_sign='sss', out_sign='a{sa{ss}}', |
80 method=self._eventInviteesList, | 93 method=self._eventInviteesList, |
81 async=True), | 94 async=True), |
82 host.bridge.addMethod("eventInvite", ".plugin", in_sign='ssssassssssss', out_sign='', | 95 host.bridge.addMethod("eventInvite", ".plugin", in_sign='sssss', out_sign='', |
83 method=self._invite, | 96 method=self._invite, |
84 async=True) | 97 async=True) |
85 | 98 host.bridge.addMethod("eventInviteByEmail", ".plugin", in_sign='ssssassssssss', out_sign='', |
86 def _eventGet(self, service, node, id_=u'', profile_key=C.PROF_KEY_NONE): | 99 method=self._inviteByEmail, |
87 service = jid.JID(service) if service else None | 100 async=True) |
88 node = node if node else NS_EVENT | 101 |
89 client = self.host.getClient(profile_key) | 102 def getHandler(self, client): |
90 return self.eventGet(client, service, node, id_) | 103 return EventsHandler(self) |
91 | 104 |
92 @defer.inlineCallbacks | 105 def _parseEventElt(self, event_elt): |
93 def eventGet(self, client, service, node, id_=NS_EVENT): | 106 """Helper method to parse event element |
94 """Retrieve event data | 107 |
95 | 108 @param (domish.Element): event_elt |
96 @param service(unicode, None): PubSub service | 109 @return (tupple[int, dict[unicode, unicode]): timestamp, event_data |
97 @param node(unicode): PubSub node of the event | 110 """ |
98 @param id_(unicode): id_ with even data | |
99 @return (tuple[int, dict[unicode, unicode]): event data: | |
100 - timestamp of the event | |
101 - event metadata where key can be: | |
102 location: location of the event | |
103 image: URL of a picture to use to represent event | |
104 background-image: URL of a picture to use in background | |
105 """ | |
106 if not id_: | |
107 id_ = NS_EVENT | |
108 items, metadata = yield self._p.getItems(client, service, node, item_ids=[id_]) | |
109 try: | |
110 event_elt = next(items[0].elements(NS_EVENT, u'event')) | |
111 except IndexError: | |
112 raise exceptions.NotFound(_(u"No event with this id has been found")) | |
113 | |
114 try: | 111 try: |
115 timestamp = date_utils.date_parse(next(event_elt.elements(NS_EVENT, "date"))) | 112 timestamp = date_utils.date_parse(next(event_elt.elements(NS_EVENT, "date"))) |
116 except StopIteration: | 113 except StopIteration: |
117 timestamp = -1 | 114 timestamp = -1 |
118 | 115 |
143 | 140 |
144 for uri_type in (u'invitees', u'blog'): | 141 for uri_type in (u'invitees', u'blog'): |
145 try: | 142 try: |
146 elt = next(event_elt.elements(NS_EVENT, uri_type)) | 143 elt = next(event_elt.elements(NS_EVENT, uri_type)) |
147 uri = data[uri_type + u'_uri'] = elt['uri'] | 144 uri = data[uri_type + u'_uri'] = elt['uri'] |
148 uri_data = uri_parse.parseXMPPUri(uri) | 145 uri_data = xmpp_uri.parseXMPPUri(uri) |
149 if uri_data[u'type'] != u'pubsub': | 146 if uri_data[u'type'] != u'pubsub': |
150 raise ValueError | 147 raise ValueError |
151 except StopIteration: | 148 except StopIteration: |
152 log.warning(_(u"no {uri_type} element found!").format(uri_type=uri_type)) | 149 log.warning(_(u"no {uri_type} element found!").format(uri_type=uri_type)) |
153 except KeyError: | 150 except KeyError: |
162 key = meta_elt[u'name'] | 159 key = meta_elt[u'name'] |
163 if key in data: | 160 if key in data: |
164 log.warning(u'Ignoring conflicting meta element: {xml}'.format(xml=meta_elt.toXml())) | 161 log.warning(u'Ignoring conflicting meta element: {xml}'.format(xml=meta_elt.toXml())) |
165 continue | 162 continue |
166 data[key] = unicode(meta_elt) | 163 data[key] = unicode(meta_elt) |
167 | 164 if event_elt.link: |
168 defer.returnValue((timestamp, data)) | 165 link_elt = event_elt.link |
169 | 166 data['service'] = link_elt['service'] |
170 def _eventCreate(self, timestamp, data, service, node, id_=u'', profile_key=C.PROF_KEY_NONE): | 167 data['node'] = link_elt['node'] |
168 data['item'] = link_elt['item'] | |
169 if event_elt.getAttribute('creator') == 'true': | |
170 data['creator'] = True | |
171 return timestamp, data | |
172 | |
173 @defer.inlineCallbacks | |
174 def getEventElement(self, client, service, node, id_): | |
175 """Retrieve event element | |
176 | |
177 @param service(jid.JID): pubsub service | |
178 @param node(unicode): pubsub node | |
179 @param id_(unicode, None): event id | |
180 @return (domish.Element): event element | |
181 @raise exceptions.NotFound: no event element found | |
182 """ | |
183 if not id_: | |
184 id_ = NS_EVENT | |
185 items, metadata = yield self._p.getItems(client, service, node, item_ids=[id_]) | |
186 try: | |
187 event_elt = next(items[0].elements(NS_EVENT, u'event')) | |
188 except IndexError: | |
189 raise exceptions.NotFound(_(u"No event with this id has been found")) | |
190 defer.returnValue(event_elt) | |
191 | |
192 @defer.inlineCallbacks | |
193 def register(self, client, service, node, event_id, event_elt, creator=False): | |
194 """register evenement in personal events list | |
195 | |
196 @param service(jid.JID): pubsub service of the event | |
197 @param node(unicode): event node | |
198 @param event_id(unicode): event id | |
199 @param event_elt(domish.Element): event element | |
200 note that this element will be modified in place | |
201 @param creator(bool): True if client's profile is the creator of the node | |
202 """ | |
203 # we save a link to the event in our local list | |
204 try: | |
205 # TODO: check auto-create, no need to create node first if available | |
206 options = {self._p.OPT_ACCESS_MODEL: self._p.ACCESS_WHITELIST} | |
207 yield self._p.createNode(client, | |
208 client.jid.userhostJID(), | |
209 nodeIdentifier=NS_EVENT_LIST, | |
210 options=options) | |
211 except error.StanzaError as e: | |
212 if e.condition == u'conflict': | |
213 log.debug(_(u"requested node already exists")) | |
214 link_elt = event_elt.addElement((NS_EVENT_LIST, 'link')) | |
215 link_elt["service"] = service.full() | |
216 link_elt["node"] = node | |
217 link_elt["item"] = event_id | |
218 item_id = xmpp_uri.buildXMPPUri(u'pubsub', | |
219 path=service.full(), | |
220 node=node, | |
221 item=event_id) | |
222 if creator: | |
223 event_elt['creator'] = 'true' | |
224 item_elt = pubsub.Item(id=item_id, payload=event_elt) | |
225 yield self._p.publish(client, | |
226 client.jid.userhostJID(), | |
227 NS_EVENT_LIST, | |
228 items=[item_elt]) | |
229 | |
230 def _eventGet(self, service, node, id_=u'', profile_key=C.PROF_KEY_NONE): | |
171 service = jid.JID(service) if service else None | 231 service = jid.JID(service) if service else None |
172 node = node if node else NS_EVENT | 232 node = node if node else NS_EVENT |
173 client = self.host.getClient(profile_key) | 233 client = self.host.getClient(profile_key) |
234 return self.eventGet(client, service, node, id_) | |
235 | |
236 @defer.inlineCallbacks | |
237 def eventGet(self, client, service, node, id_=NS_EVENT): | |
238 """Retrieve event data | |
239 | |
240 @param service(unicode, None): PubSub service | |
241 @param node(unicode): PubSub node of the event | |
242 @param id_(unicode): id_ with even data | |
243 @return (tuple[int, dict[unicode, unicode]): event data: | |
244 - timestamp of the event | |
245 - event metadata where key can be: | |
246 location: location of the event | |
247 image: URL of a picture to use to represent event | |
248 background-image: URL of a picture to use in background | |
249 """ | |
250 event_elt = yield self.getEventElement(client, service, node, id_) | |
251 | |
252 defer.returnValue(self._parseEventElt(event_elt)) | |
253 | |
254 def _eventCreate(self, timestamp, data, service, node, id_=u'', profile_key=C.PROF_KEY_NONE): | |
255 service = jid.JID(service) if service else None | |
256 node = node or None | |
257 client = self.host.getClient(profile_key) | |
258 data[u'register'] = C.bool(data.get(u'register', C.BOOL_FALSE)) | |
174 return self.eventCreate(client, timestamp, data, service, node, id_ or NS_EVENT) | 259 return self.eventCreate(client, timestamp, data, service, node, id_ or NS_EVENT) |
175 | 260 |
176 @defer.inlineCallbacks | 261 @defer.inlineCallbacks |
177 def eventCreate(self, client, timestamp, data, service, node=None, item_id=NS_EVENT): | 262 def eventCreate(self, client, timestamp, data, service, node=None, event_id=NS_EVENT): |
178 """Create or replace an event | 263 """Create or replace an event |
179 | 264 |
180 @param service(jid.JID, None): PubSub service | 265 @param service(jid.JID, None): PubSub service |
181 @param node(unicode, None): PubSub node of the event | 266 @param node(unicode, None): PubSub node of the event |
182 None will create instant node. | 267 None will create instant node. |
183 @param item_id(unicode): ID of the item to create. | 268 @param event_id(unicode): ID of the item to create. |
184 @param timestamp(timestamp, None) | 269 @param timestamp(timestamp, None) |
185 @param data(dict[unicode, unicode]): data to update | 270 @param data(dict[unicode, unicode]): data to update |
186 dict will be cleared, do a copy if data are still needed | 271 dict will be cleared, do a copy if data are still needed |
187 key can be: | 272 key can be: |
188 - name: name of the event | 273 - name: name of the event |
189 - description: details | 274 - description: details |
190 - image: main picture of the event | 275 - image: main picture of the event |
191 - background-image: image to use as background | 276 - background-image: image to use as background |
277 - register: bool, True if we want to register the event in our local list | |
192 @return (unicode): created node | 278 @return (unicode): created node |
193 """ | 279 """ |
194 if not item_id: | 280 if not event_id: |
195 raise ValueError(_(u"item_id must be set")) | 281 raise ValueError(_(u"event_id must be set")) |
196 if not service: | 282 if not service: |
197 service = client.jid.userhostJID() | 283 service = client.jid.userhostJID() |
284 if not node: | |
285 node = NS_EVENT + u'__' + shortuuid.uuid() | |
198 event_elt = domish.Element((NS_EVENT, 'event')) | 286 event_elt = domish.Element((NS_EVENT, 'event')) |
199 if timestamp is not None and timestamp != -1: | 287 if timestamp is not None and timestamp != -1: |
200 formatted_date = utils.xmpp_date(timestamp) | 288 formatted_date = utils.xmpp_date(timestamp) |
201 event_elt.addElement((NS_EVENT, 'date'), content=formatted_date) | 289 event_elt.addElement((NS_EVENT, 'date'), content=formatted_date) |
290 register = data.pop('register', False) | |
202 for key in (u'name',): | 291 for key in (u'name',): |
203 if key in data: | 292 if key in data: |
204 event_elt[key] = data.pop(key) | 293 event_elt[key] = data.pop(key) |
205 for key in (u'description',): | 294 for key in (u'description',): |
206 if key in data: | 295 if key in data: |
222 uri_node = yield self._p.createNode(client, service) | 311 uri_node = yield self._p.createNode(client, service) |
223 yield self._p.setConfiguration(client, service, uri_node, {self._p.OPT_ACCESS_MODEL: self._p.ACCESS_WHITELIST}) | 312 yield self._p.setConfiguration(client, service, uri_node, {self._p.OPT_ACCESS_MODEL: self._p.ACCESS_WHITELIST}) |
224 uri_service = service | 313 uri_service = service |
225 else: | 314 else: |
226 uri = data.pop(key) | 315 uri = data.pop(key) |
227 uri_data = uri_parse.parseXMPPUri(uri) | 316 uri_data = xmpp_uri.parseXMPPUri(uri) |
228 if uri_data[u'type'] != u'pubsub': | 317 if uri_data[u'type'] != u'pubsub': |
229 raise ValueError(_(u'The given URI is not valid: {uri}').format(uri=uri)) | 318 raise ValueError(_(u'The given URI is not valid: {uri}').format(uri=uri)) |
230 uri_service = jid.JID(uri_data[u'path']) | 319 uri_service = jid.JID(uri_data[u'path']) |
231 uri_node = uri_data[u'node'] | 320 uri_node = uri_data[u'node'] |
232 | 321 |
233 elt = event_elt.addElement((NS_EVENT, uri_type)) | 322 elt = event_elt.addElement((NS_EVENT, uri_type)) |
234 elt['uri'] = uri_parse.buildXMPPUri('pubsub', path=uri_service.full(), node=uri_node) | 323 elt['uri'] = xmpp_uri.buildXMPPUri('pubsub', path=uri_service.full(), node=uri_node) |
235 | 324 |
236 # remaining data are put in <meta> elements | 325 # remaining data are put in <meta> elements |
237 for key in data.keys(): | 326 for key in data.keys(): |
238 elt = event_elt.addElement((NS_EVENT, 'meta'), content = data.pop(key)) | 327 elt = event_elt.addElement((NS_EVENT, 'meta'), content = data.pop(key)) |
239 elt['name'] = key | 328 elt['name'] = key |
240 | 329 |
241 item_elt = pubsub.Item(id=item_id, payload=event_elt) | 330 item_elt = pubsub.Item(id=event_id, payload=event_elt) |
242 try: | 331 try: |
243 # TODO: check auto-create, no need to create node first if available | 332 # TODO: check auto-create, no need to create node first if available |
244 node = yield self._p.createNode(client, service, nodeIdentifier=node) | 333 node = yield self._p.createNode(client, service, nodeIdentifier=node) |
245 except error.StanzaError as e: | 334 except error.StanzaError as e: |
246 if e.condition == u'conflict': | 335 if e.condition == u'conflict': |
247 log.debug(_(u"requested node already exists")) | 336 log.debug(_(u"requested node already exists")) |
248 | 337 |
249 yield self._p.publish(client, service, node, items=[item_elt]) | 338 yield self._p.publish(client, service, node, items=[item_elt]) |
250 | 339 |
340 if register: | |
341 yield self.register(client, service, node, event_id, event_elt, creator=True) | |
251 defer.returnValue(node) | 342 defer.returnValue(node) |
252 | 343 |
253 def _eventModify(self, service, node, id_, timestamp_update, data_update, profile_key=C.PROF_KEY_NONE): | 344 def _eventModify(self, service, node, id_, timestamp_update, data_update, profile_key=C.PROF_KEY_NONE): |
254 service = jid.JID(service) if service else None | 345 service = jid.JID(service) if service else None |
255 node = node if node else NS_EVENT | 346 node = node if node else NS_EVENT |
268 new_data = event_metadata | 359 new_data = event_metadata |
269 if data_update: | 360 if data_update: |
270 for k, v in data_update.iteritems(): | 361 for k, v in data_update.iteritems(): |
271 new_data[k] = v | 362 new_data[k] = v |
272 yield self.eventCreate(client, new_timestamp, new_data, service, node, id_) | 363 yield self.eventCreate(client, new_timestamp, new_data, service, node, id_) |
364 | |
365 def _eventsListSerialise(self, events): | |
366 for timestamp, data in events: | |
367 data['date'] = unicode(timestamp) | |
368 data['creator'] = C.boolConst(data.get('creator', False)) | |
369 return [e[1] for e in events] | |
370 | |
371 def _eventsList(self, service, node, profile): | |
372 service = jid.JID(service) if service else None | |
373 node = node or None | |
374 client = self.host.getClient(profile) | |
375 d = self.eventsList(client, service, node) | |
376 d.addCallback(self._eventsListSerialise) | |
377 return d | |
378 | |
379 @defer.inlineCallbacks | |
380 def eventsList(self, client, service, node): | |
381 """Retrieve list of registered events | |
382 | |
383 @return list(tuple(int, dict)): list of events (timestamp + metadata) | |
384 """ | |
385 if not node: | |
386 node = NS_EVENT_LIST | |
387 items = yield self._p.getItems(client, service, node) | |
388 events = [] | |
389 for item in items[0]: | |
390 try: | |
391 event_elt = next(item.elements(NS_EVENT, u'event')) | |
392 except IndexError: | |
393 log.error(_(u"No event found in item {item_id}").format( | |
394 item_id = item['id'])) | |
395 timestamp, data = self._parseEventElt(event_elt) | |
396 events.append((timestamp, data)) | |
397 defer.returnValue(events) | |
273 | 398 |
274 def _eventInviteeGet(self, service, node, profile_key): | 399 def _eventInviteeGet(self, service, node, profile_key): |
275 service = jid.JID(service) if service else None | 400 service = jid.JID(service) if service else None |
276 node = node if node else NS_EVENT | 401 node = node if node else NS_EVENT |
277 client = self.host.getClient(profile_key) | 402 client = self.host.getClient(profile_key) |
343 items, metadata = yield self._p.getItems(client, service, node) | 468 items, metadata = yield self._p.getItems(client, service, node) |
344 invitees = {} | 469 invitees = {} |
345 for item in items: | 470 for item in items: |
346 try: | 471 try: |
347 event_elt = next(item.elements(NS_EVENT, u'invitee')) | 472 event_elt = next(item.elements(NS_EVENT, u'invitee')) |
348 except IndexError: | 473 except StopIteration: |
349 # no item found, event data are not set yet | 474 # no item found, event data are not set yet |
350 log.warning(_(u"no data found for {item_id} (service: {service}, node: {node})".format( | 475 log.warning(_(u"no data found for {item_id} (service: {service}, node: {node})".format( |
351 item_id=item['id'], | 476 item_id=item['id'], |
352 service=service, | 477 service=service, |
353 node=node | 478 node=node |
354 ))) | 479 ))) |
355 data = {} | 480 else: |
356 for key in (u'attend', u'guests'): | 481 data = {} |
357 try: | 482 for key in (u'attend', u'guests'): |
358 data[key] = event_elt[key] | 483 try: |
359 except KeyError: | 484 data[key] = event_elt[key] |
360 continue | 485 except KeyError: |
361 invitees[item['id']] = data | 486 continue |
487 invitees[item['id']] = data | |
362 defer.returnValue(invitees) | 488 defer.returnValue(invitees) |
363 | 489 |
364 def _invite(self, service, node, id_=NS_EVENT, email=u'', emails_extra=None, name=u'', host_name=u'', language=u'', url_template=u'', | 490 def sendMessageInvitation(self, client, invitee_jid, service, node, item_id): |
491 """Send an invitation in a <message> stanza | |
492 | |
493 @param invitee_jid(jid.JID): entitee to send invitation to | |
494 @param service(jid.JID): pubsub service of the event | |
495 @param node(unicode): node of the event | |
496 @param item_id(unicode): id of the event | |
497 """ | |
498 mess_data = { | |
499 'from': client.jid, | |
500 'to': invitee_jid, | |
501 'uid': '', | |
502 'message': {}, | |
503 'type': C.MESS_TYPE_CHAT, | |
504 'subject': {}, | |
505 'extra': {}, | |
506 } | |
507 client.generateMessageXML(mess_data) | |
508 event_elt = mess_data['xml'].addElement('invitation', NS_EVENT_INVIT) | |
509 event_elt['service'] = service.full() | |
510 event_elt['node'] = node | |
511 event_elt['item'] = item_id | |
512 client.send(mess_data['xml']) | |
513 | |
514 def _invite(self, invitee_jid, service, node, item_id, profile): | |
515 client = self.host.getClient(profile) | |
516 service = jid.JID(service) if service else None | |
517 node = node or None | |
518 item_id = item_id or None | |
519 invitee_jid = jid.JID(invitee_jid) | |
520 return self.invite(client, invitee_jid, service, node, item_id) | |
521 | |
522 @defer.inlineCallbacks | |
523 def invite(self, client, invitee_jid, service, node, item_id=NS_EVENT): | |
524 """Invite an entity to the event | |
525 | |
526 This will set permission to let the entity access everything needed | |
527 @pararm invitee_jid(jid.JID): entity to invite | |
528 @param service(jid.JID, None): pubsub service | |
529 None to use client's PEP | |
530 @param node(unicode): event node | |
531 @param item_id(unicode): event id | |
532 """ | |
533 if self._b is None: | |
534 raise exceptions.FeatureNotFound(_(u'"XEP-0277" (blog) plugin is needed for this feature')) | |
535 if item_id is None: | |
536 item_id = NS_EVENT | |
537 | |
538 # first we authorize our invitee to see the nodes of interest | |
539 yield self._p.setNodeAffiliations(client, service, node, {invitee_jid: u'member'}) | |
540 log.debug(_(u'affiliation set on event node')) | |
541 dummy, event_data = yield self.eventGet(client, service, node, item_id) | |
542 log.debug(_(u'got event data')) | |
543 invitees_service = jid.JID(event_data['invitees_service']) | |
544 invitees_node = event_data['invitees_node'] | |
545 blog_service = jid.JID(event_data['blog_service']) | |
546 blog_node = event_data['blog_node'] | |
547 yield self._p.setNodeAffiliations(client, invitees_service, invitees_node, {invitee_jid: u'publisher'}) | |
548 log.debug(_(u'affiliation set on invitee node')) | |
549 yield self._p.setNodeAffiliations(client, blog_service, blog_node, {invitee_jid: u'member'}) | |
550 blog_items, dummy = yield self._b.mbGet(client, blog_service, blog_node, None) | |
551 | |
552 for item in blog_items: | |
553 try: | |
554 comments_service = jid.JID(item['comments_service']) | |
555 comments_node = item['comments_node'] | |
556 except KeyError: | |
557 log.debug(u"no comment service set for item {item_id}".format(item_id=item['id'])) | |
558 else: | |
559 yield self._p.setNodeAffiliations(client, comments_service, comments_node, {invitee_jid: u'publisher'}) | |
560 log.debug(_(u'affiliation set on blog and comments nodes')) | |
561 | |
562 # now we send the invitation | |
563 self.sendMessageInvitation(client, invitee_jid, service, node, item_id) | |
564 | |
565 def _inviteByEmail(self, service, node, id_=NS_EVENT, email=u'', emails_extra=None, name=u'', host_name=u'', language=u'', url_template=u'', | |
365 message_subject=u'', message_body=u'', profile_key=C.PROF_KEY_NONE): | 566 message_subject=u'', message_body=u'', profile_key=C.PROF_KEY_NONE): |
366 client = self.host.getClient(profile_key) | 567 client = self.host.getClient(profile_key) |
367 kwargs = {u'profile': client.profile, | 568 kwargs = {u'profile': client.profile, |
368 u'emails_extra': [unicode(e) for e in emails_extra] | 569 u'emails_extra': [unicode(e) for e in emails_extra] |
369 } | 570 } |
370 for key in ("email", "name", "host_name", "language", "url_template", "message_subject", "message_body"): | 571 for key in ("email", "name", "host_name", "language", "url_template", "message_subject", "message_body"): |
371 value = locals()[key] | 572 value = locals()[key] |
372 kwargs[key] = unicode(value) | 573 kwargs[key] = unicode(value) |
373 return self.invite(client, | 574 return self.inviteByEmail(client, |
374 jid.JID(service) if service else None, | 575 jid.JID(service) if service else None, |
375 node, | 576 node, |
376 id_ or NS_EVENT, | 577 id_ or NS_EVENT, |
377 **kwargs) | 578 **kwargs) |
378 | 579 |
379 @defer.inlineCallbacks | 580 @defer.inlineCallbacks |
380 def invite(self, client, service, node, id_=NS_EVENT, **kwargs): | 581 def inviteByEmail(self, client, service, node, id_=NS_EVENT, **kwargs): |
381 """High level method to create an email invitation to an event | 582 """High level method to create an email invitation to an event |
382 | 583 |
383 @param service(unicode, None): PubSub service | 584 @param service(unicode, None): PubSub service |
384 @param node(unicode): PubSub node of the event | 585 @param node(unicode): PubSub node of the event |
385 @param id_(unicode): id_ with even data | 586 @param id_(unicode): id_ with even data |
386 """ | 587 """ |
387 if self._i is None: | 588 if self._i is None: |
388 raise exceptions.FeatureNotFound(_(u'"Invitations" plugin is needed for this feature')) | 589 raise exceptions.FeatureNotFound(_(u'"Invitations" plugin is needed for this feature')) |
389 if self._b is None: | 590 if self._b is None: |
390 raise exceptions.FeatureNotFound(_(u'"XEP-0277" (blog) plugin is needed for this feature')) | 591 raise exceptions.FeatureNotFound(_(u'"XEP-0277" (blog) plugin is needed for this feature')) |
391 event_service = (service or client.jid.userhostJID()) | 592 service = service or client.jid.userhostJID() |
392 event_uri = uri_parse.buildXMPPUri('pubsub', | 593 event_uri = xmpp_uri.buildXMPPUri('pubsub', |
393 path=event_service.full(), | 594 path=service.full(), |
394 node=node, | 595 node=node, |
395 item=id_) | 596 item=id_) |
396 kwargs['extra'] = {u'event_uri': event_uri} | 597 kwargs['extra'] = {u'event_uri': event_uri} |
397 invitation_data = yield self._i.create(**kwargs) | 598 invitation_data = yield self._i.create(**kwargs) |
398 invitee_jid = invitation_data[u'jid'] | 599 invitee_jid = invitation_data[u'jid'] |
399 log.debug(_(u'invitation created')) | 600 log.debug(_(u'invitation created')) |
400 yield self._p.setNodeAffiliations(client, event_service, node, {invitee_jid: u'member'}) | 601 # now that we have a jid, we can send normal invitation |
401 log.debug(_(u'affiliation set on event node')) | 602 yield self.invite(client, invitee_jid, service, node, id_) |
402 dummy, event_data = yield self.eventGet(client, service, node, id_) | 603 |
403 log.debug(_(u'got event data')) | 604 @defer.inlineCallbacks |
404 invitees_service = jid.JID(event_data['invitees_service']) | 605 def onInvitation(self, message_elt, client): |
405 invitees_node = event_data['invitees_node'] | 606 invitation_elt = message_elt.invitation |
406 blog_service = jid.JID(event_data['blog_service']) | 607 try: |
407 blog_node = event_data['blog_node'] | 608 service = jid.JID(invitation_elt['service']) |
408 yield self._p.setNodeAffiliations(client, invitees_service, invitees_node, {invitee_jid: u'publisher'}) | 609 node = invitation_elt['node'] |
409 log.debug(_(u'affiliation set on invitee node')) | 610 event_id = invitation_elt['item'] |
410 yield self._p.setNodeAffiliations(client, blog_service, blog_node, {invitee_jid: u'member'}) | 611 except (RuntimeError, KeyError): |
411 # FIXME: what follow is crazy, we have no good way to handle comments affiliations for blog | 612 log.warning(_(u"Bad invitation: {xml}").format(xml=message_elt.toXml())) |
412 blog_items, dummy = yield self._b.mbGet(client, blog_service, blog_node, None) | 613 |
413 | 614 event_elt = yield self.getEventElement(client, service, node, event_id) |
414 for item in blog_items: | 615 yield self.register(client, service, node, event_id, event_elt, creator=False) |
415 comments_service = jid.JID(item['comments_service']) | 616 |
416 comments_node = item['comments_node'] | 617 |
417 yield self._p.setNodeAffiliations(client, comments_service, comments_node, {invitee_jid: u'publisher'}) | 618 class EventsHandler(XMPPHandler): |
418 log.debug(_(u'affiliation set on blog and comments nodes')) | 619 implements(iwokkel.IDisco) |
419 | 620 |
420 | 621 def __init__(self, plugin_parent): |
622 self.plugin_parent = plugin_parent | |
623 | |
624 @property | |
625 def host(self): | |
626 return self.plugin_parent.host | |
627 | |
628 def connectionInitialized(self): | |
629 self.xmlstream.addObserver(INVITATION, | |
630 self.plugin_parent.onInvitation, | |
631 client=self.parent) | |
632 | |
633 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): | |
634 return [disco.DiscoFeature(NS_EVENT), | |
635 disco.DiscoFeature(NS_EVENT_LIST), | |
636 disco.DiscoFeature(NS_EVENT_INVIT)] | |
637 | |
638 def getDiscoItems(self, requestor, target, nodeIdentifier=''): | |
639 return [] |