Mercurial > libervia-backend
comparison frontends/src/quick_frontend/quick_app.py @ 1337:f29beedb33b0 frontends_multi_profiles
merged souliane changes
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 23 Feb 2015 18:08:22 +0100 |
parents | 2ecc07a8f91b 0f92b6a150ff |
children | 139263ee85c5 |
comparison
equal
deleted
inserted
replaced
1336:2ecc07a8f91b | 1337:f29beedb33b0 |
---|---|
15 # GNU Affero General Public License for more details. | 15 # GNU Affero General Public License for more details. |
16 | 16 |
17 # You should have received a copy of the GNU Affero General Public License | 17 # You should have received a copy of the GNU Affero General Public License |
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | 19 |
20 from sat.core.i18n import _ | |
21 import sys | |
22 from sat.core.log import getLogger | 20 from sat.core.log import getLogger |
23 log = getLogger(__name__) | 21 log = getLogger(__name__) |
22 | |
23 from sat.core.i18n import _ | |
24 from sat.core import exceptions | 24 from sat.core import exceptions |
25 | |
25 from sat_frontends.tools import jid | 26 from sat_frontends.tools import jid |
26 from sat_frontends.quick_frontend.quick_widgets import QuickWidgetsManager | 27 from sat_frontends.quick_frontend.quick_widgets import QuickWidgetsManager |
27 from sat_frontends.quick_frontend import quick_chat | 28 from sat_frontends.quick_frontend import quick_chat |
28 | |
29 from sat_frontends.quick_frontend.constants import Const as C | 29 from sat_frontends.quick_frontend.constants import Const as C |
30 | |
31 import sys | |
32 from collections import OrderedDict | |
33 | |
34 try: | |
35 # FIXME: to be removed when an acceptable solution is here | |
36 unicode('') # XXX: unicode doesn't exist in pyjamas | |
37 except (TypeError, AttributeError): # Error raised is not the same depending on pyjsbuild options | |
38 unicode = str | |
30 | 39 |
31 | 40 |
32 class ProfileManager(object): | 41 class ProfileManager(object): |
33 """Class managing all data relative to one profile, and plugging in mechanism""" | 42 """Class managing all data relative to one profile, and plugging in mechanism""" |
34 host = None | 43 host = None |
73 log.error("Couldn't get cached values: {}".format(failure)) | 82 log.error("Couldn't get cached values: {}".format(failure)) |
74 self._plug_profile_gotCachedValues({}) | 83 self._plug_profile_gotCachedValues({}) |
75 | 84 |
76 def _plug_profile_gotCachedValues(self, cached_values): | 85 def _plug_profile_gotCachedValues(self, cached_values): |
77 # TODO: watched plugin | 86 # TODO: watched plugin |
87 | |
88 # add the contact list and its listener | |
78 contact_list = self.host.addContactList(self.profile) | 89 contact_list = self.host.addContactList(self.profile) |
90 self.host.contact_lists[self.profile] = contact_list | |
79 | 91 |
80 for entity, data in cached_values.iteritems(): | 92 for entity, data in cached_values.iteritems(): |
81 for key, value in data.iteritems(): | 93 for key, value in data.iteritems(): |
82 self.host.contact_lists[self.profile].setCache(jid.JID(entity), key, value) | 94 contact_list.setCache(jid.JID(entity), key, value) |
83 | 95 |
84 if not self.bridge.isConnected(self.profile): | 96 if not self.bridge.isConnected(self.profile): |
85 self.host.setStatusOnline(False, profile=self.profile) | 97 self.host.setStatusOnline(False, profile=self.profile) |
86 else: | 98 else: |
87 self.host.setStatusOnline(True, profile=self.profile) | 99 self.host.setStatusOnline(True, profile=self.profile) |
167 self._profiles[profile].plug() | 179 self._profiles[profile].plug() |
168 | 180 |
169 def unplug(self, profile): | 181 def unplug(self, profile): |
170 if profile not in self._profiles: | 182 if profile not in self._profiles: |
171 raise ValueError('The profile [{}] is not plugged'.format(profile)) | 183 raise ValueError('The profile [{}] is not plugged'.format(profile)) |
184 | |
185 # remove the contact list and its listener | |
186 host = self._profiles[profile].host | |
187 host.contact_lists[profile].onDelete() | |
188 del host.contact_lists[profile] | |
189 | |
172 del self._profiles[profile] | 190 del self._profiles[profile] |
173 | 191 |
174 def chooseOneProfile(self): | 192 def chooseOneProfile(self): |
175 return self._profiles.keys()[0] | 193 return self._profiles.keys()[0] |
194 | |
176 | 195 |
177 class QuickApp(object): | 196 class QuickApp(object): |
178 """This class contain the main methods needed for the frontend""" | 197 """This class contain the main methods needed for the frontend""" |
179 | 198 |
180 def __init__(self, create_bridge, check_options=None): | 199 def __init__(self, create_bridge, check_options=None): |
194 | 213 |
195 # widgets | 214 # widgets |
196 self.selected_widget = None # widget currently selected (must be filled by frontend) | 215 self.selected_widget = None # widget currently selected (must be filled by frontend) |
197 | 216 |
198 # listeners | 217 # listeners |
199 self._listeners = {} # key: listerner type ("avatar", "selected", etc), value: list of callbacks | 218 self._listeners = {} # key: listener type ("avatar", "selected", etc), value: list of callbacks |
200 | 219 |
201 ## bridge ## | 220 ## bridge ## |
202 try: | 221 try: |
203 self.bridge = create_bridge() | 222 self.bridge = create_bridge() |
204 except exceptions.BridgeExceptionNoService: | 223 except exceptions.BridgeExceptionNoService: |
285 if profile is not None and not self.check_profile(profile): | 304 if profile is not None and not self.check_profile(profile): |
286 return # we ignore signal for profiles we don't manage | 305 return # we ignore signal for profiles we don't manage |
287 handler(*args, **kwargs) | 306 handler(*args, **kwargs) |
288 self.bridge.register(functionName, signalReceived, iface) | 307 self.bridge.register(functionName, signalReceived, iface) |
289 | 308 |
290 def addListerner(self, type_, callback): | 309 def addListener(self, type_, callback, profiles_filter=None): |
291 """Add a listerner for an event | 310 """Add a listener for an event |
292 | 311 |
293 /!\ don't forget to remove listener when not used anymore (e.g. if you delete a widget) | 312 /!\ don't forget to remove listener when not used anymore (e.g. if you delete a widget) |
294 @param type_: type of event, can be: | 313 @param type_: type of event, can be: |
295 - avatar: called when avatar data is updated | 314 - avatar: called when avatar data is updated |
296 args: (entity, avatar file, profile) | 315 args: (entity, avatar file) |
316 - presence: called when a presence is received | |
317 args: (entity, show, priority, statuses) | |
297 @param callback: method to call on event | 318 @param callback: method to call on event |
319 @param profiles_filter (set[unicode]): if set and not empty, the | |
320 listener will be callable only by one of the given profiles. | |
298 """ | 321 """ |
299 assert type_ in C.LISTENERS | 322 assert type_ in C.LISTENERS |
300 self._listeners.setdefault(type_, []).append(callback) | 323 self._listeners.setdefault(type_, OrderedDict())[callback] = profiles_filter |
301 | 324 |
302 def removeListener(self, type_, callback): | 325 def removeListener(self, type_, callback): |
303 """Remove a callback from listeners | 326 """Remove a callback from listeners |
304 | 327 |
305 @param type_: same as for [addListerner] | 328 @param type_: same as for [addListener] |
306 @param callback: callback to remove | 329 @param callback: callback to remove |
307 """ | 330 """ |
308 assert type_ in C.LISTENERS | 331 assert type_ in C.LISTENERS |
309 self._listeners[type_].remove(callback) | 332 self._listeners[type_].pop(callback) |
310 | 333 |
311 def callListeners(self, type_, *args): | 334 def callListeners(self, type_, profile, *args): |
312 """Call all methods which listen of type_ event | 335 """Call the methods which listen type_ event. If a profiles filter has |
313 | 336 been register with a listener and profile argument is not None, the |
314 @param type_: same as for [addListerner] | 337 listener will be called only if profile is in the profiles filter list. |
338 | |
339 @param type_: same as for [addListener] | |
340 @param profile (unicode): %(doc_profile)s | |
315 @param *args: arguments sent to callback | 341 @param *args: arguments sent to callback |
316 """ | 342 """ |
317 assert type_ in C.LISTENERS | 343 assert type_ in C.LISTENERS |
318 try: | 344 try: |
319 listeners = self._listeners[type_] | 345 listeners = self._listeners[type_] |
320 except KeyError: | 346 except KeyError: |
321 pass | 347 pass |
322 else: | 348 else: |
323 for listener in listeners: | 349 for listener, profiles_filter in listeners.iteritems(): |
324 listener(*args) | 350 if profile is None or not profiles_filter or profile in profiles_filter: |
351 listener(*args) | |
325 | 352 |
326 def check_profile(self, profile): | 353 def check_profile(self, profile): |
327 """Tell if the profile is currently followed by the application""" | 354 """Tell if the profile is currently followed by the application""" |
328 return profile in self.profiles | 355 return profile in self.profiles |
329 | 356 |
372 | 399 |
373 def unplug_profile(self, profile): | 400 def unplug_profile(self, profile): |
374 """Tell the application to not follow anymore the profile""" | 401 """Tell the application to not follow anymore the profile""" |
375 if not profile in self.profiles: | 402 if not profile in self.profiles: |
376 raise ValueError("The profile [{}] is not plugged".format(profile)) | 403 raise ValueError("The profile [{}] is not plugged".format(profile)) |
377 self.profiles.remove(profile) | 404 self.profiles.unplug(profile) |
378 | 405 |
379 def clear_profile(self): | 406 def clear_profile(self): |
380 self.profiles.clear() | 407 self.profiles.clear() |
381 | 408 |
382 def addContactList(self, profile): | 409 def addContactList(self, profile): |
466 | 493 |
467 # #FIXME: must be moved in a plugin | 494 # #FIXME: must be moved in a plugin |
468 # if entity.bare in self.profiles[profile].data.get('watched',[]) and not entity.bare in self.profiles[profile]['onlineContact']: | 495 # if entity.bare in self.profiles[profile].data.get('watched',[]) and not entity.bare in self.profiles[profile]['onlineContact']: |
469 # self.showAlert(_("Watched jid [%s] is connected !") % entity.bare) | 496 # self.showAlert(_("Watched jid [%s] is connected !") % entity.bare) |
470 | 497 |
471 self.contact_lists[profile].updatePresence(entity, show, priority, statuses) | 498 self.callListeners('presence', profile, entity, show, priority, statuses) |
472 | 499 |
473 def roomJoinedHandler(self, room_jid_s, room_nicks, user_nick, profile): | 500 def roomJoinedHandler(self, room_jid_s, room_nicks, user_nick, profile): |
474 """Called when a MUC room is joined""" | 501 """Called when a MUC room is joined""" |
475 log.debug("Room [%(room_jid)s] joined by %(profile)s, users presents:%(users)s" % {'room_jid': room_jid_s, 'profile': profile, 'users': room_nicks}) | 502 log.debug("Room [%(room_jid)s] joined by %(profile)s, users presents:%(users)s" % {'room_jid': room_jid_s, 'profile': profile, 'users': room_nicks}) |
476 room_jid = jid.JID(room_jid_s) | 503 room_jid = jid.JID(room_jid_s) |
612 if from_jid == C.ENTITY_ALL or from_jid.bare == widget.target.bare: | 639 if from_jid == C.ENTITY_ALL or from_jid.bare == widget.target.bare: |
613 widget.updateChatState(from_jid, state) | 640 widget.updateChatState(from_jid, state) |
614 | 641 |
615 def _subscribe_cb(self, answer, data): | 642 def _subscribe_cb(self, answer, data): |
616 entity, profile = data | 643 entity, profile = data |
617 if answer: | 644 type_ = "subscribed" if answer else "unsubscribed" |
618 self.bridge.subscription("subscribed", entity.bare, profile_key=profile) | 645 self.bridge.subscription(type_, unicode(entity.bare), profile_key=profile) |
619 else: | |
620 self.bridge.subscription("unsubscribed", entity.bare, profile_key=profile) | |
621 | 646 |
622 def subscribeHandler(self, type, raw_jid, profile): | 647 def subscribeHandler(self, type, raw_jid, profile): |
623 """Called when a subsciption management signal is received""" | 648 """Called when a subsciption management signal is received""" |
624 entity = jid.JID(raw_jid) | 649 entity = jid.JID(raw_jid) |
625 if type == "subscribed": | 650 if type == "subscribed": |
630 self.showDialog(_("The contact %s has refused your subscription") % entity.bare, _('Subscription refusal'), 'error') | 655 self.showDialog(_("The contact %s has refused your subscription") % entity.bare, _('Subscription refusal'), 'error') |
631 elif type == "subscribe": | 656 elif type == "subscribe": |
632 # this is a subscriptionn request, we have to ask for user confirmation | 657 # this is a subscriptionn request, we have to ask for user confirmation |
633 self.showDialog(_("The contact %s wants to subscribe to your presence.\nDo you accept ?") % entity.bare, _('Subscription confirmation'), 'yes/no', answer_cb=self._subscribe_cb, answer_data=(entity, profile)) | 658 self.showDialog(_("The contact %s wants to subscribe to your presence.\nDo you accept ?") % entity.bare, _('Subscription confirmation'), 'yes/no', answer_cb=self._subscribe_cb, answer_data=(entity, profile)) |
634 | 659 |
635 def showDialog(self, message, title, type="info", answer_cb=None): | 660 def showDialog(self, message, title, type="info", answer_cb=None, answer_data=None): |
636 raise NotImplementedError | 661 raise NotImplementedError |
637 | 662 |
638 def showAlert(self, message): | 663 def showAlert(self, message): |
639 pass #FIXME | 664 pass #FIXME |
640 | 665 |
648 elif (namespace, name) == ('General', C.SHOW_OFFLINE_CONTACTS): | 673 elif (namespace, name) == ('General', C.SHOW_OFFLINE_CONTACTS): |
649 self.contact_lists[profile].showOfflineContacts(C.bool(value)) | 674 self.contact_lists[profile].showOfflineContacts(C.bool(value)) |
650 elif (namespace, name) == ('General', C.SHOW_EMPTY_GROUPS): | 675 elif (namespace, name) == ('General', C.SHOW_EMPTY_GROUPS): |
651 self.contact_lists[profile].showEmptyGroups(C.bool(value)) | 676 self.contact_lists[profile].showEmptyGroups(C.bool(value)) |
652 | 677 |
653 def contactDeletedHandler(self, jid, profile): | 678 def contactDeletedHandler(self, jid_s, profile): |
654 target = jid.JID(jid) | 679 target = jid.JID(jid_s) |
655 self.contact_lists[profile].remove(target) | 680 self.contact_lists[profile].remove(target) |
656 | 681 |
657 def entityDataUpdatedHandler(self, entity_s, key, value, profile): | 682 def entityDataUpdatedHandler(self, entity_s, key, value, profile): |
658 entity = jid.JID(entity_s) | 683 entity = jid.JID(entity_s) |
659 if key == "nick": | 684 if key == "nick": |
661 self.contact_lists[profile].setCache(entity, 'nick', value) | 686 self.contact_lists[profile].setCache(entity, 'nick', value) |
662 elif key == "avatar": | 687 elif key == "avatar": |
663 if entity in self.contact_lists[profile]: | 688 if entity in self.contact_lists[profile]: |
664 def gotFilename(filename): | 689 def gotFilename(filename): |
665 self.contact_lists[profile].setCache(entity, 'avatar', filename) | 690 self.contact_lists[profile].setCache(entity, 'avatar', filename) |
666 self.callListeners('avatar', entity, filename, profile) | 691 self.callListeners('avatar', profile, entity, filename) |
667 self.bridge.getAvatarFile(value, callback=gotFilename) | 692 self.bridge.getAvatarFile(value, callback=gotFilename) |
668 | 693 |
669 def askConfirmationHandler(self, confirm_id, confirm_type, data, profile): | 694 def askConfirmationHandler(self, confirm_id, confirm_type, data, profile): |
670 raise NotImplementedError | 695 raise NotImplementedError |
671 | 696 |
685 """ | 710 """ |
686 raise NotImplementedError | 711 raise NotImplementedError |
687 | 712 |
688 def onExit(self): | 713 def onExit(self): |
689 """Must be called when the frontend is terminating""" | 714 """Must be called when the frontend is terminating""" |
715 to_unplug = [] | |
690 for profile in self.profiles: | 716 for profile in self.profiles: |
691 if self.bridge.isConnected(profile): | 717 if self.bridge.isConnected(profile): |
692 if C.bool(self.bridge.getParamA("autodisconnect", "Connection", profile_key=profile)): | 718 if C.bool(self.bridge.getParamA("autodisconnect", "Connection", profile_key=profile)): |
693 #The user wants autodisconnection | 719 #The user wants autodisconnection |
694 self.bridge.disconnect(profile) | 720 self.bridge.disconnect(profile) |
721 to_unplug.append(profile) | |
722 for profile in to_unplug: | |
723 self.unplug_profile(profile) |