comparison sat_frontends/quick_frontend/quick_app.py @ 4037:524856bd7b19

massive refactoring to switch from camelCase to snake_case: historically, Libervia (SàT before) was using camelCase as allowed by PEP8 when using a pre-PEP8 code, to use the same coding style as in Twisted. However, snake_case is more readable and it's better to follow PEP8 best practices, so it has been decided to move on full snake_case. Because Libervia has a huge codebase, this ended with a ugly mix of camelCase and snake_case. To fix that, this patch does a big refactoring by renaming every function and method (including bridge) that are not coming from Twisted or Wokkel, to use fully snake_case. This is a massive change, and may result in some bugs.
author Goffi <goffi@goffi.org>
date Sat, 08 Apr 2023 13:54:42 +0200
parents 509f7a1c67dc
children 2594e1951cf7
comparison
equal deleted inserted replaced
4036:c4464d7ae97b 4037:524856bd7b19
61 return autodisconnect 61 return autodisconnect
62 62
63 def plug(self): 63 def plug(self):
64 """Plug the profile to the host""" 64 """Plug the profile to the host"""
65 # first of all we create the contact lists 65 # first of all we create the contact lists
66 self.host.contact_lists.addProfile(self.profile) 66 self.host.contact_lists.add_profile(self.profile)
67 67
68 # we get the essential params 68 # we get the essential params
69 self.bridge.asyncGetParamA( 69 self.bridge.param_get_a_async(
70 "JabberID", 70 "JabberID",
71 "Connection", 71 "Connection",
72 profile_key=self.profile, 72 profile_key=self.profile,
73 callback=self._plug_profile_jid, 73 callback=self._plug_profile_jid,
74 errback=self._getParamError, 74 errback=self._get_param_error,
75 ) 75 )
76 76
77 def _plug_profile_jid(self, jid_s): 77 def _plug_profile_jid(self, jid_s):
78 self.whoami = jid.JID(jid_s) # resource might change after the connection 78 self.whoami = jid.JID(jid_s) # resource might change after the connection
79 log.info(f"Our current jid is: {self.whoami}") 79 log.info(f"Our current jid is: {self.whoami}")
80 self.bridge.isConnected(self.profile, callback=self._plug_profile_isconnected) 80 self.bridge.is_connected(self.profile, callback=self._plug_profile_isconnected)
81 81
82 def _autodisconnectEb(self, failure_): 82 def _autodisconnect_eb(self, failure_):
83 # XXX: we ignore error on this parameter, as Libervia can't access it 83 # XXX: we ignore error on this parameter, as Libervia can't access it
84 log.warning( 84 log.warning(
85 _("Error while trying to get autodisconnect param, ignoring: {}").format( 85 _("Error while trying to get autodisconnect param, ignoring: {}").format(
86 failure_ 86 failure_
87 ) 87 )
89 self._plug_profile_autodisconnect("false") 89 self._plug_profile_autodisconnect("false")
90 90
91 def _plug_profile_isconnected(self, connected): 91 def _plug_profile_isconnected(self, connected):
92 self.connected = connected 92 self.connected = connected
93 if connected: 93 if connected:
94 self.host.profileConnected(self.profile) 94 self.host.profile_connected(self.profile)
95 self.bridge.asyncGetParamA( 95 self.bridge.param_get_a_async(
96 "autodisconnect", 96 "autodisconnect",
97 "Connection", 97 "Connection",
98 profile_key=self.profile, 98 profile_key=self.profile,
99 callback=self._plug_profile_autodisconnect, 99 callback=self._plug_profile_autodisconnect,
100 errback=self._autodisconnectEb, 100 errback=self._autodisconnect_eb,
101 ) 101 )
102 102
103 def _plug_profile_autodisconnect(self, autodisconnect): 103 def _plug_profile_autodisconnect(self, autodisconnect):
104 if C.bool(autodisconnect): 104 if C.bool(autodisconnect):
105 self._autodisconnect = True 105 self._autodisconnect = True
106 self.bridge.asyncGetParamA( 106 self.bridge.param_get_a_async(
107 "autoconnect", 107 "autoconnect",
108 "Connection", 108 "Connection",
109 profile_key=self.profile, 109 profile_key=self.profile,
110 callback=self._plug_profile_autoconnect, 110 callback=self._plug_profile_autoconnect,
111 errback=self._getParamError, 111 errback=self._get_param_error,
112 ) 112 )
113 113
114 def _plug_profile_autoconnect(self, value_str): 114 def _plug_profile_autoconnect(self, value_str):
115 autoconnect = C.bool(value_str) 115 autoconnect = C.bool(value_str)
116 if autoconnect and not self.connected: 116 if autoconnect and not self.connected:
122 122
123 def _plug_profile_afterconnect(self): 123 def _plug_profile_afterconnect(self):
124 # Profile can be connected or not 124 # Profile can be connected or not
125 # we get cached data 125 # we get cached data
126 self.connected = True 126 self.connected = True
127 self.host.bridge.getFeatures( 127 self.host.bridge.features_get(
128 profile_key=self.profile, 128 profile_key=self.profile,
129 callback=self._plug_profile_getFeaturesCb, 129 callback=self._plug_profile_get_features_cb,
130 errback=self._plug_profile_getFeaturesEb, 130 errback=self._plug_profile_get_features_eb,
131 ) 131 )
132 132
133 def _plug_profile_getFeaturesEb(self, failure): 133 def _plug_profile_get_features_eb(self, failure):
134 log.error("Couldn't get features: {}".format(failure)) 134 log.error("Couldn't get features: {}".format(failure))
135 self._plug_profile_getFeaturesCb({}) 135 self._plug_profile_get_features_cb({})
136 136
137 def _plug_profile_getFeaturesCb(self, features): 137 def _plug_profile_get_features_cb(self, features):
138 self.host.features = features 138 self.host.features = features
139 self.host.bridge.getEntitiesData([], ProfileManager.cache_keys_to_get, 139 self.host.bridge.entities_data_get([], ProfileManager.cache_keys_to_get,
140 profile=self.profile, 140 profile=self.profile,
141 callback=self._plug_profile_gotCachedValues, 141 callback=self._plug_profile_got_cached_values,
142 errback=self._plug_profile_failedCachedValues) 142 errback=self._plug_profile_failed_cached_values)
143 143
144 def _plug_profile_failedCachedValues(self, failure): 144 def _plug_profile_failed_cached_values(self, failure):
145 log.error("Couldn't get cached values: {}".format(failure)) 145 log.error("Couldn't get cached values: {}".format(failure))
146 self._plug_profile_gotCachedValues({}) 146 self._plug_profile_got_cached_values({})
147 147
148 def _plug_profile_gotCachedValues(self, cached_values): 148 def _plug_profile_got_cached_values(self, cached_values):
149 contact_list = self.host.contact_lists[self.profile] 149 contact_list = self.host.contact_lists[self.profile]
150 # add the contact list and its listener 150 # add the contact list and its listener
151 for entity_s, data in cached_values.items(): 151 for entity_s, data in cached_values.items():
152 for key, value in data.items(): 152 for key, value in data.items():
153 self.host.entityDataUpdatedHandler(entity_s, key, value, self.profile) 153 self.host.entity_data_updated_handler(entity_s, key, value, self.profile)
154 154
155 if not self.connected: 155 if not self.connected:
156 self.host.setPresenceStatus(C.PRESENCE_UNAVAILABLE, "", profile=self.profile) 156 self.host.set_presence_status(C.PRESENCE_UNAVAILABLE, "", profile=self.profile)
157 else: 157 else:
158 158
159 contact_list.fill() 159 contact_list.fill()
160 self.host.setPresenceStatus(profile=self.profile) 160 self.host.set_presence_status(profile=self.profile)
161 161
162 # The waiting subscription requests 162 # The waiting subscription requests
163 self.bridge.getWaitingSub( 163 self.bridge.sub_waiting_get(
164 self.profile, callback=self._plug_profile_gotWaitingSub 164 self.profile, callback=self._plug_profile_got_waiting_sub
165 ) 165 )
166 166
167 def _plug_profile_gotWaitingSub(self, waiting_sub): 167 def _plug_profile_got_waiting_sub(self, waiting_sub):
168 for sub in waiting_sub: 168 for sub in waiting_sub:
169 self.host.subscribeHandler(waiting_sub[sub], sub, self.profile) 169 self.host.subscribe_handler(waiting_sub[sub], sub, self.profile)
170 170
171 self.bridge.mucGetRoomsJoined( 171 self.bridge.muc_get_rooms_joined(
172 self.profile, callback=self._plug_profile_gotRoomsJoined 172 self.profile, callback=self._plug_profile_got_rooms_joined
173 ) 173 )
174 174
175 def _plug_profile_gotRoomsJoined(self, rooms_args): 175 def _plug_profile_got_rooms_joined(self, rooms_args):
176 # Now we open the MUC window where we already are: 176 # Now we open the MUC window where we already are:
177 for room_args in rooms_args: 177 for room_args in rooms_args:
178 self.host.mucRoomJoinedHandler(*room_args, profile=self.profile) 178 self.host.muc_room_joined_handler(*room_args, profile=self.profile)
179 # Presence must be requested after rooms are filled 179 # Presence must be requested after rooms are filled
180 self.host.bridge.getPresenceStatuses( 180 self.host.bridge.presence_statuses_get(
181 self.profile, callback=self._plug_profile_gotPresences 181 self.profile, callback=self._plug_profile_got_presences
182 ) 182 )
183 183
184 def _plug_profile_gotPresences(self, presences): 184 def _plug_profile_got_presences(self, presences):
185 for contact in presences: 185 for contact in presences:
186 for res in presences[contact]: 186 for res in presences[contact]:
187 jabber_id = ("%s/%s" % (jid.JID(contact).bare, res)) if res else contact 187 jabber_id = ("%s/%s" % (jid.JID(contact).bare, res)) if res else contact
188 show = presences[contact][res][0] 188 show = presences[contact][res][0]
189 priority = presences[contact][res][1] 189 priority = presences[contact][res][1]
190 statuses = presences[contact][res][2] 190 statuses = presences[contact][res][2]
191 self.host.presenceUpdateHandler( 191 self.host.presence_update_handler(
192 jabber_id, show, priority, statuses, self.profile 192 jabber_id, show, priority, statuses, self.profile
193 ) 193 )
194 194
195 # At this point, profile should be fully plugged 195 # At this point, profile should be fully plugged
196 # and we launch frontend specific method 196 # and we launch frontend specific method
197 self.host.profilePlugged(self.profile) 197 self.host.profile_plugged(self.profile)
198 198
199 def _getParamError(self, failure): 199 def _get_param_error(self, failure):
200 log.error(_("Can't get profile parameter: {msg}").format(msg=failure)) 200 log.error(_("Can't get profile parameter: {msg}").format(msg=failure))
201 201
202 202
203 class ProfilesManager(object): 203 class ProfilesManager(object):
204 """Class managing collection of profiles""" 204 """Class managing collection of profiles"""
240 host = self._profiles[profile].host 240 host = self._profiles[profile].host
241 host.contact_lists[profile].unplug() 241 host.contact_lists[profile].unplug()
242 242
243 del self._profiles[profile] 243 del self._profiles[profile]
244 244
245 def chooseOneProfile(self): 245 def choose_one_profile(self):
246 return list(self._profiles.keys())[0] 246 return list(self._profiles.keys())[0]
247 247
248 248
249 class QuickApp(object): 249 class QuickApp(object):
250 """This class contain the main methods needed for the frontend""" 250 """This class contain the main methods needed for the frontend"""
258 AUTO_RESYNC = True 258 AUTO_RESYNC = True
259 259
260 def __init__(self, bridge_factory, xmlui, check_options=None, connect_bridge=True): 260 def __init__(self, bridge_factory, xmlui, check_options=None, connect_bridge=True):
261 """Create a frontend application 261 """Create a frontend application
262 262
263 @param bridge_factory: method to use to create the Bridge 263 @param bridge_factory: method to use to create the bridge
264 @param xmlui: xmlui module 264 @param xmlui: xmlui module
265 @param check_options: method to call to check options (usually command line 265 @param check_options: method to call to check options (usually command line
266 arguments) 266 arguments)
267 """ 267 """
268 self.xmlui = xmlui 268 self.xmlui = xmlui
295 295
296 ## bridge ## 296 ## bridge ##
297 self.bridge = bridge_factory() 297 self.bridge = bridge_factory()
298 ProfileManager.bridge = self.bridge 298 ProfileManager.bridge = self.bridge
299 if connect_bridge: 299 if connect_bridge:
300 self.connectBridge() 300 self.connect_bridge()
301 301
302 # frontend notifications 302 # frontend notifications
303 self._notif_id = 0 303 self._notif_id = 0
304 self._notifications = {} 304 self._notifications = {}
305 # watched progresses and associated callbacks 305 # watched progresses and associated callbacks
312 #: available encryptions 312 #: available encryptions
313 self.encryption_plugins = [] 313 self.encryption_plugins = []
314 # state of synchronisation with backend 314 # state of synchronisation with backend
315 self._sync = True 315 self._sync = True
316 316
317 def connectBridge(self): 317 def connect_bridge(self):
318 self.bridge.bridgeConnect(callback=self._bridgeCb, errback=self._bridgeEb) 318 self.bridge.bridge_connect(callback=self._bridge_cb, errback=self._bridge_eb)
319 319
320 def _namespacesGetCb(self, ns_map): 320 def _namespaces_get_cb(self, ns_map):
321 self.ns_map = ns_map 321 self.ns_map = ns_map
322 322
323 def _namespacesGetEb(self, failure_): 323 def _namespaces_get_eb(self, failure_):
324 log.error(_("Can't get namespaces map: {msg}").format(msg=failure_)) 324 log.error(_("Can't get namespaces map: {msg}").format(msg=failure_))
325 325
326 def _encryptionPluginsGetCb(self, plugins_ser): 326 def _encryption_plugins_get_cb(self, plugins_ser):
327 self.encryption_plugins = data_format.deserialise(plugins_ser, type_check=list) 327 self.encryption_plugins = data_format.deserialise(plugins_ser, type_check=list)
328 328
329 def _encryptionPluginsGetEb(self, failure_): 329 def _encryption_plugins_get_eb(self, failure_):
330 log.warning(_("Can't retrieve encryption plugins: {msg}").format(msg=failure_)) 330 log.warning(_("Can't retrieve encryption plugins: {msg}").format(msg=failure_))
331 331
332 def onBridgeConnected(self): 332 def on_bridge_connected(self):
333 self.bridge.getReady(self.onBackendReady) 333 self.bridge.ready_get(self.on_backend_ready)
334 334
335 def _bridgeCb(self): 335 def _bridge_cb(self):
336 self.registerSignal("connected") 336 self.register_signal("connected")
337 self.registerSignal("disconnected") 337 self.register_signal("disconnected")
338 self.registerSignal("actionNew") 338 self.register_signal("action_new")
339 self.registerSignal("newContact") 339 self.register_signal("contact_new")
340 self.registerSignal("messageNew") 340 self.register_signal("message_new")
341 if self.ENCRYPTION_HANDLERS: 341 if self.ENCRYPTION_HANDLERS:
342 self.registerSignal("messageEncryptionStarted") 342 self.register_signal("message_encryption_started")
343 self.registerSignal("messageEncryptionStopped") 343 self.register_signal("message_encryption_stopped")
344 self.registerSignal("presenceUpdate") 344 self.register_signal("presence_update")
345 self.registerSignal("subscribe") 345 self.register_signal("subscribe")
346 self.registerSignal("paramUpdate") 346 self.register_signal("param_update")
347 self.registerSignal("contactDeleted") 347 self.register_signal("contact_deleted")
348 self.registerSignal("entityDataUpdated") 348 self.register_signal("entity_data_updated")
349 self.registerSignal("progressStarted") 349 self.register_signal("progress_started")
350 self.registerSignal("progressFinished") 350 self.register_signal("progress_finished")
351 self.registerSignal("progressError") 351 self.register_signal("progress_error")
352 self.registerSignal("mucRoomJoined", iface="plugin") 352 self.register_signal("muc_room_joined", iface="plugin")
353 self.registerSignal("mucRoomLeft", iface="plugin") 353 self.register_signal("muc_room_left", iface="plugin")
354 self.registerSignal("mucRoomUserChangedNick", iface="plugin") 354 self.register_signal("muc_room_user_changed_nick", iface="plugin")
355 self.registerSignal("mucRoomNewSubject", iface="plugin") 355 self.register_signal("muc_room_new_subject", iface="plugin")
356 self.registerSignal("chatStateReceived", iface="plugin") 356 self.register_signal("chat_state_received", iface="plugin")
357 self.registerSignal("messageState", iface="plugin") 357 self.register_signal("message_state", iface="plugin")
358 self.registerSignal("psEvent", iface="plugin") 358 self.register_signal("ps_event", iface="plugin")
359 # useful for debugging 359 # useful for debugging
360 self.registerSignal("_debug", iface="core") 360 self.register_signal("_debug", iface="core")
361 361
362 # FIXME: do it dynamically 362 # FIXME: do it dynamically
363 quick_games.Tarot.registerSignals(self) 363 quick_games.Tarot.register_signals(self)
364 quick_games.Quiz.registerSignals(self) 364 quick_games.Quiz.register_signals(self)
365 quick_games.Radiocol.registerSignals(self) 365 quick_games.Radiocol.register_signals(self)
366 self.onBridgeConnected() 366 self.on_bridge_connected()
367 367
368 def _bridgeEb(self, failure): 368 def _bridge_eb(self, failure):
369 if isinstance(failure, exceptions.BridgeExceptionNoService): 369 if isinstance(failure, exceptions.BridgeExceptionNoService):
370 print((_("Can't connect to SàT backend, are you sure it's launched ?"))) 370 print((_("Can't connect to SàT backend, are you sure it's launched ?")))
371 sys.exit(C.EXIT_BACKEND_NOT_FOUND) 371 sys.exit(C.EXIT_BACKEND_NOT_FOUND)
372 elif isinstance(failure, exceptions.BridgeInitError): 372 elif isinstance(failure, exceptions.BridgeInitError):
373 print((_("Can't init bridge"))) 373 print((_("Can't init bridge")))
374 sys.exit(C.EXIT_BRIDGE_ERROR) 374 sys.exit(C.EXIT_BRIDGE_ERROR)
375 else: 375 else:
376 print((_("Error while initialising bridge: {}".format(failure)))) 376 print((_("Error while initialising bridge: {}".format(failure))))
377 377
378 def onBackendReady(self): 378 def on_backend_ready(self):
379 log.info("backend is ready") 379 log.info("backend is ready")
380 self.bridge.namespacesGet( 380 self.bridge.namespaces_get(
381 callback=self._namespacesGetCb, errback=self._namespacesGetEb) 381 callback=self._namespaces_get_cb, errback=self._namespaces_get_eb)
382 # we cache available encryption plugins, as we'll use them on each 382 # we cache available encryption plugins, as we'll use them on each
383 # new chat widget 383 # new chat widget
384 self.bridge.encryptionPluginsGet( 384 self.bridge.encryption_plugins_get(
385 callback=self._encryptionPluginsGetCb, 385 callback=self._encryption_plugins_get_cb,
386 errback=self._encryptionPluginsGetEb) 386 errback=self._encryption_plugins_get_eb)
387 387
388 388
389 @property 389 @property
390 def current_profile(self): 390 def current_profile(self):
391 """Profile that a user would expect to use""" 391 """Profile that a user would expect to use"""
392 try: 392 try:
393 return self.selected_widget.profile 393 return self.selected_widget.profile
394 except (TypeError, AttributeError): 394 except (TypeError, AttributeError):
395 return self.profiles.chooseOneProfile() 395 return self.profiles.choose_one_profile()
396 396
397 @property 397 @property
398 def visible_widgets(self): 398 def visible_widgets(self):
399 """Widgets currently visible 399 """Widgets currently visible
400 400
432 """ 432 """
433 if self._selected_widget == wid: 433 if self._selected_widget == wid:
434 return 434 return
435 self._selected_widget = wid 435 self._selected_widget = wid
436 try: 436 try:
437 onSelected = wid.onSelected 437 on_selected = wid.on_selected
438 except AttributeError: 438 except AttributeError:
439 pass 439 pass
440 else: 440 else:
441 onSelected() 441 on_selected()
442 442
443 self.callListeners("selected", wid) 443 self.call_listeners("selected", wid)
444 444
445 # backend state management 445 # backend state management
446 446
447 @property 447 @property
448 def sync(self): 448 def sync(self):
484 try: 484 try:
485 w.sync = False 485 w.sync = False
486 except AttributeError: 486 except AttributeError:
487 pass 487 pass
488 488
489 def registerSignal( 489 def register_signal(
490 self, function_name, handler=None, iface="core", with_profile=True 490 self, function_name, handler=None, iface="core", with_profile=True
491 ): 491 ):
492 """Register a handler for a signal 492 """Register a handler for a signal
493 493
494 @param function_name (str): name of the signal to handle 494 @param function_name (str): name of the signal to handle
498 @param with_profile (boolean): True if the signal concerns a specific profile, 498 @param with_profile (boolean): True if the signal concerns a specific profile,
499 in that case the profile name has to be passed by the caller 499 in that case the profile name has to be passed by the caller
500 """ 500 """
501 log.debug("registering signal {name}".format(name=function_name)) 501 log.debug("registering signal {name}".format(name=function_name))
502 if handler is None: 502 if handler is None:
503 handler = getattr(self, "{}{}".format(function_name, "Handler")) 503 handler = getattr(self, "{}{}".format(function_name, "_handler"))
504 if not with_profile: 504 if not with_profile:
505 self.bridge.register_signal(function_name, handler, iface) 505 self.bridge.register_signal(function_name, handler, iface)
506 return 506 return
507 507
508 def signalReceived(*args, **kwargs): 508 def signal_received(*args, **kwargs):
509 profile = kwargs.get("profile") 509 profile = kwargs.get("profile")
510 if profile is None: 510 if profile is None:
511 if not args: 511 if not args:
512 raise exceptions.ProfileNotSetError 512 raise exceptions.ProfileNotSetError
513 profile = args[-1] 513 profile = args[-1]
520 (function_name, handler, args, kwargs) 520 (function_name, handler, args, kwargs)
521 ) 521 )
522 return # we ignore signal for profiles we don't manage 522 return # we ignore signal for profiles we don't manage
523 handler(*args, **kwargs) 523 handler(*args, **kwargs)
524 524
525 self.bridge.register_signal(function_name, signalReceived, iface) 525 self.bridge.register_signal(function_name, signal_received, iface)
526 526
527 def addListener(self, type_, callback, profiles_filter=None): 527 def addListener(self, type_, callback, profiles_filter=None):
528 """Add a listener for an event 528 """Add a listener for an event
529 529
530 /!\ don't forget to remove listener when not used anymore (e.g. if you delete a 530 /!\ don't forget to remove listener when not used anymore (e.g. if you delete a
549 - widgetDeleted: all instances of a widget with specific hash have been 549 - widgetDeleted: all instances of a widget with specific hash have been
550 deleted 550 deleted
551 args: (widget_deleted,) 551 args: (widget_deleted,)
552 - menu: called when a menu item is added or removed 552 - menu: called when a menu item is added or removed
553 args: (type_, path, path_i18n, item) were values are: 553 args: (type_, path, path_i18n, item) were values are:
554 type_: same as in [sat.core.sat_main.SAT.importMenu] 554 type_: same as in [sat.core.sat_main.SAT.import_menu]
555 path: same as in [sat.core.sat_main.SAT.importMenu] 555 path: same as in [sat.core.sat_main.SAT.import_menu]
556 path_i18n: translated path (or None if the item is removed) 556 path_i18n: translated path (or None if the item is removed)
557 item: instance of quick_menus.MenuItemBase or None if the item is 557 item: instance of quick_menus.MenuItemBase or None if the item is
558 removed 558 removed
559 - gotMenus: called only once when menu are available (no arg) 559 - gotMenus: called only once when menu are available (no arg)
560 - progressFinished: called when a progressing action has just finished 560 - progress_finished: called when a progressing action has just finished
561 args: (progress_id, metadata, profile) 561 args: (progress_id, metadata, profile)
562 - progressError: called when a progressing action failed 562 - progress_error: called when a progressing action failed
563 args: (progress_id, error_msg, profile): 563 args: (progress_id, error_msg, profile):
564 @param callback: method to call on event 564 @param callback: method to call on event
565 @param profiles_filter (set[unicode]): if set and not empty, the 565 @param profiles_filter (set[unicode]): if set and not empty, the
566 listener will be callable only by one of the given profiles. 566 listener will be callable only by one of the given profiles.
567 """ 567 """
583 if not ignore_missing: 583 if not ignore_missing:
584 log.error( 584 log.error(
585 f"Trying to remove an inexisting listener (type = {type_}): " 585 f"Trying to remove an inexisting listener (type = {type_}): "
586 f"{callback}") 586 f"{callback}")
587 587
588 def callListeners(self, type_, *args, **kwargs): 588 def call_listeners(self, type_, *args, **kwargs):
589 """Call the methods which listen type_ event. If a profiles filter has 589 """Call the methods which listen type_ event. If a profiles filter has
590 been register with a listener and profile argument is not None, the 590 been register with a listener and profile argument is not None, the
591 listener will be called only if profile is in the profiles filter list. 591 listener will be called only if profile is in the profiles filter list.
592 592
593 @param type_: same as for [addListener] 593 @param type_: same as for [addListener]
607 607
608 def check_profile(self, profile): 608 def check_profile(self, profile):
609 """Tell if the profile is currently followed by the application, and ready""" 609 """Tell if the profile is currently followed by the application, and ready"""
610 return profile in self.ready_profiles 610 return profile in self.ready_profiles
611 611
612 def postInit(self, profile_manager): 612 def post_init(self, profile_manager):
613 """Must be called after initialization is done, do all automatic task 613 """Must be called after initialization is done, do all automatic task
614 614
615 (auto plug profile) 615 (auto plug profile)
616 @param profile_manager: instance of a subclass of 616 @param profile_manager: instance of a subclass of
617 Quick_frontend.QuickProfileManager 617 Quick_frontend.QuickProfileManager
618 """ 618 """
619 if self.options and self.options.profile: 619 if self.options and self.options.profile:
620 profile_manager.autoconnect([self.options.profile]) 620 profile_manager.autoconnect([self.options.profile])
621 621
622 def profilePlugged(self, profile): 622 def profile_plugged(self, profile):
623 """Method called when the profile is fully plugged 623 """Method called when the profile is fully plugged
624 624
625 This will launch frontend specific workflow 625 This will launch frontend specific workflow
626 626
627 /!\ if you override the method and don't call the parent, be sure to add the 627 /!\ if you override the method and don't call the parent, be sure to add the
639 "Calling cached signal [%s] with args %s and kwargs %s" 639 "Calling cached signal [%s] with args %s and kwargs %s"
640 % (function_name, args, kwargs) 640 % (function_name, args, kwargs)
641 ) 641 )
642 handler(*args, **kwargs) 642 handler(*args, **kwargs)
643 643
644 self.callListeners("profilePlugged", profile=profile) 644 self.call_listeners("profile_plugged", profile=profile)
645 if not self._plugs_in_progress: 645 if not self._plugs_in_progress:
646 self.contact_lists.lockUpdate(False) 646 self.contact_lists.lock_update(False)
647 647
648 def profileConnected(self, profile): 648 def profile_connected(self, profile):
649 """Called when a plugged profile is connected 649 """Called when a plugged profile is connected
650 650
651 it is called independently of profilePlugged (may be called before or after 651 it is called independently of profile_plugged (may be called before or after
652 profilePlugged) 652 profile_plugged)
653 """ 653 """
654 pass 654 pass
655 655
656 def connect(self, profile, callback=None, errback=None): 656 def connect(self, profile, callback=None, errback=None):
657 if not callback: 657 if not callback:
674 fullname = "error" 674 fullname = "error"
675 if ( 675 if (
676 module.startswith("twisted.words.protocols.jabber") 676 module.startswith("twisted.words.protocols.jabber")
677 and failure.condition == "not-authorized" 677 and failure.condition == "not-authorized"
678 ): 678 ):
679 self.launchAction(C.CHANGE_XMPP_PASSWD_ID, {}, profile=profile) 679 self.action_launch(C.CHANGE_XMPP_PASSWD_ID, {}, profile=profile)
680 else: 680 else:
681 self.showDialog(message, fullname, "error") 681 self.show_dialog(message, fullname, "error")
682 682
683 self.bridge.connect(profile, callback=callback, errback=errback) 683 self.bridge.connect(profile, callback=callback, errback=errback)
684 684
685 def plug_profiles(self, profiles): 685 def plug_profiles(self, profiles):
686 """Tell application which profiles must be used 686 """Tell application which profiles must be used
687 687
688 @param profiles: list of valid profile names 688 @param profiles: list of valid profile names
689 """ 689 """
690 self.contact_lists.lockUpdate() 690 self.contact_lists.lock_update()
691 self._plugs_in_progress.update(profiles) 691 self._plugs_in_progress.update(profiles)
692 self.plugging_profiles() 692 self.plugging_profiles()
693 for profile in profiles: 693 for profile in profiles:
694 self.profiles.plug(profile) 694 self.profiles.plug(profile)
695 695
707 self.profiles.unplug(profile) 707 self.profiles.unplug(profile)
708 708
709 def clear_profile(self): 709 def clear_profile(self):
710 self.profiles.clear() 710 self.profiles.clear()
711 711
712 def newWidget(self, widget): 712 def new_widget(self, widget):
713 raise NotImplementedError 713 raise NotImplementedError
714 714
715 # bridge signals hanlers 715 # bridge signals hanlers
716 716
717 def connectedHandler(self, jid_s, profile): 717 def connected_handler(self, jid_s, profile):
718 """Called when the connection is made. 718 """Called when the connection is made.
719 719
720 @param jid_s (unicode): the JID that we were assigned by the server, 720 @param jid_s (unicode): the JID that we were assigned by the server,
721 as the resource might differ from the JID we asked for. 721 as the resource might differ from the JID we asked for.
722 """ 722 """
723 log.debug(_("Connected")) 723 log.debug(_("Connected"))
724 self.profiles[profile].whoami = jid.JID(jid_s) 724 self.profiles[profile].whoami = jid.JID(jid_s)
725 self.setPresenceStatus(profile=profile) 725 self.set_presence_status(profile=profile)
726 # FIXME: fill() is already called for all profiles when doing self.sync = True 726 # FIXME: fill() is already called for all profiles when doing self.sync = True
727 # a per-profile fill() should be done once, see below note 727 # a per-profile fill() should be done once, see below note
728 self.contact_lists[profile].fill() 728 self.contact_lists[profile].fill()
729 # if we were already displaying widgets, they must be resynchronized 729 # if we were already displaying widgets, they must be resynchronized
730 # FIXME: self.sync is for all profiles 730 # FIXME: self.sync is for all profiles
731 # while (dis)connection is per-profile. 731 # while (dis)connection is per-profile.
732 # A mechanism similar to sync should be available 732 # A mechanism similar to sync should be available
733 # on a per-profile basis 733 # on a per-profile basis
734 self.sync = True 734 self.sync = True
735 self.profileConnected(profile) 735 self.profile_connected(profile)
736 736
737 def disconnectedHandler(self, profile): 737 def disconnected_handler(self, profile):
738 """called when the connection is closed""" 738 """called when the connection is closed"""
739 log.debug(_("Disconnected")) 739 log.debug(_("Disconnected"))
740 self.contact_lists[profile].disconnect() 740 self.contact_lists[profile].disconnect()
741 # FIXME: see note on connectedHandler 741 # FIXME: see note on connected_handler
742 self.sync = False 742 self.sync = False
743 self.setPresenceStatus(C.PRESENCE_UNAVAILABLE, "", profile=profile) 743 self.set_presence_status(C.PRESENCE_UNAVAILABLE, "", profile=profile)
744 744
745 def actionNewHandler(self, action_data, id_, security_limit, profile): 745 def action_new_handler(self, action_data, id_, security_limit, profile):
746 self.actionManager(action_data, user_action=False, profile=profile) 746 self.action_manager(action_data, user_action=False, profile=profile)
747 747
748 def newContactHandler(self, jid_s, attributes, groups, profile): 748 def contact_new_handler(self, jid_s, attributes, groups, profile):
749 entity = jid.JID(jid_s) 749 entity = jid.JID(jid_s)
750 groups = list(groups) 750 groups = list(groups)
751 self.contact_lists[profile].setContact(entity, groups, attributes, in_roster=True) 751 self.contact_lists[profile].set_contact(entity, groups, attributes, in_roster=True)
752 752
753 def messageNewHandler( 753 def message_new_handler(
754 self, uid, timestamp, from_jid_s, to_jid_s, msg, subject, type_, extra_s, 754 self, uid, timestamp, from_jid_s, to_jid_s, msg, subject, type_, extra_s,
755 profile): 755 profile):
756 from_jid = jid.JID(from_jid_s) 756 from_jid = jid.JID(from_jid_s)
757 to_jid = jid.JID(to_jid_s) 757 to_jid = jid.JID(to_jid_s)
758 extra = data_format.deserialise(extra_s) 758 extra = data_format.deserialise(extra_s)
765 mess_to_jid = to_jid if from_me else from_jid 765 mess_to_jid = to_jid if from_me else from_jid
766 target = mess_to_jid.bare 766 target = mess_to_jid.bare
767 contact_list = self.contact_lists[profile] 767 contact_list = self.contact_lists[profile]
768 768
769 try: 769 try:
770 is_room = contact_list.isRoom(target) 770 is_room = contact_list.is_room(target)
771 except exceptions.NotFound: 771 except exceptions.NotFound:
772 is_room = False 772 is_room = False
773 773
774 if target.resource and not is_room: 774 if target.resource and not is_room:
775 # we avoid resource locking, but we must keep resource for private MUC 775 # we avoid resource locking, but we must keep resource for private MUC
776 # messages 776 # messages
777 target = target 777 target = target
778 # we want to be sure to have at least one QuickChat instance 778 # we want to be sure to have at least one QuickChat instance
779 self.widgets.getOrCreateWidget( 779 self.widgets.get_or_create_widget(
780 quick_chat.QuickChat, 780 quick_chat.QuickChat,
781 target, 781 target,
782 type_ = C.CHAT_GROUP if is_room else C.CHAT_ONE2ONE, 782 type_ = C.CHAT_GROUP if is_room else C.CHAT_ONE2ONE,
783 on_new_widget = None, 783 on_new_widget = None,
784 profile = profile, 784 profile = profile,
788 not from_jid in contact_list 788 not from_jid in contact_list
789 and from_jid.bare != self.profiles[profile].whoami.bare 789 and from_jid.bare != self.profiles[profile].whoami.bare
790 ): 790 ):
791 # XXX: needed to show entities which haven't sent any 791 # XXX: needed to show entities which haven't sent any
792 # presence information and which are not in roster 792 # presence information and which are not in roster
793 contact_list.setContact(from_jid) 793 contact_list.set_contact(from_jid)
794 794
795 # we dispatch the message in the widgets 795 # we dispatch the message in the widgets
796 for widget in self.widgets.getWidgets( 796 for widget in self.widgets.get_widgets(
797 quick_chat.QuickChat, target=target, profiles=(profile,) 797 quick_chat.QuickChat, target=target, profiles=(profile,)
798 ): 798 ):
799 widget.messageNew( 799 widget.message_new(
800 uid, timestamp, from_jid, mess_to_jid, msg, subject, type_, extra, profile 800 uid, timestamp, from_jid, mess_to_jid, msg, subject, type_, extra, profile
801 ) 801 )
802 802
803 def messageEncryptionStartedHandler(self, destinee_jid_s, plugin_data, profile): 803 def message_encryption_started_handler(self, destinee_jid_s, plugin_data, profile):
804 destinee_jid = jid.JID(destinee_jid_s) 804 destinee_jid = jid.JID(destinee_jid_s)
805 plugin_data = data_format.deserialise(plugin_data) 805 plugin_data = data_format.deserialise(plugin_data)
806 for widget in self.widgets.getWidgets(quick_chat.QuickChat, 806 for widget in self.widgets.get_widgets(quick_chat.QuickChat,
807 target=destinee_jid.bare, 807 target=destinee_jid.bare,
808 profiles=(profile,)): 808 profiles=(profile,)):
809 widget.messageEncryptionStarted(plugin_data) 809 widget.message_encryption_started(plugin_data)
810 810
811 def messageEncryptionStoppedHandler(self, destinee_jid_s, plugin_data, profile): 811 def message_encryption_stopped_handler(self, destinee_jid_s, plugin_data, profile):
812 destinee_jid = jid.JID(destinee_jid_s) 812 destinee_jid = jid.JID(destinee_jid_s)
813 for widget in self.widgets.getWidgets(quick_chat.QuickChat, 813 for widget in self.widgets.get_widgets(quick_chat.QuickChat,
814 target=destinee_jid.bare, 814 target=destinee_jid.bare,
815 profiles=(profile,)): 815 profiles=(profile,)):
816 widget.messageEncryptionStopped(plugin_data) 816 widget.message_encryption_stopped(plugin_data)
817 817
818 def messageStateHandler(self, uid, status, profile): 818 def message_state_handler(self, uid, status, profile):
819 for widget in self.widgets.getWidgets(quick_chat.QuickChat, profiles=(profile,)): 819 for widget in self.widgets.get_widgets(quick_chat.QuickChat, profiles=(profile,)):
820 widget.onMessageState(uid, status, profile) 820 widget.on_message_state(uid, status, profile)
821 821
822 def messageSend(self, to_jid, message, subject=None, mess_type="auto", extra=None, callback=None, errback=None, profile_key=C.PROF_KEY_NONE): 822 def message_send(self, to_jid, message, subject=None, mess_type="auto", extra=None, callback=None, errback=None, profile_key=C.PROF_KEY_NONE):
823 if not subject and not extra and (not message or message == {'': ''}): 823 if not subject and not extra and (not message or message == {'': ''}):
824 log.debug("Not sending empty message") 824 log.debug("Not sending empty message")
825 return 825 return
826 826
827 if subject is None: 827 if subject is None:
832 callback = ( 832 callback = (
833 lambda __=None: None 833 lambda __=None: None
834 ) # FIXME: optional argument is here because pyjamas doesn't support callback 834 ) # FIXME: optional argument is here because pyjamas doesn't support callback
835 # without arg with json proxy 835 # without arg with json proxy
836 if errback is None: 836 if errback is None:
837 errback = lambda failure: self.showDialog( 837 errback = lambda failure: self.show_dialog(
838 message=failure.message, title=failure.fullname, type="error" 838 message=failure.message, title=failure.fullname, type="error"
839 ) 839 )
840 840
841 if not self.trigger.point("messageSendTrigger", to_jid, message, subject, mess_type, extra, callback, errback, profile_key=profile_key): 841 if not self.trigger.point("messageSendTrigger", to_jid, message, subject, mess_type, extra, callback, errback, profile_key=profile_key):
842 return 842 return
843 843
844 self.bridge.messageSend( 844 self.bridge.message_send(
845 str(to_jid), 845 str(to_jid),
846 message, 846 message,
847 subject, 847 subject,
848 mess_type, 848 mess_type,
849 data_format.serialise(extra), 849 data_format.serialise(extra),
850 profile_key, 850 profile_key,
851 callback=callback, 851 callback=callback,
852 errback=errback, 852 errback=errback,
853 ) 853 )
854 854
855 def setPresenceStatus(self, show="", status=None, profile=C.PROF_KEY_NONE): 855 def set_presence_status(self, show="", status=None, profile=C.PROF_KEY_NONE):
856 raise NotImplementedError 856 raise NotImplementedError
857 857
858 def presenceUpdateHandler(self, entity_s, show, priority, statuses, profile): 858 def presence_update_handler(self, entity_s, show, priority, statuses, profile):
859 # XXX: this log is commented because it's really too verbose even for DEBUG logs 859 # XXX: this log is commented because it's really too verbose even for DEBUG logs
860 # but it is kept here as it may still be useful for troubleshooting 860 # but it is kept here as it may still be useful for troubleshooting
861 # log.debug( 861 # log.debug(
862 # _( 862 # _(
863 # u"presence update for %(entity)s (show=%(show)s, priority=%(priority)s, " 863 # u"presence update for %(entity)s (show=%(show)s, priority=%(priority)s, "
873 # ) 873 # )
874 entity = jid.JID(entity_s) 874 entity = jid.JID(entity_s)
875 875
876 if entity == self.profiles[profile].whoami: 876 if entity == self.profiles[profile].whoami:
877 if show == C.PRESENCE_UNAVAILABLE: 877 if show == C.PRESENCE_UNAVAILABLE:
878 self.setPresenceStatus(C.PRESENCE_UNAVAILABLE, "", profile=profile) 878 self.set_presence_status(C.PRESENCE_UNAVAILABLE, "", profile=profile)
879 else: 879 else:
880 # FIXME: try to retrieve user language status before fallback to default 880 # FIXME: try to retrieve user language status before fallback to default
881 status = statuses.get(C.PRESENCE_STATUSES_DEFAULT, None) 881 status = statuses.get(C.PRESENCE_STATUSES_DEFAULT, None)
882 self.setPresenceStatus(show, status, profile=profile) 882 self.set_presence_status(show, status, profile=profile)
883 return 883 return
884 884
885 self.callListeners("presence", entity, show, priority, statuses, profile=profile) 885 self.call_listeners("presence", entity, show, priority, statuses, profile=profile)
886 886
887 def mucRoomJoinedHandler( 887 def muc_room_joined_handler(
888 self, room_jid_s, occupants, user_nick, subject, statuses, profile): 888 self, room_jid_s, occupants, user_nick, subject, statuses, profile):
889 """Called when a MUC room is joined""" 889 """Called when a MUC room is joined"""
890 log.debug( 890 log.debug(
891 "Room [{room_jid}] joined by {profile}, users presents:{users}".format( 891 "Room [{room_jid}] joined by {profile}, users presents:{users}".format(
892 room_jid=room_jid_s, profile=profile, users=list(occupants.keys()) 892 room_jid=room_jid_s, profile=profile, users=list(occupants.keys())
893 ) 893 )
894 ) 894 )
895 room_jid = jid.JID(room_jid_s) 895 room_jid = jid.JID(room_jid_s)
896 self.contact_lists[profile].setSpecial(room_jid, C.CONTACT_SPECIAL_GROUP) 896 self.contact_lists[profile].set_special(room_jid, C.CONTACT_SPECIAL_GROUP)
897 self.widgets.getOrCreateWidget( 897 self.widgets.get_or_create_widget(
898 quick_chat.QuickChat, 898 quick_chat.QuickChat,
899 room_jid, 899 room_jid,
900 type_=C.CHAT_GROUP, 900 type_=C.CHAT_GROUP,
901 nick=user_nick, 901 nick=user_nick,
902 occupants=occupants, 902 occupants=occupants,
903 subject=subject, 903 subject=subject,
904 statuses=statuses, 904 statuses=statuses,
905 profile=profile, 905 profile=profile,
906 ) 906 )
907 907
908 def mucRoomLeftHandler(self, room_jid_s, profile): 908 def muc_room_left_handler(self, room_jid_s, profile):
909 """Called when a MUC room is left""" 909 """Called when a MUC room is left"""
910 log.debug( 910 log.debug(
911 "Room [%(room_jid)s] left by %(profile)s" 911 "Room [%(room_jid)s] left by %(profile)s"
912 % {"room_jid": room_jid_s, "profile": profile} 912 % {"room_jid": room_jid_s, "profile": profile}
913 ) 913 )
914 room_jid = jid.JID(room_jid_s) 914 room_jid = jid.JID(room_jid_s)
915 chat_widget = self.widgets.getWidget(quick_chat.QuickChat, room_jid, profile) 915 chat_widget = self.widgets.get_widget(quick_chat.QuickChat, room_jid, profile)
916 if chat_widget: 916 if chat_widget:
917 self.widgets.deleteWidget( 917 self.widgets.delete_widget(
918 chat_widget, all_instances=True, explicit_close=True) 918 chat_widget, all_instances=True, explicit_close=True)
919 self.contact_lists[profile].removeContact(room_jid) 919 self.contact_lists[profile].remove_contact(room_jid)
920 920
921 def mucRoomUserChangedNickHandler(self, room_jid_s, old_nick, new_nick, profile): 921 def muc_room_user_changed_nick_handler(self, room_jid_s, old_nick, new_nick, profile):
922 """Called when an user joined a MUC room""" 922 """Called when an user joined a MUC room"""
923 room_jid = jid.JID(room_jid_s) 923 room_jid = jid.JID(room_jid_s)
924 chat_widget = self.widgets.getOrCreateWidget( 924 chat_widget = self.widgets.get_or_create_widget(
925 quick_chat.QuickChat, room_jid, type_=C.CHAT_GROUP, profile=profile 925 quick_chat.QuickChat, room_jid, type_=C.CHAT_GROUP, profile=profile
926 ) 926 )
927 chat_widget.changeUserNick(old_nick, new_nick) 927 chat_widget.change_user_nick(old_nick, new_nick)
928 log.debug( 928 log.debug(
929 "user [%(old_nick)s] is now known as [%(new_nick)s] in room [%(room_jid)s]" 929 "user [%(old_nick)s] is now known as [%(new_nick)s] in room [%(room_jid)s]"
930 % {"old_nick": old_nick, "new_nick": new_nick, "room_jid": room_jid} 930 % {"old_nick": old_nick, "new_nick": new_nick, "room_jid": room_jid}
931 ) 931 )
932 932
933 def mucRoomNewSubjectHandler(self, room_jid_s, subject, profile): 933 def muc_room_new_subject_handler(self, room_jid_s, subject, profile):
934 """Called when subject of MUC room change""" 934 """Called when subject of MUC room change"""
935 room_jid = jid.JID(room_jid_s) 935 room_jid = jid.JID(room_jid_s)
936 chat_widget = self.widgets.getOrCreateWidget( 936 chat_widget = self.widgets.get_or_create_widget(
937 quick_chat.QuickChat, room_jid, type_=C.CHAT_GROUP, profile=profile 937 quick_chat.QuickChat, room_jid, type_=C.CHAT_GROUP, profile=profile
938 ) 938 )
939 chat_widget.setSubject(subject) 939 chat_widget.set_subject(subject)
940 log.debug( 940 log.debug(
941 "new subject for room [%(room_jid)s]: %(subject)s" 941 "new subject for room [%(room_jid)s]: %(subject)s"
942 % {"room_jid": room_jid, "subject": subject} 942 % {"room_jid": room_jid, "subject": subject}
943 ) 943 )
944 944
945 def chatStateReceivedHandler(self, from_jid_s, state, profile): 945 def chat_state_received_handler(self, from_jid_s, state, profile):
946 """Called when a new chat state (XEP-0085) is received. 946 """Called when a new chat state (XEP-0085) is received.
947 947
948 @param from_jid_s (unicode): JID of a contact or C.ENTITY_ALL 948 @param from_jid_s (unicode): JID of a contact or C.ENTITY_ALL
949 @param state (unicode): new state 949 @param state (unicode): new state
950 @param profile (unicode): current profile 950 @param profile (unicode): current profile
951 """ 951 """
952 from_jid = jid.JID(from_jid_s) 952 from_jid = jid.JID(from_jid_s)
953 for widget in self.widgets.getWidgets(quick_chat.QuickChat, target=from_jid.bare, 953 for widget in self.widgets.get_widgets(quick_chat.QuickChat, target=from_jid.bare,
954 profiles=(profile,)): 954 profiles=(profile,)):
955 widget.onChatState(from_jid, state, profile) 955 widget.on_chat_state(from_jid, state, profile)
956 956
957 def notify(self, type_, entity=None, message=None, subject=None, callback=None, 957 def notify(self, type_, entity=None, message=None, subject=None, callback=None,
958 cb_args=None, widget=None, profile=C.PROF_KEY_NONE): 958 cb_args=None, widget=None, profile=C.PROF_KEY_NONE):
959 """Trigger an event notification 959 """Trigger an event notification
960 960
984 if widget is not None: 984 if widget is not None:
985 notif_data[widget] = widget 985 notif_data[widget] = widget
986 type_notifs.append(notif_data) 986 type_notifs.append(notif_data)
987 self._notifications[self._notif_id] = notif_data 987 self._notifications[self._notif_id] = notif_data
988 self._notif_id += 1 988 self._notif_id += 1
989 self.callListeners("notification", entity, notif_data, profile=profile) 989 self.call_listeners("notification", entity, notif_data, profile=profile)
990 990
991 def getNotifs(self, entity=None, type_=None, exact_jid=None, profile=C.PROF_KEY_NONE): 991 def get_notifs(self, entity=None, type_=None, exact_jid=None, profile=C.PROF_KEY_NONE):
992 """return notifications for given entity 992 """return notifications for given entity
993 993
994 @param entity(jid.JID, None, C.ENTITY_ALL): jid of the entity to check 994 @param entity(jid.JID, None, C.ENTITY_ALL): jid of the entity to check
995 bare jid to get all notifications, full jid to filter on resource 995 bare jid to get all notifications, full jid to filter on resource
996 None to get general notifications 996 None to get general notifications
1030 for notif in notifs: 1030 for notif in notifs:
1031 if exact_jid and notif["entity"] != entity: 1031 if exact_jid and notif["entity"] != entity:
1032 continue 1032 continue
1033 yield notif 1033 yield notif
1034 1034
1035 def clearNotifs(self, entity, type_=None, profile=C.PROF_KEY_NONE): 1035 def clear_notifs(self, entity, type_=None, profile=C.PROF_KEY_NONE):
1036 """return notifications for given entity 1036 """return notifications for given entity
1037 1037
1038 @param entity(jid.JID, None): bare jid of the entity to check 1038 @param entity(jid.JID, None): bare jid of the entity to check
1039 None to clear general notifications (but keep entities ones) 1039 None to clear general notifications (but keep entities ones)
1040 @param type_(unicode, None): notification type to filter 1040 @param type_(unicode, None): notification type to filter
1048 del notif_dict[key] 1048 del notif_dict[key]
1049 else: 1049 else:
1050 del notif_dict[key][type_] 1050 del notif_dict[key][type_]
1051 except KeyError: 1051 except KeyError:
1052 return 1052 return
1053 self.callListeners("notificationsClear", entity, type_, profile=profile) 1053 self.call_listeners("notificationsClear", entity, type_, profile=profile)
1054 1054
1055 def psEventHandler(self, category, service_s, node, event_type, data, profile): 1055 def ps_event_handler(self, category, service_s, node, event_type, data, profile):
1056 """Called when a PubSub event is received. 1056 """Called when a PubSub event is received.
1057 1057
1058 @param category(unicode): event category (e.g. "PEP", "MICROBLOG") 1058 @param category(unicode): event category (e.g. "PEP", "MICROBLOG")
1059 @param service_s (unicode): pubsub service 1059 @param service_s (unicode): pubsub service
1060 @param node (unicode): pubsub node 1060 @param node (unicode): pubsub node
1071 return 1071 return
1072 1072
1073 # FIXME: check if [] make sense (instead of None) 1073 # FIXME: check if [] make sense (instead of None)
1074 _groups = data.get("group") 1074 _groups = data.get("group")
1075 1075
1076 for wid in self.widgets.getWidgets(quick_blog.QuickBlog): 1076 for wid in self.widgets.get_widgets(quick_blog.QuickBlog):
1077 wid.addEntryIfAccepted(service_s, node, data, _groups, profile) 1077 wid.add_entry_if_accepted(service_s, node, data, _groups, profile)
1078 1078
1079 try: 1079 try:
1080 comments_node, comments_service = ( 1080 comments_node, comments_service = (
1081 data["comments_node"], 1081 data["comments_node"],
1082 data["comments_service"], 1082 data["comments_service"],
1083 ) 1083 )
1084 except KeyError: 1084 except KeyError:
1085 pass 1085 pass
1086 else: 1086 else:
1087 self.bridge.mbGet( 1087 self.bridge.mb_get(
1088 comments_service, 1088 comments_service,
1089 comments_node, 1089 comments_node,
1090 C.NO_LIMIT, 1090 C.NO_LIMIT,
1091 [], 1091 [],
1092 {"subscribe": C.BOOL_TRUE}, 1092 {"subscribe": C.BOOL_TRUE},
1093 profile=profile, 1093 profile=profile,
1094 ) 1094 )
1095 elif event_type == C.PS_RETRACT: 1095 elif event_type == C.PS_RETRACT:
1096 for wid in self.widgets.getWidgets(quick_blog.QuickBlog): 1096 for wid in self.widgets.get_widgets(quick_blog.QuickBlog):
1097 wid.deleteEntryIfPresent(service_s, node, data["id"], profile) 1097 wid.delete_entry_if_present(service_s, node, data["id"], profile)
1098 pass 1098 pass
1099 else: 1099 else:
1100 log.warning("Unmanaged PubSub event type {}".format(event_type)) 1100 log.warning("Unmanaged PubSub event type {}".format(event_type))
1101 1101
1102 def registerProgressCbs(self, progress_id, callback, errback): 1102 def register_progress_cbs(self, progress_id, callback, errback):
1103 """Register progression callbacks 1103 """Register progression callbacks
1104 1104
1105 @param progress_id(unicode): id of the progression to check 1105 @param progress_id(unicode): id of the progression to check
1106 @param callback(callable, None): method to call when progressing action 1106 @param callback(callable, None): method to call when progressing action
1107 successfuly finished. 1107 successfuly finished.
1110 None to ignore 1110 None to ignore
1111 """ 1111 """
1112 callbacks = self._progress_ids.setdefault(progress_id, []) 1112 callbacks = self._progress_ids.setdefault(progress_id, [])
1113 callbacks.append((callback, errback)) 1113 callbacks.append((callback, errback))
1114 1114
1115 def progressStartedHandler(self, pid, metadata, profile): 1115 def progress_started_handler(self, pid, metadata, profile):
1116 log.info("Progress {} started".format(pid)) 1116 log.info("Progress {} started".format(pid))
1117 1117
1118 def progressFinishedHandler(self, pid, metadata, profile): 1118 def progress_finished_handler(self, pid, metadata, profile):
1119 log.info("Progress {} finished".format(pid)) 1119 log.info("Progress {} finished".format(pid))
1120 try: 1120 try:
1121 callbacks = self._progress_ids.pop(pid) 1121 callbacks = self._progress_ids.pop(pid)
1122 except KeyError: 1122 except KeyError:
1123 pass 1123 pass
1124 else: 1124 else:
1125 for callback, __ in callbacks: 1125 for callback, __ in callbacks:
1126 if callback is not None: 1126 if callback is not None:
1127 callback(metadata, profile=profile) 1127 callback(metadata, profile=profile)
1128 self.callListeners("progressFinished", pid, metadata, profile=profile) 1128 self.call_listeners("progress_finished", pid, metadata, profile=profile)
1129 1129
1130 def progressErrorHandler(self, pid, err_msg, profile): 1130 def progress_error_handler(self, pid, err_msg, profile):
1131 log.warning("Progress {pid} error: {err_msg}".format(pid=pid, err_msg=err_msg)) 1131 log.warning("Progress {pid} error: {err_msg}".format(pid=pid, err_msg=err_msg))
1132 try: 1132 try:
1133 callbacks = self._progress_ids.pop(pid) 1133 callbacks = self._progress_ids.pop(pid)
1134 except KeyError: 1134 except KeyError:
1135 pass 1135 pass
1136 else: 1136 else:
1137 for __, errback in callbacks: 1137 for __, errback in callbacks:
1138 if errback is not None: 1138 if errback is not None:
1139 errback(err_msg, profile=profile) 1139 errback(err_msg, profile=profile)
1140 self.callListeners("progressError", pid, err_msg, profile=profile) 1140 self.call_listeners("progress_error", pid, err_msg, profile=profile)
1141 1141
1142 def _subscribe_cb(self, answer, data): 1142 def _subscribe_cb(self, answer, data):
1143 entity, profile = data 1143 entity, profile = data
1144 type_ = "subscribed" if answer else "unsubscribed" 1144 type_ = "subscribed" if answer else "unsubscribed"
1145 self.bridge.subscription(type_, str(entity.bare), profile_key=profile) 1145 self.bridge.subscription(type_, str(entity.bare), profile_key=profile)
1146 1146
1147 def subscribeHandler(self, type, raw_jid, profile): 1147 def subscribe_handler(self, type, raw_jid, profile):
1148 """Called when a subsciption management signal is received""" 1148 """Called when a subsciption management signal is received"""
1149 entity = jid.JID(raw_jid) 1149 entity = jid.JID(raw_jid)
1150 if type == "subscribed": 1150 if type == "subscribed":
1151 # this is a subscription confirmation, we just have to inform user 1151 # this is a subscription confirmation, we just have to inform user
1152 # TODO: call self.getEntityMBlog to add the new contact blogs 1152 # TODO: call self.getEntityMBlog to add the new contact blogs
1153 self.showDialog( 1153 self.show_dialog(
1154 _("The contact {contact} has accepted your subscription").format( 1154 _("The contact {contact} has accepted your subscription").format(
1155 contact=entity.bare 1155 contact=entity.bare
1156 ), 1156 ),
1157 _("Subscription confirmation"), 1157 _("Subscription confirmation"),
1158 ) 1158 )
1159 elif type == "unsubscribed": 1159 elif type == "unsubscribed":
1160 # this is a subscription refusal, we just have to inform user 1160 # this is a subscription refusal, we just have to inform user
1161 self.showDialog( 1161 self.show_dialog(
1162 _("The contact {contact} has refused your subscription").format( 1162 _("The contact {contact} has refused your subscription").format(
1163 contact=entity.bare 1163 contact=entity.bare
1164 ), 1164 ),
1165 _("Subscription refusal"), 1165 _("Subscription refusal"),
1166 "error", 1166 "error",
1167 ) 1167 )
1168 elif type == "subscribe": 1168 elif type == "subscribe":
1169 # this is a subscriptionn request, we have to ask for user confirmation 1169 # this is a subscriptionn request, we have to ask for user confirmation
1170 # TODO: use sat.stdui.ui_contact_list to display the groups selector 1170 # TODO: use sat.stdui.ui_contact_list to display the groups selector
1171 self.showDialog( 1171 self.show_dialog(
1172 _( 1172 _(
1173 "The contact {contact} wants to subscribe to your presence" 1173 "The contact {contact} wants to subscribe to your presence"
1174 ".\nDo you accept ?" 1174 ".\nDo you accept ?"
1175 ).format(contact=entity.bare), 1175 ).format(contact=entity.bare),
1176 _("Subscription confirmation"), 1176 _("Subscription confirmation"),
1177 "yes/no", 1177 "yes/no",
1178 answer_cb=self._subscribe_cb, 1178 answer_cb=self._subscribe_cb,
1179 answer_data=(entity, profile), 1179 answer_data=(entity, profile),
1180 ) 1180 )
1181 1181
1182 def _debugHandler(self, action, parameters, profile): 1182 def _debug_handler(self, action, parameters, profile):
1183 if action == "widgets_dump": 1183 if action == "widgets_dump":
1184 from pprint import pformat 1184 from pprint import pformat
1185 log.info("Widgets dump:\n{data}".format(data=pformat(self.widgets._widgets))) 1185 log.info("Widgets dump:\n{data}".format(data=pformat(self.widgets._widgets)))
1186 else: 1186 else:
1187 log.warning("Unknown debug action: {action}".format(action=action)) 1187 log.warning("Unknown debug action: {action}".format(action=action))
1188 1188
1189 1189
1190 def showDialog(self, message, title, type="info", answer_cb=None, answer_data=None): 1190 def show_dialog(self, message, title, type="info", answer_cb=None, answer_data=None):
1191 """Show a dialog to user 1191 """Show a dialog to user
1192 1192
1193 Frontends must override this method 1193 Frontends must override this method
1194 @param message(unicode): body of the dialog 1194 @param message(unicode): body of the dialog
1195 @param title(unicode): title of the dialog 1195 @param title(unicode): title of the dialog
1204 @param answer_data(object): data to link on callback 1204 @param answer_data(object): data to link on callback
1205 """ 1205 """
1206 # FIXME: misnamed method + types are not well chosen. Need to be rethought 1206 # FIXME: misnamed method + types are not well chosen. Need to be rethought
1207 raise NotImplementedError 1207 raise NotImplementedError
1208 1208
1209 def showAlert(self, message): 1209 def show_alert(self, message):
1210 # FIXME: doesn't seems used anymore, to remove? 1210 # FIXME: doesn't seems used anymore, to remove?
1211 pass # FIXME 1211 pass # FIXME
1212 1212
1213 def dialogFailure(self, failure): 1213 def dialog_failure(self, failure):
1214 log.warning("Failure: {}".format(failure)) 1214 log.warning("Failure: {}".format(failure))
1215 1215
1216 def progressIdHandler(self, progress_id, profile): 1216 def progress_id_handler(self, progress_id, profile):
1217 """Callback used when an action result in a progress id""" 1217 """Callback used when an action result in a progress id"""
1218 log.info("Progress ID received: {}".format(progress_id)) 1218 log.info("Progress ID received: {}".format(progress_id))
1219 1219
1220 def isHidden(self): 1220 def is_hidden(self):
1221 """Tells if the frontend window is hidden. 1221 """Tells if the frontend window is hidden.
1222 1222
1223 @return bool 1223 @return bool
1224 """ 1224 """
1225 raise NotImplementedError 1225 raise NotImplementedError
1226 1226
1227 def paramUpdateHandler(self, name, value, namespace, profile): 1227 def param_update_handler(self, name, value, namespace, profile):
1228 log.debug( 1228 log.debug(
1229 _("param update: [%(namespace)s] %(name)s = %(value)s") 1229 _("param update: [%(namespace)s] %(name)s = %(value)s")
1230 % {"namespace": namespace, "name": name, "value": value} 1230 % {"namespace": namespace, "name": name, "value": value}
1231 ) 1231 )
1232 if (namespace, name) == ("Connection", "JabberID"): 1232 if (namespace, name) == ("Connection", "JabberID"):
1233 log.debug(_("Changing JID to %s") % value) 1233 log.debug(_("Changing JID to %s") % value)
1234 self.profiles[profile].whoami = jid.JID(value) 1234 self.profiles[profile].whoami = jid.JID(value)
1235 elif (namespace, name) == ("General", C.SHOW_OFFLINE_CONTACTS): 1235 elif (namespace, name) == ("General", C.SHOW_OFFLINE_CONTACTS):
1236 self.contact_lists[profile].showOfflineContacts(C.bool(value)) 1236 self.contact_lists[profile].show_offline_contacts(C.bool(value))
1237 elif (namespace, name) == ("General", C.SHOW_EMPTY_GROUPS): 1237 elif (namespace, name) == ("General", C.SHOW_EMPTY_GROUPS):
1238 self.contact_lists[profile].showEmptyGroups(C.bool(value)) 1238 self.contact_lists[profile].show_empty_groups(C.bool(value))
1239 1239
1240 def contactDeletedHandler(self, jid_s, profile): 1240 def contact_deleted_handler(self, jid_s, profile):
1241 target = jid.JID(jid_s) 1241 target = jid.JID(jid_s)
1242 self.contact_lists[profile].removeContact(target) 1242 self.contact_lists[profile].remove_contact(target)
1243 1243
1244 def entityDataUpdatedHandler(self, entity_s, key, value_raw, profile): 1244 def entity_data_updated_handler(self, entity_s, key, value_raw, profile):
1245 entity = jid.JID(entity_s) 1245 entity = jid.JID(entity_s)
1246 value = data_format.deserialise(value_raw, type_check=None) 1246 value = data_format.deserialise(value_raw, type_check=None)
1247 if key == "nicknames": 1247 if key == "nicknames":
1248 assert isinstance(value, list) or value is None 1248 assert isinstance(value, list) or value is None
1249 if entity in self.contact_lists[profile]: 1249 if entity in self.contact_lists[profile]:
1250 self.contact_lists[profile].setCache(entity, "nicknames", value) 1250 self.contact_lists[profile].set_cache(entity, "nicknames", value)
1251 self.callListeners("nicknames", entity, value, profile=profile) 1251 self.call_listeners("nicknames", entity, value, profile=profile)
1252 elif key == "avatar" and self.AVATARS_HANDLER: 1252 elif key == "avatar" and self.AVATARS_HANDLER:
1253 assert isinstance(value, dict) or value is None 1253 assert isinstance(value, dict) or value is None
1254 self.contact_lists[profile].setCache(entity, "avatar", value) 1254 self.contact_lists[profile].set_cache(entity, "avatar", value)
1255 self.callListeners("avatar", entity, value, profile=profile) 1255 self.call_listeners("avatar", entity, value, profile=profile)
1256 1256
1257 def actionManager(self, action_data, callback=None, ui_show_cb=None, user_action=True, 1257 def action_manager(self, action_data, callback=None, ui_show_cb=None, user_action=True,
1258 progress_cb=None, progress_eb=None, profile=C.PROF_KEY_NONE): 1258 progress_cb=None, progress_eb=None, profile=C.PROF_KEY_NONE):
1259 """Handle backend action 1259 """Handle backend action
1260 1260
1261 @param action_data(dict): action dict as sent by launchAction or returned by an 1261 @param action_data(dict): action dict as sent by action_launch or returned by an
1262 UI action 1262 UI action
1263 @param callback(None, callback): if not None, callback to use on XMLUI answer 1263 @param callback(None, callback): if not None, callback to use on XMLUI answer
1264 @param ui_show_cb(None, callback): if not None, method to call to show the XMLUI 1264 @param ui_show_cb(None, callback): if not None, method to call to show the XMLUI
1265 @param user_action(bool): if True, the action is a result of a user interaction 1265 @param user_action(bool): if True, the action is a result of a user interaction
1266 else the action come from backend direclty (i.e. actionNew). 1266 else the action come from backend direclty (i.e. action_new).
1267 This is useful to know if the frontend can display a popup immediately (if 1267 This is useful to know if the frontend can display a popup immediately (if
1268 True) or if it should add it to a queue that the user can activate later. 1268 True) or if it should add it to a queue that the user can activate later.
1269 @param progress_cb(None, callable): method to call when progression is finished. 1269 @param progress_cb(None, callable): method to call when progression is finished.
1270 Only make sense if a progress is expected in this action 1270 Only make sense if a progress is expected in this action
1271 @param progress_eb(None, callable): method to call when something went wrong 1271 @param progress_eb(None, callable): method to call when something went wrong
1293 progress_id = action_data.pop("progress") 1293 progress_id = action_data.pop("progress")
1294 except KeyError: 1294 except KeyError:
1295 pass 1295 pass
1296 else: 1296 else:
1297 if progress_cb or progress_eb: 1297 if progress_cb or progress_eb:
1298 self.registerProgressCbs(progress_id, progress_cb, progress_eb) 1298 self.register_progress_cbs(progress_id, progress_cb, progress_eb)
1299 self.progressIdHandler(progress_id, profile) 1299 self.progress_id_handler(progress_id, profile)
1300 1300
1301 # we ignore metadata 1301 # we ignore metadata
1302 action_data = { 1302 action_data = {
1303 k: v for k, v in action_data.items() if not k.startswith("meta_") 1303 k: v for k, v in action_data.items() if not k.startswith("meta_")
1304 } 1304 }
1308 "Not all keys in action_data are managed ({keys})".format( 1308 "Not all keys in action_data are managed ({keys})".format(
1309 keys=", ".join(list(action_data.keys())) 1309 keys=", ".join(list(action_data.keys()))
1310 ) 1310 )
1311 ) 1311 )
1312 1312
1313 def _actionCb(self, data, callback, callback_id, profile): 1313 def _action_cb(self, data, callback, callback_id, profile):
1314 if callback is None: 1314 if callback is None:
1315 self.actionManager(data, profile=profile) 1315 self.action_manager(data, profile=profile)
1316 else: 1316 else:
1317 callback(data=data, cb_id=callback_id, profile=profile) 1317 callback(data=data, cb_id=callback_id, profile=profile)
1318 1318
1319 def launchAction( 1319 def action_launch(
1320 self, callback_id, data=None, callback=None, profile=C.PROF_KEY_NONE 1320 self, callback_id, data=None, callback=None, profile=C.PROF_KEY_NONE
1321 ): 1321 ):
1322 """Launch a dynamic action 1322 """Launch a dynamic action
1323 1323
1324 @param callback_id: id of the action to launch 1324 @param callback_id: id of the action to launch
1325 @param data: data needed only for certain actions 1325 @param data: data needed only for certain actions
1326 @param callback(callable, None): will be called with the resut 1326 @param callback(callable, None): will be called with the resut
1327 if None, self.actionManager will be called 1327 if None, self.action_manager will be called
1328 else the callable will be called with the following kw parameters: 1328 else the callable will be called with the following kw parameters:
1329 - data: action_data 1329 - data: action_data
1330 - cb_id: callback id 1330 - cb_id: callback id
1331 - profile: %(doc_profile)s 1331 - profile: %(doc_profile)s
1332 @param profile: %(doc_profile)s 1332 @param profile: %(doc_profile)s
1333 1333
1334 """ 1334 """
1335 if data is None: 1335 if data is None:
1336 data = dict() 1336 data = dict()
1337 action_cb = lambda data: self._actionCb(data, callback, callback_id, profile) 1337 action_cb = lambda data: self._action_cb(data, callback, callback_id, profile)
1338 self.bridge.launchAction( 1338 self.bridge.action_launch(
1339 callback_id, data, profile, callback=action_cb, errback=self.dialogFailure 1339 callback_id, data, profile, callback=action_cb, errback=self.dialog_failure
1340 ) 1340 )
1341 1341
1342 def launchMenu( 1342 def launch_menu(
1343 self, 1343 self,
1344 menu_type, 1344 menu_type,
1345 path, 1345 path,
1346 data=None, 1346 data=None,
1347 callback=None, 1347 callback=None,
1352 1352
1353 @param menu_type(unicode): type of the menu to launch 1353 @param menu_type(unicode): type of the menu to launch
1354 @param path(iterable[unicode]): path to the menu 1354 @param path(iterable[unicode]): path to the menu
1355 @param data: data needed only for certain actions 1355 @param data: data needed only for certain actions
1356 @param callback(callable, None): will be called with the resut 1356 @param callback(callable, None): will be called with the resut
1357 if None, self.actionManager will be called 1357 if None, self.action_manager will be called
1358 else the callable will be called with the following kw parameters: 1358 else the callable will be called with the following kw parameters:
1359 - data: action_data 1359 - data: action_data
1360 - cb_id: (menu_type, path) tuple 1360 - cb_id: (menu_type, path) tuple
1361 - profile: %(doc_profile)s 1361 - profile: %(doc_profile)s
1362 @param profile: %(doc_profile)s 1362 @param profile: %(doc_profile)s
1363 1363
1364 """ 1364 """
1365 if data is None: 1365 if data is None:
1366 data = dict() 1366 data = dict()
1367 action_cb = lambda data: self._actionCb( 1367 action_cb = lambda data: self._action_cb(
1368 data, callback, (menu_type, path), profile 1368 data, callback, (menu_type, path), profile
1369 ) 1369 )
1370 self.bridge.menuLaunch( 1370 self.bridge.menu_launch(
1371 menu_type, 1371 menu_type,
1372 path, 1372 path,
1373 data, 1373 data,
1374 security_limit, 1374 security_limit,
1375 profile, 1375 profile,
1376 callback=action_cb, 1376 callback=action_cb,
1377 errback=self.dialogFailure, 1377 errback=self.dialog_failure,
1378 ) 1378 )
1379 1379
1380 def disconnect(self, profile): 1380 def disconnect(self, profile):
1381 log.info("disconnecting") 1381 log.info("disconnecting")
1382 self.callListeners("disconnect", profile=profile) 1382 self.call_listeners("disconnect", profile=profile)
1383 self.bridge.disconnect(profile) 1383 self.bridge.disconnect(profile)
1384 1384
1385 def onExit(self): 1385 def on_exit(self):
1386 """Must be called when the frontend is terminating""" 1386 """Must be called when the frontend is terminating"""
1387 to_unplug = [] 1387 to_unplug = []
1388 for profile, profile_manager in self.profiles.items(): 1388 for profile, profile_manager in self.profiles.items():
1389 if profile_manager.connected and profile_manager.autodisconnect: 1389 if profile_manager.connected and profile_manager.autodisconnect:
1390 # The user wants autodisconnection 1390 # The user wants autodisconnection