comparison libervia/pages/_browser/invitation.py @ 1386:83be300d17e3

browser (invitations): handle Pubsub invitation
author Goffi <goffi@goffi.org>
date Sat, 20 Feb 2021 13:58:42 +0100
parents e3e303a30a74
children 5a132b85e1ac
comparison
equal deleted inserted replaced
1385:4b6f711b09cb 1386:83be300d17e3
1 from browser import document, window, timer 1 from browser import document, window, timer
2 from bridge import Bridge 2 from bridge import Bridge
3 from template import Template 3 from template import Template
4 from dialog import notification 4 import dialog
5 from cache import cache 5 from cache import cache
6 import javascript
7
8 bridge = Bridge()
6 # we use JS RegExp because Python's re is really long to import in Brython 9 # we use JS RegExp because Python's re is really long to import in Brython
7 from javascript import RegExp
8
9 bridge = Bridge()
10 # FIXME: this is a naive JID regex, a more accurate should be used instead 10 # FIXME: this is a naive JID regex, a more accurate should be used instead
11 jid_re = RegExp.new(r"^\w+@\w+\.\w+") 11 jid_re = javascript.RegExp.new(r"^\w+@\w+\.\w+")
12 12
13 13
14 class InvitationManager: 14 class InvitationManager:
15 15
16 def __init__(self, invitation_type, invitation_data): 16 def __init__(self, invitation_type, invitation_data):
68 """Hide the panel""" 68 """Hide the panel"""
69 document.body.style.height, document.body.style.overflow = self._body_ori_style 69 document.body.style.height, document.body.style.overflow = self._body_ori_style
70 self.side_panel.classList.remove('open') 70 self.side_panel.classList.remove('open')
71 self.side_panel.bind("transitionend", self._on_close_transition_end) 71 self.side_panel.bind("transitionend", self._on_close_transition_end)
72 72
73 def invite_by_jid(self, entity_jid): 73 def _invite_jid(self, entity_jid, callback, errback=None):
74 if errback is None:
75 errback = lambda e: dialog.notification.show(f"invitation failed: {e}", "error")
74 if self.invitation_type == 'photos': 76 if self.invitation_type == 'photos':
77 service = self.invitation_data["service"]
75 path = self.invitation_data["path"] 78 path = self.invitation_data["path"]
76 service = self.invitation_data["service"]
77 album_name = path.rsplit('/')[-1] 79 album_name = path.rsplit('/')[-1]
78 print(f"inviting {entity_jid}") 80 print(f"inviting {entity_jid}")
79 bridge.FISInvite( 81 bridge.FISInvite(
80 entity_jid, 82 entity_jid,
81 service, 83 service,
82 "photos", 84 "photos",
83 "", 85 "",
84 path, 86 path,
85 album_name, 87 album_name,
86 '', 88 '',
87 callback=lambda entity_jid=entity_jid: 89 callback=callback,
88 self._on_jid_invitation_success(entity_jid), 90 errback=errback
89 errback=lambda e: notification.show(f"invitation failed: {e}", "error") 91 )
90 ) 92 elif self.invitation_type == 'pubsub':
93 service = self.invitation_data["service"]
94 node = self.invitation_data["node"]
95 name = self.invitation_data.get("name")
96 namespace = self.invitation_data.get("namespace")
97 extra = {}
98 if namespace:
99 extra["namespace"] = namespace
100 print(f"inviting {entity_jid}")
101 bridge.psInvite(
102 entity_jid,
103 service,
104 node,
105 '',
106 name,
107 javascript.JSON.stringify(extra),
108 callback=callback,
109 errback=errback
110 )
111 else:
112 print(f"error: unknown invitation type: {self.invitation_type}")
113
114 def invite_by_jid(self, entity_jid):
115 self._invite_jid(
116 entity_jid,
117 callback=lambda entity_jid=entity_jid: self._on_jid_invitation_success(entity_jid),
118 )
91 119
92 def on_manager_close(self, evt): 120 def on_manager_close(self, evt):
93 self.close() 121 self.close()
94 122
95 def _on_jid_invitation_success(self, entity_jid): 123 def _on_jid_invitation_success(self, entity_jid):
96 form_elt = document['invitation_form'] 124 form_elt = document['invitation_form']
97 contact_elt = form_elt.select_one('input[name="contact"]') 125 contact_elt = form_elt.select_one('input[name="contact"]')
98 contact_elt.value = "" 126 contact_elt.value = ""
99 contact_elt.dispatchEvent(window.Event.new('input')) 127 contact_elt.dispatchEvent(window.Event.new('input'))
100 notification.show( 128 dialog.notification.show(
101 f"{entity_jid} has been invited", 129 f"{entity_jid} has been invited",
102 level="success", 130 level="success",
103 ) 131 )
104 if entity_jid not in self.affiliations: 132 if entity_jid not in self.affiliations:
105 self.set_affiliation(entity_jid, "member") 133 self.set_affiliation(entity_jid, "member")
206 # active item has been filtered out 234 # active item has been filtered out
207 self._active_new_item = None 235 self._active_new_item = None
208 236
209 def _on_email_invitation_success(self, invitee_jid, email, name): 237 def _on_email_invitation_success(self, invitee_jid, email, name):
210 self.set_affiliation(invitee_jid, "member") 238 self.set_affiliation(invitee_jid, "member")
211 notification.show( 239 dialog.notification.show(
212 f"{name} has been invited, he/she has received an email with a link", 240 f"{name} has been invited, he/she has received an email with a link",
213 level="success", 241 level="success",
214 ) 242 )
215 243
216 def invitationSimpleCreateCb(self, invitation_data, email, name): 244 def invitationSimpleCreateCb(self, invitation_data, email, name):
217 if self.invitation_type == 'photos': 245 invitee_jid = invitation_data['jid']
218 path = self.invitation_data["path"] 246 self._invite_jid(
219 service = self.invitation_data["service"] 247 invitee_jid,
220 invitee_jid = invitation_data['jid'] 248 callback=lambda: self._on_email_invitation_success(invitee_jid, email, name),
221 album_name = path.rsplit('/')[-1] 249 errback=lambda e: dialog.notification.show(
222 bridge.FISInvite( 250 f"invitation failed for {email}: {e}",
223 invitee_jid, 251 "error"
224 service, 252 )
225 "photos", 253 )
226 "", 254
227 path, 255 # we update identities to have the name instead of the invitation jid in
228 album_name, 256 # affiliations
229 '', 257 cache.identities[invitee_jid] = {'nicknames': [name]}
230 callback=lambda: self._on_email_invitation_success(invitee_jid, email, name), 258 cache.update()
231 errback=lambda e: window.alert(f"invitation failed for {email}: {e}")
232 )
233
234 # we update identities to have the name instead of the invitation jid in
235 # affiliations
236 cache.identities[invitee_jid] = {'nicknames': [name]}
237 cache.update()
238 259
239 def invite_by_email(self, email, name): 260 def invite_by_email(self, email, name):
240 guest_url_tpl = f'{window.URL.new("/g", document.baseURI).href}/{{uuid}}' 261 guest_url_tpl = f'{window.URL.new("/g", document.baseURI).href}/{{uuid}}'
241 bridge.invitationSimpleCreate( 262 bridge.invitationSimpleCreate(
242 email, 263 email,
283 "click", lambda evt: self.on_invite_email_close(evt, invite_email_elt)) 304 "click", lambda evt: self.on_invite_email_close(evt, invite_email_elt))
284 self.close() 305 self.close()
285 306
286 ## affiliations 307 ## affiliations
287 308
309 def _addAffiliationBindings(self, entity_jid, affiliation_elt):
310 for elt in affiliation_elt.select(".click_to_delete"):
311 elt.bind(
312 "click",
313 lambda evt, entity_jid=entity_jid, affiliation_elt=affiliation_elt:
314 self.on_affiliation_remove(entity_jid, affiliation_elt)
315 )
316 for elt in affiliation_elt.select(".click_to_set_publisher"):
317 try:
318 name = cache.identities[entity_jid]["nicknames"][0]
319 except (KeyError, IndexError):
320 name = entity_jid
321 elt.bind(
322 "click",
323 lambda evt, entity_jid=entity_jid, name=name, affiliation_elt=affiliation_elt:
324 self.on_affiliation_set(
325 entity_jid, name, affiliation_elt, "publisher"
326 ),
327 )
328 for elt in affiliation_elt.select(".click_to_set_member"):
329 try:
330 name = cache.identities[entity_jid]["nicknames"][0]
331 except (KeyError, IndexError):
332 name = entity_jid
333 elt.bind(
334 "click",
335 lambda evt, entity_jid=entity_jid, name=name,
336 affiliation_elt=affiliation_elt:
337 self.on_affiliation_set(
338 entity_jid, name, affiliation_elt, "member"
339 ),
340 )
341
288 def set_affiliation(self, entity_jid, affiliation): 342 def set_affiliation(self, entity_jid, affiliation):
289 if affiliation not in ('owner', 'member'): 343 if affiliation not in ('owner', 'member', 'publisher'):
290 raise NotImplementedError( 344 raise NotImplementedError(
291 f'{affiliation} affiliation can not be set with this method for the ' 345 f'{affiliation} affiliation can not be set with this method for the '
292 'moment') 346 'moment')
293 if entity_jid not in self.affiliations: 347 if entity_jid not in self.affiliations:
294 self.affiliations[entity_jid] = affiliation 348 self.affiliations[entity_jid] = affiliation
296 "entity_jid": entity_jid, 350 "entity_jid": entity_jid,
297 "affiliation": affiliation, 351 "affiliation": affiliation,
298 "identities": cache.identities, 352 "identities": cache.identities,
299 }) 353 })
300 document['affiliations'] <= affiliation_elt 354 document['affiliations'] <= affiliation_elt
301 for elt in affiliation_elt.select(".click_to_delete"): 355 self._addAffiliationBindings(entity_jid, affiliation_elt)
302 elt.bind(
303 "click",
304 lambda evt, entity_jid=entity_jid, affiliation_elt=affiliation_elt:
305 self.on_affiliation_remove(entity_jid, affiliation_elt)
306 )
307 356
308 def _on_affiliation_remove_success(self, affiliation_elt, entity_jid): 357 def _on_affiliation_remove_success(self, affiliation_elt, entity_jid):
309 affiliation_elt.remove() 358 affiliation_elt.remove()
310 del self.affiliations[entity_jid] 359 del self.affiliations[entity_jid]
311 360
318 "", 367 "",
319 path, 368 path,
320 {entity_jid: "none"}, 369 {entity_jid: "none"},
321 callback=lambda: self._on_affiliation_remove_success( 370 callback=lambda: self._on_affiliation_remove_success(
322 affiliation_elt, entity_jid), 371 affiliation_elt, entity_jid),
323 errback=lambda e: notification.show( 372 errback=lambda e: dialog.notification.show(
324 f"can't remove affiliation: {e}", "error") 373 f"can't remove affiliation: {e}", "error")
325 ) 374 )
375 elif self.invitation_type == 'pubsub':
376 service = self.invitation_data["service"]
377 node = self.invitation_data["node"]
378 bridge.psNodeAffiliationsSet(
379 service,
380 node,
381 {entity_jid: "none"},
382 callback=lambda: self._on_affiliation_remove_success(
383 affiliation_elt, entity_jid),
384 errback=lambda e: dialog.notification.show(
385 f"can't remove affiliation: {e}", "error")
386 )
387 else:
388 dialog.notification.show(
389 f"error: unknown invitation type: {self.invitation_type}",
390 "error"
391 )
392
393 def _on_affiliation_set_success(self, entity_jid, name, affiliation_elt, affiliation):
394 dialog.notification.show(f"permission updated for {name}")
395 self.affiliations[entity_jid] = affiliation
396 new_affiliation_elt = self.affiliation_tpl.get_elt({
397 "entity_jid": entity_jid,
398 "affiliation": affiliation,
399 "identities": cache.identities,
400 })
401 affiliation_elt.replaceWith(new_affiliation_elt)
402 self._addAffiliationBindings(entity_jid, new_affiliation_elt)
403
404 def _on_affiliation_set_ok(self, entity_jid, name, affiliation_elt, affiliation):
405 if self.invitation_type == 'pubsub':
406 service = self.invitation_data["service"]
407 node = self.invitation_data["node"]
408 bridge.psNodeAffiliationsSet(
409 service,
410 node,
411 {entity_jid: affiliation},
412 callback=lambda: self._on_affiliation_set_success(
413 entity_jid, name, affiliation_elt, affiliation
414 ),
415 errback=lambda e: dialog.notification.show(
416 f"can't set affiliation: {e}", "error")
417 )
418 else:
419 dialog.notification.show(
420 f"error: unknown invitation type: {self.invitation_type}",
421 "error"
422 )
423
424 def _on_affiliation_set_cancel(self, evt, notif_elt):
425 notif_elt.remove()
426 self.open()
427
428 def on_affiliation_set(self, entity_jid, name, affiliation_elt, affiliation):
429 if affiliation == "publisher":
430 message = f"Give autorisation to publish to {name}?"
431 elif affiliation == "member":
432 message = f"Remove autorisation to publish from {name}?"
433 else:
434 dialog.notification.show(f"unmanaged affiliation: {affiliation}", "error")
435 return
436 dialog.Confirm(message).show(
437 ok_cb=lambda evt, notif_elt:
438 self._on_affiliation_set_ok(
439 entity_jid, name, affiliation_elt, affiliation
440 ),
441 cancel_cb=self._on_affiliation_set_cancel
442 )
443 self.close()