Mercurial > libervia-backend
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) |