comparison src/plugins/plugin_misc_invitations.py @ 2219:77a3d0a28642

plugin invitations: added modify method (+ bridge) and fixed email setting in extra
author Goffi <goffi@goffi.org>
date Mon, 03 Apr 2017 00:23:01 +0200
parents eaf2467d19ce
children c6c9a97ffebf
comparison
equal deleted inserted replaced
2218:6a2fa651d7fa 2219:77a3d0a28642
46 INVITEE_PROFILE_TPL = u"guest@@{uuid}" 46 INVITEE_PROFILE_TPL = u"guest@@{uuid}"
47 KEY_ID = u'id' 47 KEY_ID = u'id'
48 KEY_JID = u'jid' 48 KEY_JID = u'jid'
49 KEY_CREATED = u'created' 49 KEY_CREATED = u'created'
50 KEY_LAST_CONNECTION = u'last_connection' 50 KEY_LAST_CONNECTION = u'last_connection'
51 EXTRA_RESERVED = {KEY_ID, KEY_JID, KEY_CREATED, u'jid_', u'jid', KEY_LAST_CONNECTION} 51 KEY_GUEST_PROFILE = u'guest_profile'
52 KEY_PASSWORD = u'password'
53 EXTRA_RESERVED = {KEY_ID, KEY_JID, KEY_CREATED, u'jid_', u'jid', KEY_LAST_CONNECTION, KEY_GUEST_PROFILE, KEY_PASSWORD}
52 DEFAULT_SUBJECT = D_(u"You have been invited by {host_name} to {app_name}") 54 DEFAULT_SUBJECT = D_(u"You have been invited by {host_name} to {app_name}")
53 DEFAULT_BODY = D_(u"""Hello {name}! 55 DEFAULT_BODY = D_(u"""Hello {name}!
54 56
55 You have received an invitation from {host_name} to participate to "{app_name}". 57 You have received an invitation from {host_name} to participate to "{app_name}".
56 To join, you just have to click on the following URL: 58 To join, you just have to click on the following URL:
68 def __init__(self, host): 70 def __init__(self, host):
69 log.info(_(u"plugin Invitations initialization")) 71 log.info(_(u"plugin Invitations initialization"))
70 self.host = host 72 self.host = host
71 self.invitations = persistent.LazyPersistentBinaryDict(u'invitations') 73 self.invitations = persistent.LazyPersistentBinaryDict(u'invitations')
72 host.bridge.addMethod("invitationCreate", ".plugin", in_sign='sssssssssa{ss}s', out_sign='a{ss}', 74 host.bridge.addMethod("invitationCreate", ".plugin", in_sign='sssssssssa{ss}s', out_sign='a{ss}',
73 method=self._createInvitation, 75 method=self._create,
74 async=True) 76 async=True)
75 host.bridge.addMethod("invitationGet", ".plugin", in_sign='s', out_sign='a{ss}', 77 host.bridge.addMethod("invitationGet", ".plugin", in_sign='s', out_sign='a{ss}',
76 method=self.getInvitation, 78 method=self.get,
77 async=True) 79 async=True)
78 80 host.bridge.addMethod("invitationModify", ".plugin", in_sign='sa{ss}b', out_sign='',
79 def _createInvitation(self, jid_=u'', password=u'', name=u'', host_name=u'', email=u'', language=u'', url_template=u'', message_subject=u'', message_body=u'', extra=None, profile=u''): 81 method=self._modify,
82 async=True)
83
84 def checkExtra(self, extra):
85 if EXTRA_RESERVED.intersection(extra):
86 raise ValueError(_(u"You can't use following key(s) in extra, they are reserved: {}").format(
87 u', '.join(EXTRA_RESERVED.intersection(extra))))
88
89 def _create(self, jid_=u'', password=u'', name=u'', host_name=u'', email=u'', language=u'', url_template=u'', message_subject=u'', message_body=u'', extra=None, profile=u''):
80 # XXX: we don't use **kwargs here to keep arguments name for introspection with D-Bus bridge 90 # XXX: we don't use **kwargs here to keep arguments name for introspection with D-Bus bridge
81 91
82 if extra is None: 92 if extra is None:
83 extra = {} 93 extra = {}
84 else: 94 else:
88 kwargs = {"extra": extra} 98 kwargs = {"extra": extra}
89 for key in ("jid_", "password", "name", "host_name", "email", "language", "url_template", "message_subject", "message_body", "profile"): 99 for key in ("jid_", "password", "name", "host_name", "email", "language", "url_template", "message_subject", "message_body", "profile"):
90 value = locals()[key] 100 value = locals()[key]
91 if value: 101 if value:
92 kwargs[key] = unicode(value) 102 kwargs[key] = unicode(value)
93 d = self.createInvitation(**kwargs) 103 d = self.create(**kwargs)
94 def serialize(data): 104 def serialize(data):
95 data[KEY_JID] = data[KEY_JID].full() 105 data[KEY_JID] = data[KEY_JID].full()
96 d.addCallback(serialize) 106 d.addCallback(serialize)
97 return d 107 return d
98 108
99 @defer.inlineCallbacks 109 @defer.inlineCallbacks
100 def createInvitation(self, **kwargs): 110 def create(self, **kwargs):
101 ur"""create an invitation 111 ur"""create an invitation
102 112
103 this will create an XMPP account and a profile, and use a UUID to retrieve them. 113 this will create an XMPP account and a profile, and use a UUID to retrieve them.
104 the profile is automatically generated in the form guest@@[UUID], this way they can be retrieved easily 114 the profile is automatically generated in the form guest@@[UUID], this way they can be retrieved easily
105 **kwargs: keywords arguments which can have the following keys, unset values are equivalent to None: 115 **kwargs: keywords arguments which can have the following keys, unset values are equivalent to None:
144 - filled extra dictionary, as saved in the databae 154 - filled extra dictionary, as saved in the databae
145 """ 155 """
146 ## initial checks 156 ## initial checks
147 extra = kwargs.pop('extra', {}) 157 extra = kwargs.pop('extra', {})
148 if set(kwargs).intersection(extra): 158 if set(kwargs).intersection(extra):
149 raise exceptions.ValueError(_(u"You can't use following key(s) in both args and extra: {}").format( 159 raise ValueError(_(u"You can't use following key(s) in both args and extra: {}").format(
150 u', '.join(set(kwargs).intersection(extra)))) 160 u', '.join(set(kwargs).intersection(extra))))
151 161
152 if EXTRA_RESERVED.intersection(extra): 162 self.checkExtra(extra)
153 raise exceptions.ValueError(_(u"You can't use following key(s) in extra, they are reserved: {}").format(
154 u', '.join(EXTRA_RESERVED.intersection(extra))))
155 163
156 if not 'url_template' in extra and not 'message_body' in extra: 164 if not 'url_template' in extra and not 'message_body' in extra:
157 raise ValueError(_(u"You need to provide url_template if you use default message body")) 165 raise ValueError(_(u"You need to provide url_template if you use default message body"))
158 166
159 167
170 # it is needed for invitation as the same password is used for profile 178 # it is needed for invitation as the same password is used for profile
171 # and SàT need to be able to automatically open the profile with the uuid 179 # and SàT need to be able to automatically open the profile with the uuid
172 # FIXME: we could add an extra encryption key which would be used with the uuid 180 # FIXME: we could add an extra encryption key which would be used with the uuid
173 # when the invitee is connecting (e.g. with URL). This key would not be saved 181 # when the invitee is connecting (e.g. with URL). This key would not be saved
174 # and could be used to encrypt profile password. 182 # and could be used to encrypt profile password.
175 extra[u'password'] = password 183 extra[KEY_PASSWORD] = password
176 184
177 jid_ = kwargs.pop(u'jid_', None) 185 jid_ = kwargs.pop(u'jid_', None)
178 if not jid_: 186 if not jid_:
179 domain = self.host.memory.getConfig(None, 'xmpp_domain') 187 domain = self.host.memory.getConfig(None, 'xmpp_domain')
180 if not domain: 188 if not domain:
204 raise e 212 raise e
205 213
206 log.info(_(u"account {jid_} created").format(jid_=jid_.full())) 214 log.info(_(u"account {jid_} created").format(jid_=jid_.full()))
207 215
208 ## profile creation 216 ## profile creation
209 extra['guest_profile'] = guest_profile = INVITEE_PROFILE_TPL.format(uuid=id_) 217 extra[KEY_GUEST_PROFILE] = guest_profile = INVITEE_PROFILE_TPL.format(uuid=id_)
210 # profile creation should not fail as we generate unique name ourselves 218 # profile creation should not fail as we generate unique name ourselves
211 yield self.host.memory.createProfile(guest_profile, password) 219 yield self.host.memory.createProfile(guest_profile, password)
212 yield self.host.memory.startSession(password, guest_profile) 220 yield self.host.memory.startSession(password, guest_profile)
213 yield self.host.memory.setParam("JabberID", jid_.full(), "Connection", profile_key=guest_profile) 221 yield self.host.memory.setParam("JabberID", jid_.full(), "Connection", profile_key=guest_profile)
214 yield self.host.memory.setParam("Password", password, "Connection", profile_key=guest_profile) 222 yield self.host.memory.setParam("Password", password, "Connection", profile_key=guest_profile)
218 if language is not None: 226 if language is not None:
219 extra[u'language'] = language 227 extra[u'language'] = language
220 email = kwargs.pop(u'email', None) 228 email = kwargs.pop(u'email', None)
221 229
222 if email is not None: 230 if email is not None:
231 extra[u'email'] = email
223 url_template = kwargs.pop(u'url_template', '') 232 url_template = kwargs.pop(u'url_template', '')
224 format_args = { 233 format_args = {
225 u'uuid': id_, 234 u'uuid': id_,
226 u'app_name': C.APP_NAME, 235 u'app_name': C.APP_NAME,
227 u'app_url': C.APP_URL} 236 u'app_url': C.APP_URL}
262 271
263 extra[KEY_ID] = id_ 272 extra[KEY_ID] = id_
264 extra[KEY_JID] = jid 273 extra[KEY_JID] = jid
265 defer.returnValue(extra) 274 defer.returnValue(extra)
266 275
267 def getInvitation(self, id_): 276 def get(self, id_):
268 """Retrieve invitation linked to uuid if it exists 277 """Retrieve invitation linked to uuid if it exists
269 278
270 @param id_(unicode): UUID linked to an invitation 279 @param id_(unicode): UUID linked to an invitation
271 @return dict(unicode, unicode): data associated to the invitation 280 @return (dict[unicode, unicode]): data associated to the invitation
272 @raise KeyError: there is not invitation with this id_ 281 @raise KeyError: there is not invitation with this id_
273 """ 282 """
274 return self.invitations[id_] 283 return self.invitations[id_]
284
285 def _modify(self, id_, new_extra, replace):
286 return self.modify(id_, {unicode(k): unicode(v) for k,v in new_extra.iteritems()}, replace)
287
288 def modify(self, id_, new_extra, replace=False):
289 """Modify invitation data
290
291 @param id_(unicode): UUID linked to an invitation
292 @param new_extra(dict[unicode, unicode]): data to update
293 @param replace(bool): if True replace the data
294 else update them
295 @raise KeyError: there is not invitation with this id_
296 """
297 self.checkExtra(new_extra)
298 def gotCurrentData(current_data):
299 if replace:
300 new_data = new_extra
301 for k in EXTRA_RESERVED:
302 try:
303 new_data[k] = current_data[k]
304 except KeyError:
305 continue
306 else:
307 new_data = current_data
308 for k,v in new_extra.iteritems():
309 new_data[k] = v
310
311 self.invitations[id_] = new_data
312
313 d = self.invitations[id_]
314 d.addCallback(gotCurrentData)
315 return d