Mercurial > libervia-backend
comparison src/sat.tac @ 331:0a8eb0461f31
core: main SAT class now moved in its own module core.sat_main
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 23 May 2011 21:32:28 +0200 |
parents | 608a4a2ba94e |
children | cf005701624b |
comparison
equal
deleted
inserted
replaced
330:608a4a2ba94e | 331:0a8eb0461f31 |
---|---|
17 | 17 |
18 You should have received a copy of the GNU General Public License | 18 You should have received a copy of the GNU General Public License |
19 along with this program. If not, see <http://www.gnu.org/licenses/>. | 19 along with this program. If not, see <http://www.gnu.org/licenses/>. |
20 """ | 20 """ |
21 | 21 |
22 CONST = { | |
23 'client_name' : u'SàT (Salut à toi)', | |
24 'client_version' : u'0.1.1D', #Please add 'D' at the end for dev versions | |
25 'local_dir' : '~/.sat' | |
26 } | |
27 | |
28 import gettext | 22 import gettext |
29 gettext.install('sat', "i18n", unicode=True) | 23 gettext.install('sat', "i18n", unicode=True) |
30 | 24 |
31 from twisted.application import internet, service | 25 from twisted.application import service |
32 from twisted.internet import glib2reactor, protocol, task, defer | 26 from twisted.internet import glib2reactor |
33 glib2reactor.install() | 27 glib2reactor.install() |
34 | 28 |
35 from twisted.words.protocols.jabber import jid, xmlstream | 29 from sat.core.sat_main import SAT |
36 from twisted.words.protocols.jabber import error as jab_error | |
37 from twisted.words.xish import domish | |
38 | |
39 from twisted.internet import reactor | |
40 import pdb | |
41 | |
42 from wokkel import client, disco, xmppim, generic, compat | |
43 | |
44 from sat.bridge.DBus import DBusBridge | |
45 import logging | |
46 from logging import debug, info, error | |
47 | |
48 import signal, sys | |
49 import os.path | |
50 | |
51 from sat.core.xmpp import SatXMPPClient, SatMessageProtocol, SatRosterProtocol, SatPresenceProtocol, SatDiscoProtocol, SatFallbackHandler, RegisteringAuthenticator, SatVersionHandler | |
52 from sat.tools.memory import Memory | |
53 from sat.tools.xml_tools import tupleList2dataForm | |
54 from sat.tools.misc import TriggerManager | |
55 from glob import glob | |
56 | |
57 try: | |
58 from twisted.words.protocols.xmlstream import XMPPHandler | |
59 except ImportError: | |
60 from wokkel.subprotocols import XMPPHandler | |
61 | |
62 | |
63 ### logging configuration FIXME: put this elsewhere ### | |
64 logging.basicConfig(level=logging.DEBUG, | |
65 format='%(message)s') | |
66 ### | |
67 | |
68 | |
69 sat_id = 0 | |
70 | |
71 def sat_next_id(): | |
72 global sat_id | |
73 sat_id+=1 | |
74 return "sat_id_"+str(sat_id) | |
75 | |
76 class SAT(service.Service): | |
77 | |
78 def get_next_id(self): | |
79 return sat_next_id() | |
80 | |
81 def get_const(self, name): | |
82 """Return a constant""" | |
83 if not CONST.has_key(name): | |
84 error(_('Trying to access an undefined constant')) | |
85 raise Exception | |
86 return CONST[name] | |
87 | |
88 def set_const(self, name, value): | |
89 """Save a constant""" | |
90 if CONST.has_key(name): | |
91 error(_('Trying to redefine a constant')) | |
92 raise Exception | |
93 CONST[name] = value | |
94 | |
95 def __init__(self): | |
96 #TODO: standardize callback system | |
97 | |
98 local_dir = os.path.expanduser(self.get_const('local_dir')) | |
99 if not os.path.exists(local_dir): | |
100 os.makedirs(local_dir) | |
101 | |
102 self.__waiting_conf = {} #callback called when a confirmation is received | |
103 self.__progress_cb_map = {} #callback called when a progress is requested (key = progress id) | |
104 self.__general_cb_map = {} #callback called for general reasons (key = name) | |
105 self.__private_data = {} #used for internal callbacks (key = id) | |
106 self.trigger = TriggerManager() #trigger are user to change SàT behaviour | |
107 self.profiles = {} | |
108 self.plugins = {} | |
109 self.menus = {} #used to know which new menus are wanted by plugins | |
110 | |
111 self.memory=Memory(self) | |
112 | |
113 self.bridge=DBusBridge() | |
114 self.bridge.register("getVersion", lambda: self.get_const('client_version')) | |
115 self.bridge.register("getProfileName", self.memory.getProfileName) | |
116 self.bridge.register("getProfilesList", self.memory.getProfilesList) | |
117 self.bridge.register("createProfile", self.memory.createProfile) | |
118 self.bridge.register("deleteProfile", self.memory.deleteProfile) | |
119 self.bridge.register("registerNewAccount", self.registerNewAccount) | |
120 self.bridge.register("connect", self.connect) | |
121 self.bridge.register("disconnect", self.disconnect) | |
122 self.bridge.register("getContacts", self.memory.getContacts) | |
123 self.bridge.register("getPresenceStatus", self.memory.getPresenceStatus) | |
124 self.bridge.register("getWaitingSub", self.memory.getWaitingSub) | |
125 self.bridge.register("sendMessage", self.sendMessage) | |
126 self.bridge.register("setParam", self.setParam) | |
127 self.bridge.register("getParamA", self.memory.getParamA) | |
128 self.bridge.register("getParamsUI", self.memory.getParamsUI) | |
129 self.bridge.register("getParams", self.memory.getParams) | |
130 self.bridge.register("getParamsForCategory", self.memory.getParamsForCategory) | |
131 self.bridge.register("getParamsCategories", self.memory.getParamsCategories) | |
132 self.bridge.register("getHistory", self.memory.getHistory) | |
133 self.bridge.register("setPresence", self.setPresence) | |
134 self.bridge.register("subscription", self.subscription) | |
135 self.bridge.register("addContact", self.addContact) | |
136 self.bridge.register("delContact", self.delContact) | |
137 self.bridge.register("isConnected", self.isConnected) | |
138 self.bridge.register("launchAction", self.launchAction) | |
139 self.bridge.register("confirmationAnswer", self.confirmationAnswer) | |
140 self.bridge.register("getProgress", self.getProgress) | |
141 self.bridge.register("getMenus", self.getMenus) | |
142 self.bridge.register("getMenuHelp", self.getMenuHelp) | |
143 self.bridge.register("callMenu", self.callMenu) | |
144 | |
145 self._import_plugins() | |
146 | |
147 | |
148 def _import_plugins(self): | |
149 """Import all plugins found in plugins directory""" | |
150 import sat.plugins | |
151 plugins_path = os.path.dirname(sat.plugins.__file__) | |
152 plug_lst = [os.path.splitext(plugin)[0] for plugin in map(os.path.basename,glob (os.path.join(plugins_path,"plugin*.py")))] | |
153 __plugins_to_import = {} #plugins will still have to import | |
154 for plug in plug_lst: | |
155 plugin_path = 'sat.plugins.'+plug | |
156 __import__(plugin_path) | |
157 mod = sys.modules[plugin_path] | |
158 plugin_info = mod.PLUGIN_INFO | |
159 __plugins_to_import[plugin_info['import_name']] = (plugin_path, mod, plugin_info) | |
160 while True: | |
161 self._import_plugins_from_dict(__plugins_to_import) | |
162 if not __plugins_to_import: | |
163 break | |
164 | |
165 def _import_plugins_from_dict(self, plugins_to_import, import_name=None): | |
166 """Recursively import and their dependencies in the right order | |
167 @param plugins_to_import: dict where key=import_name and values= (plugin_path, module, plugin_info)""" | |
168 if self.plugins.has_key(import_name): | |
169 debug('Plugin [%s] already imported, passing' % import_name) | |
170 return | |
171 if not import_name: | |
172 import_name,(plugin_path, mod, plugin_info) = plugins_to_import.popitem() | |
173 else: | |
174 if not import_name in plugins_to_import: | |
175 raise ImportError(_('Dependency plugin not found: [%s]') % import_name) | |
176 plugin_path, mod, plugin_info = plugins_to_import.pop(import_name) | |
177 dependencies = plugin_info.setdefault("dependencies",[]) | |
178 for dependency in dependencies: | |
179 if not self.plugins.has_key(dependency): | |
180 debug('Recursively import dependency of [%s]: [%s]' % (import_name, dependency)) | |
181 self._import_plugins_from_dict(plugins_to_import, dependency) | |
182 info (_("importing plugin: %s"), plugin_info['name']) | |
183 self.plugins[import_name] = getattr(mod, plugin_info['main'])(self) | |
184 if plugin_info.has_key('handler') and plugin_info['handler'] == 'yes': | |
185 self.plugins[import_name].is_handler = True | |
186 else: | |
187 self.plugins[import_name].is_handler = False | |
188 #TODO: test xmppclient presence and register handler parent | |
189 | |
190 def connect(self, profile_key = '@DEFAULT@'): | |
191 """Connect to jabber server""" | |
192 | |
193 profile = self.memory.getProfileName(profile_key) | |
194 if not profile: | |
195 error (_('Trying to connect a non-exsitant profile')) | |
196 return | |
197 | |
198 if (self.isConnected(profile)): | |
199 info(_("already connected !")) | |
200 return | |
201 current = self.profiles[profile] = SatXMPPClient(self, profile, | |
202 jid.JID(self.memory.getParamA("JabberID", "Connection", profile_key = profile), profile), | |
203 self.memory.getParamA("Password", "Connection", profile_key = profile), | |
204 self.memory.getParamA("Server", "Connection", profile_key = profile), 5222) | |
205 | |
206 current.messageProt = SatMessageProtocol(self) | |
207 current.messageProt.setHandlerParent(current) | |
208 | |
209 current.roster = SatRosterProtocol(self) | |
210 current.roster.setHandlerParent(current) | |
211 | |
212 current.presence = SatPresenceProtocol(self) | |
213 current.presence.setHandlerParent(current) | |
214 | |
215 current.fallBack = SatFallbackHandler(self) | |
216 current.fallBack.setHandlerParent(current) | |
217 | |
218 current.versionHandler = SatVersionHandler(self.get_const('client_name'), | |
219 self.get_const('client_version')) | |
220 current.versionHandler.setHandlerParent(current) | |
221 | |
222 debug (_("setting plugins parents")) | |
223 | |
224 for plugin in self.plugins.iteritems(): | |
225 if plugin[1].is_handler: | |
226 plugin[1].getHandler(profile).setHandlerParent(current) | |
227 | |
228 current.startService() | |
229 | |
230 def disconnect(self, profile_key='@DEFAULT@'): | |
231 """disconnect from jabber server""" | |
232 if (not self.isConnected(profile_key)): | |
233 info(_("not connected !")) | |
234 return | |
235 profile = self.memory.getProfileName(profile_key) | |
236 info(_("Disconnecting...")) | |
237 self.profiles[profile].stopService() | |
238 | |
239 def startService(self): | |
240 info("Salut à toi ô mon frère !") | |
241 #TODO: manage autoconnect | |
242 #self.connect() | |
243 | |
244 def stopService(self): | |
245 self.memory.save() | |
246 info("Salut aussi à Rantanplan") | |
247 | |
248 def run(self): | |
249 debug(_("running app")) | |
250 reactor.run() | |
251 | |
252 def stop(self): | |
253 debug(_("stopping app")) | |
254 reactor.stop() | |
255 | |
256 ## Misc methods ## | |
257 | |
258 def getJidNStream(self, profile_key): | |
259 """Convenient method to get jid and stream from profile key | |
260 @return: tuple (jid, xmlstream) from profile, can be None""" | |
261 profile = self.memory.getProfileName(profile_key) | |
262 if not profile or not self.profiles[profile].isConnected(): | |
263 return (None, None) | |
264 return (self.profiles[profile].jid, self.profiles[profile].xmlstream) | |
265 | |
266 def getClient(self, profile_key): | |
267 """Convenient method to get client from profile key | |
268 @return: client or None if it doesn't exist""" | |
269 profile = self.memory.getProfileName(profile_key) | |
270 if not profile: | |
271 return None | |
272 return self.profiles[profile] | |
273 | |
274 def registerNewAccount(self, login, password, server, port = 5222, id = None): | |
275 """Connect to a server and create a new account using in-band registration""" | |
276 | |
277 next_id = id or sat_next_id() #the id is used to send server's answer | |
278 serverRegistrer = xmlstream.XmlStreamFactory(RegisteringAuthenticator(self, server, login, password, next_id)) | |
279 connector = reactor.connectTCP(server, port, serverRegistrer) | |
280 serverRegistrer.clientConnectionLost = lambda conn, reason: connector.disconnect() | |
281 | |
282 return next_id | |
283 | |
284 def registerNewAccountCB(self, id, data, profile): | |
285 user = jid.parse(self.memory.getParamA("JabberID", "Connection", profile_key=profile))[0] | |
286 password = self.memory.getParamA("Password", "Connection", profile_key=profile) | |
287 server = self.memory.getParamA("Server", "Connection", profile_key=profile) | |
288 | |
289 if not user or not password or not server: | |
290 info (_('No user or server given')) | |
291 #TODO: a proper error message must be sent to frontend | |
292 self.actionResult(id, "ERROR", {'message':_("No user, password or server given, can't register new account.")}) | |
293 return | |
294 | |
295 confirm_id = sat_next_id() | |
296 self.__private_data[confirm_id]=(id,profile) | |
297 | |
298 self.askConfirmation(confirm_id, "YES/NO", | |
299 {"message":_("Are you sure to register new account [%(user)s] to server %(server)s ?") % {'user':user, 'server':server, 'profile':profile}}, | |
300 self.regisConfirmCB) | |
301 print ("===============+++++++++++ REGISTER NEW ACCOUNT++++++++++++++============") | |
302 print "id=",id | |
303 print "data=",data | |
304 | |
305 def regisConfirmCB(self, id, accepted, data): | |
306 print _("register Confirmation CB ! (%s)") % str(accepted) | |
307 action_id,profile = self.__private_data[id] | |
308 del self.__private_data[id] | |
309 if accepted: | |
310 user = jid.parse(self.memory.getParamA("JabberID", "Connection", profile_key=profile))[0] | |
311 password = self.memory.getParamA("Password", "Connection", profile_key=profile) | |
312 server = self.memory.getParamA("Server", "Connection", profile_key=profile) | |
313 self.registerNewAccount(user, password, server, id=action_id) | |
314 else: | |
315 self.actionResult(action_id, "SUPPRESS", {}) | |
316 | |
317 def submitForm(self, action, target, fields, profile_key='@DEFAULT@'): | |
318 """submit a form | |
319 @param target: target jid where we are submitting | |
320 @param fields: list of tuples (name, value) | |
321 @return: tuple: (id, deferred) | |
322 """ | |
323 | |
324 profile = self.memory.getProfileName(profile_key) | |
325 assert(profile) | |
326 to_jid = jid.JID(target) | |
327 | |
328 iq = compat.IQ(self.profiles[profile].xmlstream, 'set') | |
329 iq["to"] = target | |
330 iq["from"] = self.profiles[profile].jid.full() | |
331 query = iq.addElement(('jabber:iq:register', 'query')) | |
332 if action=='SUBMIT': | |
333 form = tupleList2dataForm(fields) | |
334 query.addChild(form.toElement()) | |
335 elif action=='CANCEL': | |
336 query.addElement('remove') | |
337 else: | |
338 error (_("FIXME FIXME FIXME: Unmanaged action (%s) in submitForm") % action) | |
339 raise NotImplementedError | |
340 | |
341 deferred = iq.send(target) | |
342 return (iq['id'], deferred) | |
343 | |
344 ## Client management ## | |
345 | |
346 def setParam(self, name, value, category, profile_key='@DEFAULT@'): | |
347 """set wanted paramater and notice observers""" | |
348 info (_("setting param: %(name)s=%(value)s in category %(category)s") % {'name':name, 'value':value, 'category':category}) | |
349 self.memory.setParam(name, value, category, profile_key) | |
350 | |
351 def isConnected(self, profile_key='@DEFAULT@'): | |
352 """Return connection status of profile | |
353 @param profile_key: key_word or profile name to determine profile name | |
354 @return True if connected | |
355 """ | |
356 profile = self.memory.getProfileName(profile_key) | |
357 if not profile: | |
358 error (_('asking connection status for a non-existant profile')) | |
359 return | |
360 if not self.profiles.has_key(profile): | |
361 return False | |
362 return self.profiles[profile].isConnected() | |
363 | |
364 def launchAction(self, type, data, profile_key='@DEFAULT@'): | |
365 """Launch a specific action asked by client | |
366 @param type: action type (button) | |
367 @param data: needed data to launch the action | |
368 | |
369 @return: action id for result, or empty string in case or error | |
370 """ | |
371 profile = self.memory.getProfileName(profile_key) | |
372 if not profile: | |
373 error (_('trying to launch action with a non-existant profile')) | |
374 raise Exception #TODO: raise a proper exception | |
375 if type=="button": | |
376 try: | |
377 cb_name = data['callback_id'] | |
378 except KeyError: | |
379 error (_("Incomplete data")) | |
380 return "" | |
381 id = sat_next_id() | |
382 self.callGeneralCB(cb_name, id, data, profile = profile) | |
383 return id | |
384 else: | |
385 error (_("Unknown action type")) | |
386 return "" | |
387 | |
388 | |
389 ## jabber methods ## | |
390 | |
391 def sendMessage(self, to, msg, subject=None, type='chat', profile_key='@DEFAULT@'): | |
392 #FIXME: check validity of recipient | |
393 profile = self.memory.getProfileName(profile_key) | |
394 assert(profile) | |
395 current_jid = self.profiles[profile].jid | |
396 debug(_("Sending jabber message to %s..."), to) | |
397 message = domish.Element(('jabber:client','message')) | |
398 message["to"] = jid.JID(to).full() | |
399 message["from"] = current_jid.full() | |
400 message["type"] = type | |
401 if subject: | |
402 message.addElement("subject", "jabber:client", subject) | |
403 message.addElement("body", "jabber:client", msg) | |
404 self.profiles[profile].xmlstream.send(message) | |
405 self.memory.addToHistory(current_jid, current_jid, jid.JID(to), message["type"], unicode(msg)) | |
406 if type!="groupchat": | |
407 self.bridge.newMessage(message['from'], unicode(msg), mess_type=type, to_jid=message['to'], profile=profile) #We send back the message, so all clients are aware of it | |
408 | |
409 | |
410 def setPresence(self, to="", show="", priority = 0, statuses={}, profile_key='@DEFAULT@'): | |
411 """Send our presence information""" | |
412 profile = self.memory.getProfileName(profile_key) | |
413 assert(profile) | |
414 to_jid = jid.JID(to) if to else None | |
415 self.profiles[profile].presence.available(to_jid, show, statuses, priority) | |
416 #XXX: FIXME: temporary fix to work around openfire 3.7.0 bug (presence is not broadcasted to generating resource) | |
417 if statuses.has_key(''): | |
418 statuses['default'] = statuses[''] | |
419 del statuses[''] | |
420 self.bridge.presenceUpdate(self.profiles[profile].jid.full(), show, | |
421 int(priority), statuses, profile) | |
422 | |
423 | |
424 def subscription(self, subs_type, raw_jid, profile_key='@DEFAULT@'): | |
425 """Called to manage subscription | |
426 @param subs_type: subsciption type (cf RFC 3921) | |
427 @param raw_jid: unicode entity's jid | |
428 @param profile_key: profile""" | |
429 profile = self.memory.getProfileName(profile_key) | |
430 assert(profile) | |
431 to_jid = jid.JID(raw_jid) | |
432 debug (_('subsciption request [%(subs_type)s] for %(jid)s') % {'subs_type':subs_type, 'jid':to_jid.full()}) | |
433 if subs_type=="subscribe": | |
434 self.profiles[profile].presence.subscribe(to_jid) | |
435 elif subs_type=="subscribed": | |
436 self.profiles[profile].presence.subscribed(to_jid) | |
437 contact = self.memory.getContact(to_jid) | |
438 if not contact or not bool(contact['to']): #we automatically subscribe to 'to' presence | |
439 debug(_('sending automatic "to" subscription request')) | |
440 self.subscription('subscribe', to_jid.userhost()) | |
441 elif subs_type=="unsubscribe": | |
442 self.profiles[profile].presence.unsubscribe(to_jid) | |
443 elif subs_type=="unsubscribed": | |
444 self.profiles[profile].presence.unsubscribed(to_jid) | |
445 | |
446 | |
447 def addContact(self, to, profile_key='@DEFAULT@'): | |
448 """Add a contact in roster list""" | |
449 profile = self.memory.getProfileName(profile_key) | |
450 assert(profile) | |
451 to_jid=jid.JID(to) | |
452 #self.profiles[profile].roster.addItem(to_jid) XXX: disabled (cf http://wokkel.ik.nu/ticket/56)) | |
453 self.profiles[profile].presence.subscribe(to_jid) | |
454 | |
455 def delContact(self, to, profile_key='@DEFAULT@'): | |
456 """Remove contact from roster list""" | |
457 profile = self.memory.getProfileName(profile_key) | |
458 assert(profile) | |
459 to_jid=jid.JID(to) | |
460 self.profiles[profile].roster.removeItem(to_jid) | |
461 self.profiles[profile].presence.unsubscribe(to_jid) | |
462 self.bridge.contactDeleted(to, profile) | |
463 | |
464 | |
465 ## callbacks ## | |
466 | |
467 def serverDisco(self, disco, profile): | |
468 """xep-0030 Discovery Protocol.""" | |
469 for feature in disco.features: | |
470 debug (_("Feature found: %s"),feature) | |
471 self.memory.addServerFeature(feature, profile) | |
472 for cat, type in disco.identities: | |
473 debug (_("Identity found: [%(category)s/%(type)s] %(identity)s") % {'category':cat, 'type':type, 'identity':disco.identities[(cat,type)]}) | |
474 | |
475 def serverDiscoItems(self, disco_result, disco_client, profile, initialized): | |
476 """xep-0030 Discovery Protocol. | |
477 @param disco_result: result of the disco item querry | |
478 @param disco_client: SatDiscoProtocol instance | |
479 @param profile: profile of the user | |
480 @param initialized: deferred which must be chained when everything is done""" | |
481 def _check_entity_cb(result, entity, profile): | |
482 for category, type in result.identities: | |
483 debug (_('Identity added: (%(category)s,%(type)s) ==> %(entity)s [%(profile)s]') % { | |
484 'category':category, 'type':type, 'entity':entity, 'profile':profile}) | |
485 self.memory.addServerIdentity(category, type, entity, profile) | |
486 | |
487 defer_list = [] | |
488 for item in disco_result._items: | |
489 defer_list.append(disco_client.requestInfo(item.entity).addCallback(_check_entity_cb, item.entity, profile)) | |
490 defer.DeferredList(defer_list).chainDeferred(initialized) | |
491 | |
492 | |
493 ## Generic HMI ## | |
494 | |
495 def actionResult(self, id, type, data): | |
496 """Send the result of an action | |
497 @param id: same id used with action | |
498 @param type: result type ("PARAM", "SUCCESS", "ERROR", "XMLUI") | |
499 @param data: dictionary | |
500 """ | |
501 self.bridge.actionResult(type, id, data) | |
502 | |
503 def actionResultExt(self, id, type, data): | |
504 """Send the result of an action, extended version | |
505 @param id: same id used with action | |
506 @param type: result type /!\ only "DICT_DICT" for this method | |
507 @param data: dictionary of dictionaries | |
508 """ | |
509 if type != "DICT_DICT": | |
510 error(_("type for actionResultExt must be DICT_DICT, fixing it")) | |
511 type = "DICT_DICT" | |
512 self.bridge.actionResultExt(type, id, data) | |
513 | |
514 | |
515 | |
516 def askConfirmation(self, id, type, data, cb): | |
517 """Add a confirmation callback | |
518 @param id: id used to get answer | |
519 @param type: confirmation type ("YES/NO", "FILE_TRANSFERT") | |
520 @param data: data (depend of confirmation type) | |
521 @param cb: callback called with the answer | |
522 """ | |
523 if self.__waiting_conf.has_key(id): | |
524 error (_("Attempt to register two callbacks for the same confirmation")) | |
525 else: | |
526 self.__waiting_conf[id] = cb | |
527 self.bridge.askConfirmation(type, id, data) | |
528 | |
529 | |
530 def confirmationAnswer(self, id, accepted, data): | |
531 """Called by frontends to answer confirmation requests""" | |
532 debug (_("Received confirmation answer for id [%(id)s]: %(success)s") % {'id': id, 'success':_("accepted") if accepted else _("refused")}) | |
533 if not self.__waiting_conf.has_key(id): | |
534 error (_("Received an unknown confirmation")) | |
535 else: | |
536 cb = self.__waiting_conf[id] | |
537 del self.__waiting_conf[id] | |
538 cb(id, accepted, data) | |
539 | |
540 def registerProgressCB(self, id, CB): | |
541 """Register a callback called when progress is requested for id""" | |
542 self.__progress_cb_map[id] = CB | |
543 | |
544 def removeProgressCB(self, id): | |
545 """Remove a progress callback""" | |
546 if not self.__progress_cb_map.has_key(id): | |
547 error (_("Trying to remove an unknow progress callback")) | |
548 else: | |
549 del self.__progress_cb_map[id] | |
550 | |
551 def getProgress(self, id): | |
552 """Return a dict with progress information | |
553 data['position'] : current possition | |
554 data['size'] : end_position | |
555 """ | |
556 data = {} | |
557 try: | |
558 self.__progress_cb_map[id](data) | |
559 except KeyError: | |
560 pass | |
561 #debug("Requested progress for unknown id") | |
562 return data | |
563 | |
564 def registerGeneralCB(self, name, CB): | |
565 """Register a callback called for general reason""" | |
566 self.__general_cb_map[name] = CB | |
567 | |
568 def removeGeneralCB(self, name): | |
569 """Remove a general callback""" | |
570 if not self.__general_cb_map.has_key(name): | |
571 error (_("Trying to remove an unknow general callback")) | |
572 else: | |
573 del self.__general_cb_map[name] | |
574 | |
575 def callGeneralCB(self, name, *args, **kwargs): | |
576 """Call general function back""" | |
577 try: | |
578 return self.__general_cb_map[name](*args, **kwargs) | |
579 except KeyError: | |
580 error(_("Trying to call unknown function (%s)") % name) | |
581 return None | |
582 | |
583 #Menus management | |
584 | |
585 def importMenu(self, category, name, callback, help_string = "", type = "NORMAL"): | |
586 """register a new menu for frontends | |
587 @param category: category of the menu | |
588 @param name: menu item entry | |
589 @param callback: method to be called when menuitem is selected""" | |
590 if self.menus.has_key((category,name)): | |
591 error ("Want to register a menu which already existe") | |
592 return | |
593 self.menus[(category,name,type)] = {'callback':callback, 'help_string':help_string, 'type':type} | |
594 | |
595 def getMenus(self): | |
596 """Return all menus registered""" | |
597 return self.menus.keys() | |
598 | |
599 def getMenuHelp(self, category, name, type="NORMAL"): | |
600 """return the help string of the menu""" | |
601 try: | |
602 return self.menus[(category,name,type)]['help_string'] | |
603 except KeyError: | |
604 error (_("Trying to access an unknown menu")) | |
605 return "" | |
606 | |
607 def callMenu(self, category, name, type="NORMAL", profile_key='@DEFAULT@'): | |
608 """return the id of the action""" | |
609 profile = self.memory.getProfileName(profile_key) | |
610 if not profile_key: | |
611 error (_('Non-exsitant profile')) | |
612 return "" | |
613 if self.menus.has_key((category,name,type)): | |
614 id = self.get_next_id() | |
615 self.menus[(category,name,type)]['callback'](id, profile) | |
616 return id | |
617 else: | |
618 error (_("Trying to access an unknown menu (%(category)s/%(name)s/%(type)s)")%{'category':category, 'name':name,'type':type}) | |
619 return "" | |
620 | |
621 | |
622 | 30 |
623 application = service.Application('SàT') | 31 application = service.Application('SàT') |
624 service = SAT() | 32 service = SAT() |
625 service.setServiceParent(application) | 33 service.setServiceParent(application) |