comparison sat_frontends/quick_frontend/quick_app.py @ 2624:56f94936df1e

code style reformatting using black
author Goffi <goffi@goffi.org>
date Wed, 27 Jun 2018 20:14:46 +0200
parents fe9888d3fcb6
children 6ef2b4fa90a5
comparison
equal deleted inserted replaced
2623:49533de4540b 2624:56f94936df1e
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.log import getLogger 20 from sat.core.log import getLogger
21
21 log = getLogger(__name__) 22 log = getLogger(__name__)
22 23
23 from sat.core.i18n import _ 24 from sat.core.i18n import _
24 from sat.core import exceptions 25 from sat.core import exceptions
25 from sat.tools import trigger 26 from sat.tools import trigger
37 from collections import OrderedDict 38 from collections import OrderedDict
38 import time 39 import time
39 40
40 try: 41 try:
41 # FIXME: to be removed when an acceptable solution is here 42 # FIXME: to be removed when an acceptable solution is here
42 unicode('') # XXX: unicode doesn't exist in pyjamas 43 unicode("") # XXX: unicode doesn't exist in pyjamas
43 except (TypeError, AttributeError): # Error raised is not the same depending on pyjsbuild options 44 except (
45 TypeError,
46 AttributeError,
47 ): # Error raised is not the same depending on pyjsbuild options
44 unicode = str 48 unicode = str
45 49
46 50
47 class ProfileManager(object): 51 class ProfileManager(object):
48 """Class managing all data relative to one profile, and plugging in mechanism""" 52 """Class managing all data relative to one profile, and plugging in mechanism"""
53
49 # TODO: handle waiting XMLUI requests: getWaitingConf doesn't exist anymore 54 # TODO: handle waiting XMLUI requests: getWaitingConf doesn't exist anymore
50 # and a way to keep some XMLUI request between sessions is expected in backend 55 # and a way to keep some XMLUI request between sessions is expected in backend
51 host = None 56 host = None
52 bridge = None 57 bridge = None
53 # cache_keys_to_get = ['avatar'] 58 # cache_keys_to_get = ['avatar']
67 return autodisconnect 72 return autodisconnect
68 73
69 def plug(self): 74 def plug(self):
70 """Plug the profile to the host""" 75 """Plug the profile to the host"""
71 # we get the essential params 76 # we get the essential params
72 self.bridge.asyncGetParamA("JabberID", "Connection", profile_key=self.profile, 77 self.bridge.asyncGetParamA(
73 callback=self._plug_profile_jid, errback=self._getParamError) 78 "JabberID",
79 "Connection",
80 profile_key=self.profile,
81 callback=self._plug_profile_jid,
82 errback=self._getParamError,
83 )
74 84
75 def _plug_profile_jid(self, jid_s): 85 def _plug_profile_jid(self, jid_s):
76 self.whoami = jid.JID(jid_s) # resource might change after the connection 86 self.whoami = jid.JID(jid_s) # resource might change after the connection
77 self.bridge.isConnected(self.profile, callback=self._plug_profile_isconnected) 87 self.bridge.isConnected(self.profile, callback=self._plug_profile_isconnected)
78 88
79 def _autodisconnectEb(self, failure_): 89 def _autodisconnectEb(self, failure_):
80 # XXX: we ignore error on this parameter, as Libervia can't access it 90 # XXX: we ignore error on this parameter, as Libervia can't access it
81 log.warning(_("Error while trying to get autodisconnect param, ignoring: {}").format(failure_)) 91 log.warning(
92 _("Error while trying to get autodisconnect param, ignoring: {}").format(
93 failure_
94 )
95 )
82 self._plug_profile_autodisconnect("false") 96 self._plug_profile_autodisconnect("false")
83 97
84 def _plug_profile_isconnected(self, connected): 98 def _plug_profile_isconnected(self, connected):
85 self.connected = connected 99 self.connected = connected
86 self.bridge.asyncGetParamA("autodisconnect", "Connection", profile_key=self.profile, 100 self.bridge.asyncGetParamA(
87 callback=self._plug_profile_autodisconnect, errback=self._autodisconnectEb) 101 "autodisconnect",
102 "Connection",
103 profile_key=self.profile,
104 callback=self._plug_profile_autodisconnect,
105 errback=self._autodisconnectEb,
106 )
88 107
89 def _plug_profile_autodisconnect(self, autodisconnect): 108 def _plug_profile_autodisconnect(self, autodisconnect):
90 if C.bool(autodisconnect): 109 if C.bool(autodisconnect):
91 self._autodisconnect = True 110 self._autodisconnect = True
92 self.bridge.asyncGetParamA("autoconnect", "Connection", profile_key=self.profile, 111 self.bridge.asyncGetParamA(
93 callback=self._plug_profile_autoconnect, errback=self._getParamError) 112 "autoconnect",
113 "Connection",
114 profile_key=self.profile,
115 callback=self._plug_profile_autoconnect,
116 errback=self._getParamError,
117 )
94 118
95 def _plug_profile_autoconnect(self, value_str): 119 def _plug_profile_autoconnect(self, value_str):
96 autoconnect = C.bool(value_str) 120 autoconnect = C.bool(value_str)
97 if autoconnect and not self.connected: 121 if autoconnect and not self.connected:
98 self.host.connect(self.profile, callback=lambda dummy: self._plug_profile_afterconnect()) 122 self.host.connect(
123 self.profile, callback=lambda dummy: self._plug_profile_afterconnect()
124 )
99 else: 125 else:
100 self._plug_profile_afterconnect() 126 self._plug_profile_afterconnect()
101 127
102 def _plug_profile_afterconnect(self): 128 def _plug_profile_afterconnect(self):
103 # Profile can be connected or not 129 # Profile can be connected or not
104 # we get cached data 130 # we get cached data
105 self.connected = True 131 self.connected = True
106 self.host.bridge.getFeatures(profile_key=self.profile, callback=self._plug_profile_getFeaturesCb, errback=self._plug_profile_getFeaturesEb) 132 self.host.bridge.getFeatures(
133 profile_key=self.profile,
134 callback=self._plug_profile_getFeaturesCb,
135 errback=self._plug_profile_getFeaturesEb,
136 )
107 137
108 def _plug_profile_getFeaturesEb(self, failure): 138 def _plug_profile_getFeaturesEb(self, failure):
109 log.error(u"Couldn't get features: {}".format(failure)) 139 log.error(u"Couldn't get features: {}".format(failure))
110 self._plug_profile_getFeaturesCb({}) 140 self._plug_profile_getFeaturesCb({})
111 141
127 for entity_s, data in cached_values.iteritems(): 157 for entity_s, data in cached_values.iteritems():
128 for key, value in data.iteritems(): 158 for key, value in data.iteritems():
129 self.host.entityDataUpdatedHandler(entity_s, key, value, self.profile) 159 self.host.entityDataUpdatedHandler(entity_s, key, value, self.profile)
130 160
131 if not self.connected: 161 if not self.connected:
132 self.host.setPresenceStatus(C.PRESENCE_UNAVAILABLE, '', profile=self.profile) 162 self.host.setPresenceStatus(C.PRESENCE_UNAVAILABLE, "", profile=self.profile)
133 else: 163 else:
134 164
135 contact_list.fill() 165 contact_list.fill()
136 self.host.setPresenceStatus(profile=self.profile) 166 self.host.setPresenceStatus(profile=self.profile)
137 167
138 #The waiting subscription requests 168 # The waiting subscription requests
139 self.bridge.getWaitingSub(self.profile, callback=self._plug_profile_gotWaitingSub) 169 self.bridge.getWaitingSub(
170 self.profile, callback=self._plug_profile_gotWaitingSub
171 )
140 172
141 def _plug_profile_gotWaitingSub(self, waiting_sub): 173 def _plug_profile_gotWaitingSub(self, waiting_sub):
142 for sub in waiting_sub: 174 for sub in waiting_sub:
143 self.host.subscribeHandler(waiting_sub[sub], sub, self.profile) 175 self.host.subscribeHandler(waiting_sub[sub], sub, self.profile)
144 176
145 self.bridge.mucGetRoomsJoined(self.profile, callback=self._plug_profile_gotRoomsJoined) 177 self.bridge.mucGetRoomsJoined(
178 self.profile, callback=self._plug_profile_gotRoomsJoined
179 )
146 180
147 def _plug_profile_gotRoomsJoined(self, rooms_args): 181 def _plug_profile_gotRoomsJoined(self, rooms_args):
148 #Now we open the MUC window where we already are: 182 # Now we open the MUC window where we already are:
149 for room_args in rooms_args: 183 for room_args in rooms_args:
150 self.host.mucRoomJoinedHandler(*room_args, profile=self.profile) 184 self.host.mucRoomJoinedHandler(*room_args, profile=self.profile)
151 #Presence must be requested after rooms are filled 185 # Presence must be requested after rooms are filled
152 self.host.bridge.getPresenceStatuses(self.profile, callback=self._plug_profile_gotPresences) 186 self.host.bridge.getPresenceStatuses(
187 self.profile, callback=self._plug_profile_gotPresences
188 )
153 189
154 def _plug_profile_gotPresences(self, presences): 190 def _plug_profile_gotPresences(self, presences):
155 def gotEntityData(data, contact): 191 def gotEntityData(data, contact):
156 for key in ('avatar', 'nick'): 192 for key in ("avatar", "nick"):
157 if key in data: 193 if key in data:
158 self.host.entityDataUpdatedHandler(contact, key, data[key], self.profile) 194 self.host.entityDataUpdatedHandler(
195 contact, key, data[key], self.profile
196 )
159 197
160 for contact in presences: 198 for contact in presences:
161 for res in presences[contact]: 199 for res in presences[contact]:
162 jabber_id = (u'%s/%s' % (jid.JID(contact).bare, res)) if res else contact 200 jabber_id = (u"%s/%s" % (jid.JID(contact).bare, res)) if res else contact
163 show = presences[contact][res][0] 201 show = presences[contact][res][0]
164 priority = presences[contact][res][1] 202 priority = presences[contact][res][1]
165 statuses = presences[contact][res][2] 203 statuses = presences[contact][res][2]
166 self.host.presenceUpdateHandler(jabber_id, show, priority, statuses, self.profile) 204 self.host.presenceUpdateHandler(
167 self.host.bridge.getEntityData(contact, ['avatar', 'nick'], self.profile, callback=lambda data, contact=contact: gotEntityData(data, contact), errback=lambda failure, contact=contact: log.debug(u"No cache data for {}".format(contact))) 205 jabber_id, show, priority, statuses, self.profile
206 )
207 self.host.bridge.getEntityData(
208 contact,
209 ["avatar", "nick"],
210 self.profile,
211 callback=lambda data, contact=contact: gotEntityData(data, contact),
212 errback=lambda failure, contact=contact: log.debug(
213 u"No cache data for {}".format(contact)
214 ),
215 )
168 216
169 # At this point, profile should be fully plugged 217 # At this point, profile should be fully plugged
170 # and we launch frontend specific method 218 # and we launch frontend specific method
171 self.host.profilePlugged(self.profile) 219 self.host.profilePlugged(self.profile)
172 220
198 def itervalues(self): 246 def itervalues(self):
199 return self._profiles.itervalues() 247 return self._profiles.itervalues()
200 248
201 def plug(self, profile): 249 def plug(self, profile):
202 if profile in self._profiles: 250 if profile in self._profiles:
203 raise exceptions.ConflictError('A profile of the name [{}] is already plugged'.format(profile)) 251 raise exceptions.ConflictError(
252 "A profile of the name [{}] is already plugged".format(profile)
253 )
204 self._profiles[profile] = ProfileManager(profile) 254 self._profiles[profile] = ProfileManager(profile)
205 self._profiles[profile].plug() 255 self._profiles[profile].plug()
206 256
207 def unplug(self, profile): 257 def unplug(self, profile):
208 if profile not in self._profiles: 258 if profile not in self._profiles:
209 raise ValueError('The profile [{}] is not plugged'.format(profile)) 259 raise ValueError("The profile [{}] is not plugged".format(profile))
210 260
211 # remove the contact list and its listener 261 # remove the contact list and its listener
212 host = self._profiles[profile].host 262 host = self._profiles[profile].host
213 host.contact_lists[profile].unplug() 263 host.contact_lists[profile].unplug()
214 264
218 return self._profiles.keys()[0] 268 return self._profiles.keys()[0]
219 269
220 270
221 class QuickApp(object): 271 class QuickApp(object):
222 """This class contain the main methods needed for the frontend""" 272 """This class contain the main methods needed for the frontend"""
273
223 MB_HANDLER = True # Set to False if the frontend doesn't manage microblog 274 MB_HANDLER = True # Set to False if the frontend doesn't manage microblog
224 AVATARS_HANDLER = True # set to False if avatars are not used 275 AVATARS_HANDLER = True # set to False if avatars are not used
225 276
226 def __init__(self, bridge_factory, xmlui, check_options=None, connect_bridge=True): 277 def __init__(self, bridge_factory, xmlui, check_options=None, connect_bridge=True):
227 """Create a frontend application 278 """Create a frontend application
232 """ 283 """
233 self.xmlui = xmlui 284 self.xmlui = xmlui
234 self.menus = quick_menus.QuickMenusManager(self) 285 self.menus = quick_menus.QuickMenusManager(self)
235 ProfileManager.host = self 286 ProfileManager.host = self
236 self.profiles = ProfilesManager() 287 self.profiles = ProfilesManager()
237 self._plugs_in_progress = set() # profiles currently being plugged, used to (un)lock contact list updates 288 self._plugs_in_progress = (
238 self.ready_profiles = set() # profiles which are connected and ready 289 set()
239 self.signals_cache = {} # used to keep signal received between start of plug_profile and when the profile is actualy ready 290 ) # profiles currently being plugged, used to (un)lock contact list updates
291 self.ready_profiles = set() # profiles which are connected and ready
292 self.signals_cache = {} # used to keep signal received between start of plug_profile and when the profile is actualy ready
240 self.contact_lists = quick_contact_list.QuickContactListHandler(self) 293 self.contact_lists = quick_contact_list.QuickContactListHandler(self)
241 self.widgets = quick_widgets.QuickWidgetsManager(self) 294 self.widgets = quick_widgets.QuickWidgetsManager(self)
242 if check_options is not None: 295 if check_options is not None:
243 self.options = check_options() 296 self.options = check_options()
244 else: 297 else:
245 self.options = None 298 self.options = None
246 299
247 # widgets 300 # widgets
248 self.selected_widget = None # widget currently selected (must be filled by frontend) 301 self.selected_widget = (
302 None
303 ) # widget currently selected (must be filled by frontend)
249 304
250 # listeners 305 # listeners
251 self._listeners = {} # key: listener type ("avatar", "selected", etc), value: list of callbacks 306 self._listeners = {} # key: listener type ("avatar", "selected", etc), value: list of callbacks
252 307
253 # triggers 308 # triggers
254 self.trigger = trigger.TriggerManager() # trigger are used to change the default behaviour 309 self.trigger = (
310 trigger.TriggerManager()
311 ) # trigger are used to change the default behaviour
255 312
256 ## bridge ## 313 ## bridge ##
257 self.bridge = bridge_factory() 314 self.bridge = bridge_factory()
258 ProfileManager.bridge = self.bridge 315 ProfileManager.bridge = self.bridge
259 if connect_bridge: 316 if connect_bridge:
272 329
273 def _namespacesGetEb(self, failure_): 330 def _namespacesGetEb(self, failure_):
274 log.error(_(u"Can't get namespaces map: {msg}").format(msg=failure_)) 331 log.error(_(u"Can't get namespaces map: {msg}").format(msg=failure_))
275 332
276 def onBridgeConnected(self): 333 def onBridgeConnected(self):
277 self.bridge.namespacesGet(callback=self._namespacesGetCb, 334 self.bridge.namespacesGet(
278 errback=self._namespacesGetEb) 335 callback=self._namespacesGetCb, errback=self._namespacesGetEb
336 )
279 337
280 def _bridgeCb(self): 338 def _bridgeCb(self):
281 self.registerSignal("connected") 339 self.registerSignal("connected")
282 self.registerSignal("disconnected") 340 self.registerSignal("disconnected")
283 self.registerSignal("actionNew") 341 self.registerSignal("actionNew")
329 387
330 @return (iter[QuickWidget]): iterable on visible widgets 388 @return (iter[QuickWidget]): iterable on visible widgets
331 """ 389 """
332 raise NotImplementedError 390 raise NotImplementedError
333 391
334 def registerSignal(self, function_name, handler=None, iface="core", with_profile=True): 392 def registerSignal(
393 self, function_name, handler=None, iface="core", with_profile=True
394 ):
335 """Register a handler for a signal 395 """Register a handler for a signal
336 396
337 @param function_name (str): name of the signal to handle 397 @param function_name (str): name of the signal to handle
338 @param handler (instancemethod): method to call when the signal arrive, None for calling an automatically named handler (function_name + 'Handler') 398 @param handler (instancemethod): method to call when the signal arrive, None for calling an automatically named handler (function_name + 'Handler')
339 @param iface (str): interface of the bridge to use ('core' or 'plugin') 399 @param iface (str): interface of the bridge to use ('core' or 'plugin')
340 @param with_profile (boolean): True if the signal concerns a specific profile, in that case the profile name has to be passed by the caller 400 @param with_profile (boolean): True if the signal concerns a specific profile, in that case the profile name has to be passed by the caller
341 """ 401 """
342 log.debug(u"registering signal {name}".format(name = function_name)) 402 log.debug(u"registering signal {name}".format(name=function_name))
343 if handler is None: 403 if handler is None:
344 handler = getattr(self, "{}{}".format(function_name, 'Handler')) 404 handler = getattr(self, "{}{}".format(function_name, "Handler"))
345 if not with_profile: 405 if not with_profile:
346 self.bridge.register_signal(function_name, handler, iface) 406 self.bridge.register_signal(function_name, handler, iface)
347 return 407 return
348 408
349 def signalReceived(*args, **kwargs): 409 def signalReceived(*args, **kwargs):
350 profile = kwargs.get('profile') 410 profile = kwargs.get("profile")
351 if profile is None: 411 if profile is None:
352 if not args: 412 if not args:
353 raise exceptions.ProfileNotSetError 413 raise exceptions.ProfileNotSetError
354 profile = args[-1] 414 profile = args[-1]
355 if profile is not None: 415 if profile is not None:
356 if not self.check_profile(profile): 416 if not self.check_profile(profile):
357 if profile in self.profiles: 417 if profile in self.profiles:
358 # profile is not ready but is in self.profiles, that's mean that it's being connecting and we need to cache the signal 418 # profile is not ready but is in self.profiles, that's mean that it's being connecting and we need to cache the signal
359 self.signals_cache.setdefault(profile, []).append((function_name, handler, args, kwargs)) 419 self.signals_cache.setdefault(profile, []).append(
420 (function_name, handler, args, kwargs)
421 )
360 return # we ignore signal for profiles we don't manage 422 return # we ignore signal for profiles we don't manage
361 handler(*args, **kwargs) 423 handler(*args, **kwargs)
424
362 self.bridge.register_signal(function_name, signalReceived, iface) 425 self.bridge.register_signal(function_name, signalReceived, iface)
363 426
364 def addListener(self, type_, callback, profiles_filter=None): 427 def addListener(self, type_, callback, profiles_filter=None):
365 """Add a listener for an event 428 """Add a listener for an event
366 429
447 self.ready_profiles.add(profile) 510 self.ready_profiles.add(profile)
448 511
449 # profile is ready, we can call send signals that where is cache 512 # profile is ready, we can call send signals that where is cache
450 cached_signals = self.signals_cache.pop(profile, []) 513 cached_signals = self.signals_cache.pop(profile, [])
451 for function_name, handler, args, kwargs in cached_signals: 514 for function_name, handler, args, kwargs in cached_signals:
452 log.debug(u"Calling cached signal [%s] with args %s and kwargs %s" % (function_name, args, kwargs)) 515 log.debug(
516 u"Calling cached signal [%s] with args %s and kwargs %s"
517 % (function_name, args, kwargs)
518 )
453 handler(*args, **kwargs) 519 handler(*args, **kwargs)
454 520
455 self.callListeners('profilePlugged', profile=profile) 521 self.callListeners("profilePlugged", profile=profile)
456 if not self._plugs_in_progress: 522 if not self._plugs_in_progress:
457 self.contact_lists.lockUpdate(False) 523 self.contact_lists.lockUpdate(False)
458 524
459 def connect(self, profile, callback=None, errback=None): 525 def connect(self, profile, callback=None, errback=None):
460 if not callback: 526 if not callback:
461 callback = lambda dummy: None 527 callback = lambda dummy: None
462 if not errback: 528 if not errback:
529
463 def errback(failure): 530 def errback(failure):
464 log.error(_(u"Can't connect profile [%s]") % failure) 531 log.error(_(u"Can't connect profile [%s]") % failure)
465 try: 532 try:
466 module = failure.module 533 module = failure.module
467 except AttributeError: 534 except AttributeError:
468 module = '' 535 module = ""
469 try: 536 try:
470 message = failure.message 537 message = failure.message
471 except AttributeError: 538 except AttributeError:
472 message = 'error' 539 message = "error"
473 try: 540 try:
474 fullname = failure.fullname 541 fullname = failure.fullname
475 except AttributeError: 542 except AttributeError:
476 fullname = 'error' 543 fullname = "error"
477 if module.startswith('twisted.words.protocols.jabber') and failure.condition == "not-authorized": 544 if (
545 module.startswith("twisted.words.protocols.jabber")
546 and failure.condition == "not-authorized"
547 ):
478 self.launchAction(C.CHANGE_XMPP_PASSWD_ID, {}, profile=profile) 548 self.launchAction(C.CHANGE_XMPP_PASSWD_ID, {}, profile=profile)
479 else: 549 else:
480 self.showDialog(message, fullname, 'error') 550 self.showDialog(message, fullname, "error")
551
481 self.bridge.connect(profile, callback=callback, errback=errback) 552 self.bridge.connect(profile, callback=callback, errback=errback)
482 553
483 def plug_profiles(self, profiles): 554 def plug_profiles(self, profiles):
484 """Tell application which profiles must be used 555 """Tell application which profiles must be used
485 556
525 596
526 def disconnectedHandler(self, profile): 597 def disconnectedHandler(self, profile):
527 """called when the connection is closed""" 598 """called when the connection is closed"""
528 log.debug(_("Disconnected")) 599 log.debug(_("Disconnected"))
529 self.contact_lists[profile].disconnect() 600 self.contact_lists[profile].disconnect()
530 self.setPresenceStatus(C.PRESENCE_UNAVAILABLE, '', profile=profile) 601 self.setPresenceStatus(C.PRESENCE_UNAVAILABLE, "", profile=profile)
531 602
532 def actionNewHandler(self, action_data, id_, security_limit, profile): 603 def actionNewHandler(self, action_data, id_, security_limit, profile):
533 self.actionManager(action_data, user_action=False, profile=profile) 604 self.actionManager(action_data, user_action=False, profile=profile)
534 605
535 def newContactHandler(self, jid_s, attributes, groups, profile): 606 def newContactHandler(self, jid_s, attributes, groups, profile):
536 entity = jid.JID(jid_s) 607 entity = jid.JID(jid_s)
537 groups = list(groups) 608 groups = list(groups)
538 self.contact_lists[profile].setContact(entity, groups, attributes, in_roster=True) 609 self.contact_lists[profile].setContact(entity, groups, attributes, in_roster=True)
539 610
540 def messageNewHandler(self, uid, timestamp, from_jid_s, to_jid_s, msg, subject, type_, extra, profile): 611 def messageNewHandler(
612 self, uid, timestamp, from_jid_s, to_jid_s, msg, subject, type_, extra, profile
613 ):
541 from_jid = jid.JID(from_jid_s) 614 from_jid = jid.JID(from_jid_s)
542 to_jid = jid.JID(to_jid_s) 615 to_jid = jid.JID(to_jid_s)
543 if not self.trigger.point("messageNewTrigger", uid, timestamp, from_jid, to_jid, msg, subject, type_, extra, profile=profile): 616 if not self.trigger.point(
617 "messageNewTrigger",
618 uid,
619 timestamp,
620 from_jid,
621 to_jid,
622 msg,
623 subject,
624 type_,
625 extra,
626 profile=profile,
627 ):
544 return 628 return
545 629
546 from_me = from_jid.bare == self.profiles[profile].whoami.bare 630 from_me = from_jid.bare == self.profiles[profile].whoami.bare
547 target = to_jid if from_me else from_jid 631 target = to_jid if from_me else from_jid
548 contact_list = self.contact_lists[profile] 632 contact_list = self.contact_lists[profile]
549 if target.resource and not contact_list.isRoom(target.bare): 633 if target.resource and not contact_list.isRoom(target.bare):
550 # we avoid resource locking, but we must keep resource for private MUC messages 634 # we avoid resource locking, but we must keep resource for private MUC messages
551 target = target.bare 635 target = target.bare
552 # we want to be sure to have at least one QuickChat instance 636 # we want to be sure to have at least one QuickChat instance
553 self.widgets.getOrCreateWidget(quick_chat.QuickChat, target, type_=C.CHAT_ONE2ONE, on_new_widget=None, profile=profile) 637 self.widgets.getOrCreateWidget(
554 638 quick_chat.QuickChat,
555 if not from_jid in contact_list and from_jid.bare != self.profiles[profile].whoami.bare: 639 target,
556 #XXX: needed to show entities which haven't sent any 640 type_=C.CHAT_ONE2ONE,
641 on_new_widget=None,
642 profile=profile,
643 )
644
645 if (
646 not from_jid in contact_list
647 and from_jid.bare != self.profiles[profile].whoami.bare
648 ):
649 # XXX: needed to show entities which haven't sent any
557 # presence information and which are not in roster 650 # presence information and which are not in roster
558 contact_list.setContact(from_jid) 651 contact_list.setContact(from_jid)
559 652
560 # we dispatch the message in the widgets 653 # we dispatch the message in the widgets
561 for widget in self.widgets.getWidgets(quick_chat.QuickChat, target=target, profiles=(profile,)): 654 for widget in self.widgets.getWidgets(
562 widget.messageNew(uid, timestamp, from_jid, target, msg, subject, type_, extra, profile) 655 quick_chat.QuickChat, target=target, profiles=(profile,)
656 ):
657 widget.messageNew(
658 uid, timestamp, from_jid, target, msg, subject, type_, extra, profile
659 )
563 660
564 def messageStateHandler(self, uid, status, profile): 661 def messageStateHandler(self, uid, status, profile):
565 for widget in self.widgets.getWidgets(quick_chat.QuickChat, profiles=(profile,)): 662 for widget in self.widgets.getWidgets(quick_chat.QuickChat, profiles=(profile,)):
566 widget.onMessageState(uid, status, profile) 663 widget.onMessageState(uid, status, profile)
567 664
568 def messageSend(self, to_jid, message, subject=None, mess_type="auto", extra=None, callback=None, errback=None, profile_key=C.PROF_KEY_NONE): 665 def messageSend(
666 self,
667 to_jid,
668 message,
669 subject=None,
670 mess_type="auto",
671 extra=None,
672 callback=None,
673 errback=None,
674 profile_key=C.PROF_KEY_NONE,
675 ):
569 if subject is None: 676 if subject is None:
570 subject = {} 677 subject = {}
571 if extra is None: 678 if extra is None:
572 extra = {} 679 extra = {}
573 if callback is None: 680 if callback is None:
574 callback = lambda dummy=None: None # FIXME: optional argument is here because pyjamas doesn't support callback without arg with json proxy 681 callback = (
682 lambda dummy=None: None
683 ) # FIXME: optional argument is here because pyjamas doesn't support callback without arg with json proxy
575 if errback is None: 684 if errback is None:
576 errback = lambda failure: self.showDialog(failure.fullname, failure.message, "error") 685 errback = lambda failure: self.showDialog(
577 686 failure.fullname, failure.message, "error"
578 if not self.trigger.point("messageSendTrigger", to_jid, message, subject, mess_type, extra, callback, errback, profile_key=profile_key): 687 )
688
689 if not self.trigger.point(
690 "messageSendTrigger",
691 to_jid,
692 message,
693 subject,
694 mess_type,
695 extra,
696 callback,
697 errback,
698 profile_key=profile_key,
699 ):
579 return 700 return
580 701
581 self.bridge.messageSend(unicode(to_jid), message, subject, mess_type, extra, profile_key, callback=callback, errback=errback) 702 self.bridge.messageSend(
582 703 unicode(to_jid),
583 def setPresenceStatus(self, show='', status=None, profile=C.PROF_KEY_NONE): 704 message,
705 subject,
706 mess_type,
707 extra,
708 profile_key,
709 callback=callback,
710 errback=errback,
711 )
712
713 def setPresenceStatus(self, show="", status=None, profile=C.PROF_KEY_NONE):
584 raise NotImplementedError 714 raise NotImplementedError
585 715
586 def presenceUpdateHandler(self, entity_s, show, priority, statuses, profile): 716 def presenceUpdateHandler(self, entity_s, show, priority, statuses, profile):
587 log.debug(_(u"presence update for %(entity)s (show=%(show)s, priority=%(priority)s, statuses=%(statuses)s) [profile:%(profile)s]") 717 log.debug(
588 % {'entity': entity_s, C.PRESENCE_SHOW: show, C.PRESENCE_PRIORITY: priority, C.PRESENCE_STATUSES: statuses, 'profile': profile}) 718 _(
719 u"presence update for %(entity)s (show=%(show)s, priority=%(priority)s, statuses=%(statuses)s) [profile:%(profile)s]"
720 )
721 % {
722 "entity": entity_s,
723 C.PRESENCE_SHOW: show,
724 C.PRESENCE_PRIORITY: priority,
725 C.PRESENCE_STATUSES: statuses,
726 "profile": profile,
727 }
728 )
589 entity = jid.JID(entity_s) 729 entity = jid.JID(entity_s)
590 730
591 if entity == self.profiles[profile].whoami: 731 if entity == self.profiles[profile].whoami:
592 if show == C.PRESENCE_UNAVAILABLE: 732 if show == C.PRESENCE_UNAVAILABLE:
593 self.setPresenceStatus(C.PRESENCE_UNAVAILABLE, '', profile=profile) 733 self.setPresenceStatus(C.PRESENCE_UNAVAILABLE, "", profile=profile)
594 else: 734 else:
595 # FIXME: try to retrieve user language status before fallback to default 735 # FIXME: try to retrieve user language status before fallback to default
596 status = statuses.get(C.PRESENCE_STATUSES_DEFAULT, None) 736 status = statuses.get(C.PRESENCE_STATUSES_DEFAULT, None)
597 self.setPresenceStatus(show, status, profile=profile) 737 self.setPresenceStatus(show, status, profile=profile)
598 return 738 return
599 739
600 self.callListeners('presence', entity, show, priority, statuses, profile=profile) 740 self.callListeners("presence", entity, show, priority, statuses, profile=profile)
601 741
602 def mucRoomJoinedHandler(self, room_jid_s, occupants, user_nick, subject, profile): 742 def mucRoomJoinedHandler(self, room_jid_s, occupants, user_nick, subject, profile):
603 """Called when a MUC room is joined""" 743 """Called when a MUC room is joined"""
604 log.debug(u"Room [{room_jid}] joined by {profile}, users presents:{users}".format(room_jid=room_jid_s, profile=profile, users=occupants.keys())) 744 log.debug(
745 u"Room [{room_jid}] joined by {profile}, users presents:{users}".format(
746 room_jid=room_jid_s, profile=profile, users=occupants.keys()
747 )
748 )
605 room_jid = jid.JID(room_jid_s) 749 room_jid = jid.JID(room_jid_s)
606 self.widgets.getOrCreateWidget(quick_chat.QuickChat, room_jid, type_=C.CHAT_GROUP, nick=user_nick, occupants=occupants, subject=subject, profile=profile) 750 self.widgets.getOrCreateWidget(
751 quick_chat.QuickChat,
752 room_jid,
753 type_=C.CHAT_GROUP,
754 nick=user_nick,
755 occupants=occupants,
756 subject=subject,
757 profile=profile,
758 )
607 self.contact_lists[profile].setSpecial(room_jid, C.CONTACT_SPECIAL_GROUP) 759 self.contact_lists[profile].setSpecial(room_jid, C.CONTACT_SPECIAL_GROUP)
608 # chat_widget.update() 760 # chat_widget.update()
609 761
610 def mucRoomLeftHandler(self, room_jid_s, profile): 762 def mucRoomLeftHandler(self, room_jid_s, profile):
611 """Called when a MUC room is left""" 763 """Called when a MUC room is left"""
612 log.debug(u"Room [%(room_jid)s] left by %(profile)s" % {'room_jid': room_jid_s, 'profile': profile}) 764 log.debug(
765 u"Room [%(room_jid)s] left by %(profile)s"
766 % {"room_jid": room_jid_s, "profile": profile}
767 )
613 room_jid = jid.JID(room_jid_s) 768 room_jid = jid.JID(room_jid_s)
614 chat_widget = self.widgets.getWidget(quick_chat.QuickChat, room_jid, profile) 769 chat_widget = self.widgets.getWidget(quick_chat.QuickChat, room_jid, profile)
615 if chat_widget: 770 if chat_widget:
616 self.widgets.deleteWidget(chat_widget) 771 self.widgets.deleteWidget(chat_widget)
617 self.contact_lists[profile].removeContact(room_jid) 772 self.contact_lists[profile].removeContact(room_jid)
618 773
619 def mucRoomUserChangedNickHandler(self, room_jid_s, old_nick, new_nick, profile): 774 def mucRoomUserChangedNickHandler(self, room_jid_s, old_nick, new_nick, profile):
620 """Called when an user joined a MUC room""" 775 """Called when an user joined a MUC room"""
621 room_jid = jid.JID(room_jid_s) 776 room_jid = jid.JID(room_jid_s)
622 chat_widget = self.widgets.getOrCreateWidget(quick_chat.QuickChat, room_jid, type_=C.CHAT_GROUP, profile=profile) 777 chat_widget = self.widgets.getOrCreateWidget(
778 quick_chat.QuickChat, room_jid, type_=C.CHAT_GROUP, profile=profile
779 )
623 chat_widget.changeUserNick(old_nick, new_nick) 780 chat_widget.changeUserNick(old_nick, new_nick)
624 log.debug(u"user [%(old_nick)s] is now known as [%(new_nick)s] in room [%(room_jid)s]" % {'old_nick': old_nick, 'new_nick': new_nick, 'room_jid': room_jid}) 781 log.debug(
782 u"user [%(old_nick)s] is now known as [%(new_nick)s] in room [%(room_jid)s]"
783 % {"old_nick": old_nick, "new_nick": new_nick, "room_jid": room_jid}
784 )
625 785
626 def mucRoomNewSubjectHandler(self, room_jid_s, subject, profile): 786 def mucRoomNewSubjectHandler(self, room_jid_s, subject, profile):
627 """Called when subject of MUC room change""" 787 """Called when subject of MUC room change"""
628 room_jid = jid.JID(room_jid_s) 788 room_jid = jid.JID(room_jid_s)
629 chat_widget = self.widgets.getOrCreateWidget(quick_chat.QuickChat, room_jid, type_=C.CHAT_GROUP, profile=profile) 789 chat_widget = self.widgets.getOrCreateWidget(
790 quick_chat.QuickChat, room_jid, type_=C.CHAT_GROUP, profile=profile
791 )
630 chat_widget.setSubject(subject) 792 chat_widget.setSubject(subject)
631 log.debug(u"new subject for room [%(room_jid)s]: %(subject)s" % {'room_jid': room_jid, "subject": subject}) 793 log.debug(
794 u"new subject for room [%(room_jid)s]: %(subject)s"
795 % {"room_jid": room_jid, "subject": subject}
796 )
632 797
633 def chatStateReceivedHandler(self, from_jid_s, state, profile): 798 def chatStateReceivedHandler(self, from_jid_s, state, profile):
634 """Called when a new chat state (XEP-0085) is received. 799 """Called when a new chat state (XEP-0085) is received.
635 800
636 @param from_jid_s (unicode): JID of a contact or C.ENTITY_ALL 801 @param from_jid_s (unicode): JID of a contact or C.ENTITY_ALL
639 """ 804 """
640 from_jid = jid.JID(from_jid_s) 805 from_jid = jid.JID(from_jid_s)
641 for widget in self.widgets.getWidgets(quick_chat.QuickChat, profiles=(profile,)): 806 for widget in self.widgets.getWidgets(quick_chat.QuickChat, profiles=(profile,)):
642 widget.onChatState(from_jid, state, profile) 807 widget.onChatState(from_jid, state, profile)
643 808
644 def notify(self, type_, entity=None, message=None, subject=None, callback=None, cb_args=None, widget=None, profile=C.PROF_KEY_NONE): 809 def notify(
810 self,
811 type_,
812 entity=None,
813 message=None,
814 subject=None,
815 callback=None,
816 cb_args=None,
817 widget=None,
818 profile=C.PROF_KEY_NONE,
819 ):
645 """Trigger an event notification 820 """Trigger an event notification
646 821
647 @param type_(unicode): notifation kind, 822 @param type_(unicode): notifation kind,
648 one of C.NOTIFY_* constant or any custom type specific to frontend 823 one of C.NOTIFY_* constant or any custom type specific to frontend
649 @param entity(jid.JID, None): entity involved in the notification 824 @param entity(jid.JID, None): entity involved in the notification
654 @param cb_args(list, None): list of args for callback 829 @param cb_args(list, None): list of args for callback
655 @param widget(object, None): widget where the notification happened 830 @param widget(object, None): widget where the notification happened
656 """ 831 """
657 assert type_ in C.NOTIFY_ALL 832 assert type_ in C.NOTIFY_ALL
658 notif_dict = self.profiles[profile].notifications 833 notif_dict = self.profiles[profile].notifications
659 key = '' if entity is None else entity.bare 834 key = "" if entity is None else entity.bare
660 type_notifs = notif_dict.setdefault(key, {}).setdefault(type_, []) 835 type_notifs = notif_dict.setdefault(key, {}).setdefault(type_, [])
661 notif_data = { 836 notif_data = {
662 'id': self._notif_id, 837 "id": self._notif_id,
663 'time': time.time(), 838 "time": time.time(),
664 'entity': entity, 839 "entity": entity,
665 'callback': callback, 840 "callback": callback,
666 'cb_args': cb_args, 841 "cb_args": cb_args,
667 'message': message, 842 "message": message,
668 'subject': subject, 843 "subject": subject,
669 } 844 }
670 if widget is not None: 845 if widget is not None:
671 notif_data[widget] = widget 846 notif_data[widget] = widget
672 type_notifs.append(notif_data) 847 type_notifs.append(notif_data)
673 self._notifications[self._notif_id] = notif_data 848 self._notifications[self._notif_id] = notif_data
674 self.callListeners('notification', entity, notif_data, profile=profile) 849 self.callListeners("notification", entity, notif_data, profile=profile)
675 850
676 def getNotifs(self, entity=None, type_=None, exact_jid=None, profile=C.PROF_KEY_NONE): 851 def getNotifs(self, entity=None, type_=None, exact_jid=None, profile=C.PROF_KEY_NONE):
677 """return notifications for given entity 852 """return notifications for given entity
678 853
679 @param entity(jid.JID, None, C.ENTITY_ALL): jid of the entity to check 854 @param entity(jid.JID, None, C.ENTITY_ALL): jid of the entity to check
694 if entity is C.ENTITY_ALL: 869 if entity is C.ENTITY_ALL:
695 selected_notifs = main_notif_dict.itervalues() 870 selected_notifs = main_notif_dict.itervalues()
696 exact_jid = False 871 exact_jid = False
697 else: 872 else:
698 if entity is None: 873 if entity is None:
699 key = '' 874 key = ""
700 exact_jid = False 875 exact_jid = False
701 else: 876 else:
702 key = entity.bare 877 key = entity.bare
703 if exact_jid is None: 878 if exact_jid is None:
704 exact_jid = bool(entity.resource) 879 exact_jid = bool(entity.resource)
711 else: 886 else:
712 type_notifs = (notifs_from_select.get(type_, []),) 887 type_notifs = (notifs_from_select.get(type_, []),)
713 888
714 for notifs in type_notifs: 889 for notifs in type_notifs:
715 for notif in notifs: 890 for notif in notifs:
716 if exact_jid and notif['entity'] != entity: 891 if exact_jid and notif["entity"] != entity:
717 continue 892 continue
718 yield notif 893 yield notif
719 894
720 def clearNotifs(self, entity, type_=None, profile=C.PROF_KEY_NONE): 895 def clearNotifs(self, entity, type_=None, profile=C.PROF_KEY_NONE):
721 """return notifications for given entity 896 """return notifications for given entity
725 @param type_(unicode, None): notification type to filter 900 @param type_(unicode, None): notification type to filter
726 None to clear all notifications 901 None to clear all notifications
727 @return (list[dict]): list of notifications 902 @return (list[dict]): list of notifications
728 """ 903 """
729 notif_dict = self.profiles[profile].notifications 904 notif_dict = self.profiles[profile].notifications
730 key = '' if entity is None else entity.bare 905 key = "" if entity is None else entity.bare
731 try: 906 try:
732 if type_ is None: 907 if type_ is None:
733 del notif_dict[key] 908 del notif_dict[key]
734 else: 909 else:
735 del notif_dict[key][type_] 910 del notif_dict[key][type_]
736 except KeyError: 911 except KeyError:
737 return 912 return
738 self.callListeners('notificationsClear', entity, type_, profile=profile) 913 self.callListeners("notificationsClear", entity, type_, profile=profile)
739 914
740 def psEventHandler(self, category, service_s, node, event_type, data, profile): 915 def psEventHandler(self, category, service_s, node, event_type, data, profile):
741 """Called when a PubSub event is received. 916 """Called when a PubSub event is received.
742 917
743 @param category(unicode): event category (e.g. "PEP", "MICROBLOG") 918 @param category(unicode): event category (e.g. "PEP", "MICROBLOG")
748 """ 923 """
749 service_s = jid.JID(service_s) 924 service_s = jid.JID(service_s)
750 925
751 if category == C.PS_MICROBLOG and self.MB_HANDLER: 926 if category == C.PS_MICROBLOG and self.MB_HANDLER:
752 if event_type == C.PS_PUBLISH: 927 if event_type == C.PS_PUBLISH:
753 if not 'content' in data: 928 if not "content" in data:
754 log.warning("No content found in microblog data") 929 log.warning("No content found in microblog data")
755 return 930 return
756 _groups = set(data_format.dict2iter('group', data)) or None # FIXME: check if [] make sense (instead of None) 931 _groups = (
932 set(data_format.dict2iter("group", data)) or None
933 ) # FIXME: check if [] make sense (instead of None)
757 934
758 for wid in self.widgets.getWidgets(quick_blog.QuickBlog): 935 for wid in self.widgets.getWidgets(quick_blog.QuickBlog):
759 wid.addEntryIfAccepted(service_s, node, data, _groups, profile) 936 wid.addEntryIfAccepted(service_s, node, data, _groups, profile)
760 937
761 try: 938 try:
762 comments_node, comments_service = data['comments_node'], data['comments_service'] 939 comments_node, comments_service = (
940 data["comments_node"],
941 data["comments_service"],
942 )
763 except KeyError: 943 except KeyError:
764 pass 944 pass
765 else: 945 else:
766 self.bridge.mbGet(comments_service, comments_node, C.NO_LIMIT, [], {"subscribe":C.BOOL_TRUE}, profile=profile) 946 self.bridge.mbGet(
947 comments_service,
948 comments_node,
949 C.NO_LIMIT,
950 [],
951 {"subscribe": C.BOOL_TRUE},
952 profile=profile,
953 )
767 elif event_type == C.PS_RETRACT: 954 elif event_type == C.PS_RETRACT:
768 for wid in self.widgets.getWidgets(quick_blog.QuickBlog): 955 for wid in self.widgets.getWidgets(quick_blog.QuickBlog):
769 wid.deleteEntryIfPresent(service_s, node, data['id'], profile) 956 wid.deleteEntryIfPresent(service_s, node, data["id"], profile)
770 pass 957 pass
771 else: 958 else:
772 log.warning("Unmanaged PubSub event type {}".format(event_type)) 959 log.warning("Unmanaged PubSub event type {}".format(event_type))
773 960
774 def progressStartedHandler(self, pid, metadata, profile): 961 def progressStartedHandler(self, pid, metadata, profile):
775 log.info(u"Progress {} started".format(pid)) 962 log.info(u"Progress {} started".format(pid))
776 963
777 def progressFinishedHandler(self, pid, metadata, profile): 964 def progressFinishedHandler(self, pid, metadata, profile):
778 log.info(u"Progress {} finished".format(pid)) 965 log.info(u"Progress {} finished".format(pid))
779 self.callListeners('progressFinished', pid, metadata, profile=profile) 966 self.callListeners("progressFinished", pid, metadata, profile=profile)
780 967
781 def progressErrorHandler(self, pid, err_msg, profile): 968 def progressErrorHandler(self, pid, err_msg, profile):
782 log.warning(u"Progress {pid} error: {err_msg}".format(pid=pid, err_msg=err_msg)) 969 log.warning(u"Progress {pid} error: {err_msg}".format(pid=pid, err_msg=err_msg))
783 self.callListeners('progressError', pid, err_msg, profile=profile) 970 self.callListeners("progressError", pid, err_msg, profile=profile)
784 971
785 def _subscribe_cb(self, answer, data): 972 def _subscribe_cb(self, answer, data):
786 entity, profile = data 973 entity, profile = data
787 type_ = "subscribed" if answer else "unsubscribed" 974 type_ = "subscribed" if answer else "unsubscribed"
788 self.bridge.subscription(type_, unicode(entity.bare), profile_key=profile) 975 self.bridge.subscription(type_, unicode(entity.bare), profile_key=profile)
791 """Called when a subsciption management signal is received""" 978 """Called when a subsciption management signal is received"""
792 entity = jid.JID(raw_jid) 979 entity = jid.JID(raw_jid)
793 if type == "subscribed": 980 if type == "subscribed":
794 # this is a subscription confirmation, we just have to inform user 981 # this is a subscription confirmation, we just have to inform user
795 # TODO: call self.getEntityMBlog to add the new contact blogs 982 # TODO: call self.getEntityMBlog to add the new contact blogs
796 self.showDialog(_(u"The contact {contact} has accepted your subscription") 983 self.showDialog(
797 .format(contact=entity.bare), _(u'Subscription confirmation')) 984 _(u"The contact {contact} has accepted your subscription").format(
985 contact=entity.bare
986 ),
987 _(u"Subscription confirmation"),
988 )
798 elif type == "unsubscribed": 989 elif type == "unsubscribed":
799 # this is a subscription refusal, we just have to inform user 990 # this is a subscription refusal, we just have to inform user
800 self.showDialog(_(u"The contact {contact} has refused your subscription") 991 self.showDialog(
801 .format(contact=entity.bare), 992 _(u"The contact {contact} has refused your subscription").format(
802 _(u'Subscription refusal'), 993 contact=entity.bare
803 'error') 994 ),
995 _(u"Subscription refusal"),
996 "error",
997 )
804 elif type == "subscribe": 998 elif type == "subscribe":
805 # this is a subscriptionn request, we have to ask for user confirmation 999 # this is a subscriptionn request, we have to ask for user confirmation
806 # TODO: use sat.stdui.ui_contact_list to display the groups selector 1000 # TODO: use sat.stdui.ui_contact_list to display the groups selector
807 self.showDialog(_(u"The contact {contact} wants to subscribe to your presence" 1001 self.showDialog(
808 u".\nDo you accept ?").format(contact=entity.bare), 1002 _(
809 _('Subscription confirmation'), 1003 u"The contact {contact} wants to subscribe to your presence"
810 'yes/no', 1004 u".\nDo you accept ?"
811 answer_cb=self._subscribe_cb, 1005 ).format(contact=entity.bare),
812 answer_data=(entity, profile)) 1006 _("Subscription confirmation"),
1007 "yes/no",
1008 answer_cb=self._subscribe_cb,
1009 answer_data=(entity, profile),
1010 )
813 1011
814 def showDialog(self, message, title, type="info", answer_cb=None, answer_data=None): 1012 def showDialog(self, message, title, type="info", answer_cb=None, answer_data=None):
815 """Show a dialog to user 1013 """Show a dialog to user
816 1014
817 Frontends must override this method 1015 Frontends must override this method
830 # FIXME: misnamed method + types are not well chosen. Need to be rethought 1028 # FIXME: misnamed method + types are not well chosen. Need to be rethought
831 raise NotImplementedError 1029 raise NotImplementedError
832 1030
833 def showAlert(self, message): 1031 def showAlert(self, message):
834 # FIXME: doesn't seems used anymore, to remove? 1032 # FIXME: doesn't seems used anymore, to remove?
835 pass #FIXME 1033 pass # FIXME
836 1034
837 def dialogFailure(self, failure): 1035 def dialogFailure(self, failure):
838 log.warning(u"Failure: {}".format(failure)) 1036 log.warning(u"Failure: {}".format(failure))
839 1037
840 def progressIdHandler(self, progress_id, profile): 1038 def progressIdHandler(self, progress_id, profile):
847 @return bool 1045 @return bool
848 """ 1046 """
849 raise NotImplementedError 1047 raise NotImplementedError
850 1048
851 def paramUpdateHandler(self, name, value, namespace, profile): 1049 def paramUpdateHandler(self, name, value, namespace, profile):
852 log.debug(_(u"param update: [%(namespace)s] %(name)s = %(value)s") % {'namespace': namespace, 'name': name, 'value': value}) 1050 log.debug(
1051 _(u"param update: [%(namespace)s] %(name)s = %(value)s")
1052 % {"namespace": namespace, "name": name, "value": value}
1053 )
853 if (namespace, name) == ("Connection", "JabberID"): 1054 if (namespace, name) == ("Connection", "JabberID"):
854 log.debug(_(u"Changing JID to %s") % value) 1055 log.debug(_(u"Changing JID to %s") % value)
855 self.profiles[profile].whoami = jid.JID(value) 1056 self.profiles[profile].whoami = jid.JID(value)
856 elif (namespace, name) == ('General', C.SHOW_OFFLINE_CONTACTS): 1057 elif (namespace, name) == ("General", C.SHOW_OFFLINE_CONTACTS):
857 self.contact_lists[profile].showOfflineContacts(C.bool(value)) 1058 self.contact_lists[profile].showOfflineContacts(C.bool(value))
858 elif (namespace, name) == ('General', C.SHOW_EMPTY_GROUPS): 1059 elif (namespace, name) == ("General", C.SHOW_EMPTY_GROUPS):
859 self.contact_lists[profile].showEmptyGroups(C.bool(value)) 1060 self.contact_lists[profile].showEmptyGroups(C.bool(value))
860 1061
861 def contactDeletedHandler(self, jid_s, profile): 1062 def contactDeletedHandler(self, jid_s, profile):
862 target = jid.JID(jid_s) 1063 target = jid.JID(jid_s)
863 self.contact_lists[profile].removeContact(target) 1064 self.contact_lists[profile].removeContact(target)
864 1065
865 def entityDataUpdatedHandler(self, entity_s, key, value, profile): 1066 def entityDataUpdatedHandler(self, entity_s, key, value, profile):
866 entity = jid.JID(entity_s) 1067 entity = jid.JID(entity_s)
867 if key == "nick": # this is the roster nick, not the MUC nick 1068 if key == "nick": # this is the roster nick, not the MUC nick
868 if entity in self.contact_lists[profile]: 1069 if entity in self.contact_lists[profile]:
869 self.contact_lists[profile].setCache(entity, 'nick', value) 1070 self.contact_lists[profile].setCache(entity, "nick", value)
870 self.callListeners('nick', entity, value, profile=profile) 1071 self.callListeners("nick", entity, value, profile=profile)
871 elif key == "avatar" and self.AVATARS_HANDLER: 1072 elif key == "avatar" and self.AVATARS_HANDLER:
872 if value and entity in self.contact_lists[profile]: 1073 if value and entity in self.contact_lists[profile]:
873 self.getAvatar(entity, ignore_cache=True, profile=profile) 1074 self.getAvatar(entity, ignore_cache=True, profile=profile)
874 1075
875 def actionManager(self, action_data, callback=None, ui_show_cb=None, user_action=True, profile=C.PROF_KEY_NONE): 1076 def actionManager(
1077 self,
1078 action_data,
1079 callback=None,
1080 ui_show_cb=None,
1081 user_action=True,
1082 profile=C.PROF_KEY_NONE,
1083 ):
876 """Handle backend action 1084 """Handle backend action
877 1085
878 @param action_data(dict): action dict as sent by launchAction or returned by an UI action 1086 @param action_data(dict): action dict as sent by launchAction or returned by an UI action
879 @param callback(None, callback): if not None, callback to use on XMLUI answer 1087 @param callback(None, callback): if not None, callback to use on XMLUI answer
880 @param ui_show_cb(None, callback): if not None, method to call to show the XMLUI 1088 @param ui_show_cb(None, callback): if not None, method to call to show the XMLUI
881 @param user_action(bool): if True, the action is a result of a user interaction 1089 @param user_action(bool): if True, the action is a result of a user interaction
882 else the action come from backend direclty (i.e. actionNew) 1090 else the action come from backend direclty (i.e. actionNew)
883 """ 1091 """
884 try: 1092 try:
885 xmlui = action_data.pop('xmlui') 1093 xmlui = action_data.pop("xmlui")
886 except KeyError: 1094 except KeyError:
887 pass 1095 pass
888 else: 1096 else:
889 ui = self.xmlui.create(self, xml_data=xmlui, flags=("FROM_BACKEND",) if not user_action else None, callback=callback, profile=profile) 1097 ui = self.xmlui.create(
1098 self,
1099 xml_data=xmlui,
1100 flags=("FROM_BACKEND",) if not user_action else None,
1101 callback=callback,
1102 profile=profile,
1103 )
890 if ui_show_cb is None: 1104 if ui_show_cb is None:
891 ui.show() 1105 ui.show()
892 else: 1106 else:
893 ui_show_cb(ui) 1107 ui_show_cb(ui)
894 1108
895 try: 1109 try:
896 progress_id = action_data.pop('progress') 1110 progress_id = action_data.pop("progress")
897 except KeyError: 1111 except KeyError:
898 pass 1112 pass
899 else: 1113 else:
900 self.progressIdHandler(progress_id, profile) 1114 self.progressIdHandler(progress_id, profile)
901 1115
902 # we ignore metadata 1116 # we ignore metadata
903 action_data = {k:v for k,v in action_data.iteritems() if not k.startswith("meta_")} 1117 action_data = {
1118 k: v for k, v in action_data.iteritems() if not k.startswith("meta_")
1119 }
904 1120
905 if action_data: 1121 if action_data:
906 raise exceptions.DataError(u"Not all keys in action_data are managed ({keys})".format(keys=', '.join(action_data.keys()))) 1122 raise exceptions.DataError(
907 1123 u"Not all keys in action_data are managed ({keys})".format(
1124 keys=", ".join(action_data.keys())
1125 )
1126 )
908 1127
909 def _actionCb(self, data, callback, callback_id, profile): 1128 def _actionCb(self, data, callback, callback_id, profile):
910 if callback is None: 1129 if callback is None:
911 self.actionManager(data, profile=profile) 1130 self.actionManager(data, profile=profile)
912 else: 1131 else:
913 callback(data=data, cb_id=callback_id, profile=profile) 1132 callback(data=data, cb_id=callback_id, profile=profile)
914 1133
915 def launchAction(self, callback_id, data=None, callback=None, profile=C.PROF_KEY_NONE): 1134 def launchAction(
1135 self, callback_id, data=None, callback=None, profile=C.PROF_KEY_NONE
1136 ):
916 """Launch a dynamic action 1137 """Launch a dynamic action
917 1138
918 @param callback_id: id of the action to launch 1139 @param callback_id: id of the action to launch
919 @param data: data needed only for certain actions 1140 @param data: data needed only for certain actions
920 @param callback(callable, None): will be called with the resut 1141 @param callback(callable, None): will be called with the resut
927 1148
928 """ 1149 """
929 if data is None: 1150 if data is None:
930 data = dict() 1151 data = dict()
931 action_cb = lambda data: self._actionCb(data, callback, callback_id, profile) 1152 action_cb = lambda data: self._actionCb(data, callback, callback_id, profile)
932 self.bridge.launchAction(callback_id, data, profile, callback=action_cb, errback=self.dialogFailure) 1153 self.bridge.launchAction(
933 1154 callback_id, data, profile, callback=action_cb, errback=self.dialogFailure
934 def launchMenu(self, menu_type, path, data=None, callback=None, security_limit=C.SECURITY_LIMIT_MAX, profile=C.PROF_KEY_NONE): 1155 )
1156
1157 def launchMenu(
1158 self,
1159 menu_type,
1160 path,
1161 data=None,
1162 callback=None,
1163 security_limit=C.SECURITY_LIMIT_MAX,
1164 profile=C.PROF_KEY_NONE,
1165 ):
935 """Launch a menu manually 1166 """Launch a menu manually
936 1167
937 @param menu_type(unicode): type of the menu to launch 1168 @param menu_type(unicode): type of the menu to launch
938 @param path(iterable[unicode]): path to the menu 1169 @param path(iterable[unicode]): path to the menu
939 @param data: data needed only for certain actions 1170 @param data: data needed only for certain actions
946 @param profile: %(doc_profile)s 1177 @param profile: %(doc_profile)s
947 1178
948 """ 1179 """
949 if data is None: 1180 if data is None:
950 data = dict() 1181 data = dict()
951 action_cb = lambda data: self._actionCb(data, callback, (menu_type, path), profile) 1182 action_cb = lambda data: self._actionCb(
952 self.bridge.menuLaunch(menu_type, path, data, security_limit, profile, callback=action_cb, errback=self.dialogFailure) 1183 data, callback, (menu_type, path), profile
1184 )
1185 self.bridge.menuLaunch(
1186 menu_type,
1187 path,
1188 data,
1189 security_limit,
1190 profile,
1191 callback=action_cb,
1192 errback=self.dialogFailure,
1193 )
953 1194
954 def _avatarGetCb(self, avatar_path, entity, contact_list, profile): 1195 def _avatarGetCb(self, avatar_path, entity, contact_list, profile):
955 path = avatar_path or self.getDefaultAvatar(entity) 1196 path = avatar_path or self.getDefaultAvatar(entity)
956 contact_list.setCache(entity, "avatar", path) 1197 contact_list.setCache(entity, "avatar", path)
957 self.callListeners('avatar', entity, path, profile=profile) 1198 self.callListeners("avatar", entity, path, profile=profile)
958 1199
959 def _avatarGetEb(self, failure, entity, contact_list): 1200 def _avatarGetEb(self, failure, entity, contact_list):
960 log.warning(u"Can't get avatar: {}".format(failure)) 1201 log.warning(u"Can't get avatar: {}".format(failure))
961 contact_list.setCache(entity, "avatar", self.getDefaultAvatar(entity)) 1202 contact_list.setCache(entity, "avatar", self.getDefaultAvatar(entity))
962 1203
963 def getAvatar(self, entity, cache_only=True, hash_only=False, ignore_cache=False, profile=C.PROF_KEY_NONE): 1204 def getAvatar(
1205 self,
1206 entity,
1207 cache_only=True,
1208 hash_only=False,
1209 ignore_cache=False,
1210 profile=C.PROF_KEY_NONE,
1211 ):
964 """return avatar path for an entity 1212 """return avatar path for an entity
965 1213
966 @param entity(jid.JID): entity to get avatar from 1214 @param entity(jid.JID): entity to get avatar from
967 @param cache_only(bool): if False avatar will be requested if not in cache 1215 @param cache_only(bool): if False avatar will be requested if not in cache
968 with current vCard based implementation, it's better to keep True 1216 with current vCard based implementation, it's better to keep True
980 self.bridge.avatarGet( 1228 self.bridge.avatarGet(
981 unicode(entity), 1229 unicode(entity),
982 cache_only, 1230 cache_only,
983 hash_only, 1231 hash_only,
984 profile=profile, 1232 profile=profile,
985 callback=lambda path: self._avatarGetCb(path, entity, contact_list, profile), 1233 callback=lambda path: self._avatarGetCb(
986 errback=lambda failure: self._avatarGetEb(failure, entity, contact_list)) 1234 path, entity, contact_list, profile
1235 ),
1236 errback=lambda failure: self._avatarGetEb(failure, entity, contact_list),
1237 )
987 # we set avatar to empty string to avoid requesting several time the same avatar 1238 # we set avatar to empty string to avoid requesting several time the same avatar
988 # while we are waiting for avatarGet result 1239 # while we are waiting for avatarGet result
989 contact_list.setCache(entity, "avatar", "") 1240 contact_list.setCache(entity, "avatar", "")
990 return avatar 1241 return avatar
991 1242
997 """ 1248 """
998 raise NotImplementedError 1249 raise NotImplementedError
999 1250
1000 def disconnect(self, profile): 1251 def disconnect(self, profile):
1001 log.info("disconnecting") 1252 log.info("disconnecting")
1002 self.callListeners('disconnect', profile=profile) 1253 self.callListeners("disconnect", profile=profile)
1003 self.bridge.disconnect(profile) 1254 self.bridge.disconnect(profile)
1004 1255
1005 def onExit(self): 1256 def onExit(self):
1006 """Must be called when the frontend is terminating""" 1257 """Must be called when the frontend is terminating"""
1007 to_unplug = [] 1258 to_unplug = []
1008 for profile, profile_manager in self.profiles.iteritems(): 1259 for profile, profile_manager in self.profiles.iteritems():
1009 if profile_manager.connected and profile_manager.autodisconnect: 1260 if profile_manager.connected and profile_manager.autodisconnect:
1010 #The user wants autodisconnection 1261 # The user wants autodisconnection
1011 self.disconnect(profile) 1262 self.disconnect(profile)
1012 to_unplug.append(profile) 1263 to_unplug.append(profile)
1013 for profile in to_unplug: 1264 for profile in to_unplug:
1014 self.unplug_profile(profile) 1265 self.unplug_profile(profile)