Mercurial > libervia-backend
changeset 60:9764e027ecc0
SàT: multi-profile parameters, first draft
- new bridge methods getProfilesList and createProfile
- param xml now use two types: general and individual, general are common parameters, individual parameters are linked to profiles
- indidual xml are constructed from param xml and saved values
/!\ params values are not saved yet
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 28 Jan 2010 02:32:27 +1100 |
parents | 3e5abe3bbead |
children | 58d49fc19639 |
files | plugins/plugin_xep_0065.py sat sat.tac sat_bridge/DBus.py tools/memory.py |
diffstat | 5 files changed, 207 insertions(+), 39 deletions(-) [+] |
line wrap: on
line diff
--- a/plugins/plugin_xep_0065.py Sun Jan 10 18:40:45 2010 +1100 +++ b/plugins/plugin_xep_0065.py Thu Jan 28 02:32:27 2010 +1100 @@ -455,10 +455,12 @@ params = """ <params> + <general> <category name="File Transfert"> <param name="IP" value='0.0.0.0' default_cb='yes' type="string" /> <param name="Port" value="28915" type="string" /> </category> + </general> </params> """
--- a/sat Sun Jan 10 18:40:45 2010 +1100 +++ b/sat Thu Jan 28 02:32:27 2010 +1100 @@ -1,3 +1,3 @@ #!/bin/sh -twistd -oy sat.tac +twistd -noy sat.tac
--- a/sat.tac Sun Jan 10 18:40:45 2010 +1100 +++ b/sat.tac Thu Jan 28 02:32:27 2010 +1100 @@ -21,7 +21,7 @@ CONST = { 'client_name' : u'SàT (Salut à toi)', - 'client_version' : u'0.0.2', #Please add 'D' at the end for dev versions + 'client_version' : u'0.0.2D', #Please add 'D' at the end for dev versions 'local_dir' : '~/.sat' } @@ -312,6 +312,8 @@ self.server_features=[] #XXX: temp dic, need to be transfered into self.memory in the future self.bridge=DBusBridge() + self.bridge.register("getProfilesList", self.memory.getProfilesList) + self.bridge.register("createProfile", self.memory.createProfile) self.bridge.register("registerNewAccount", self.registerNewAccount) self.bridge.register("connect", self.connect) self.bridge.register("disconnect", self.disconnect) @@ -351,14 +353,19 @@ self.plugins[plug_info['import_name']] = getattr(mod, plug_info['main'])(self) #TODO: test xmppclient presence and register handler parent - def connect(self): + def getJidFrProfile(self, profile): + """Return jid from source""" + if profile == "@DEFAULT@": + return jid.JID(self.memory.getParamA("JabberID", "Connection")) + + def connect(self, profile = '@DEFAULT@'): """Connect to jabber server""" if (self.isConnected()): info("already connected !") return print "connecting..." - self.me = jid.JID(self.memory.getParamA("JabberID", "Connection")) + self.me = self.getJidFrProfile(profile) self.xmppclient = SatXMPPClient(self.bridge, self.me, self.memory.getParamA("Password", "Connection"), self.memory.getParamA("Server", "Connection"), 5222) self.xmppclient.streamInitialized = self.streamInitialized
--- a/sat_bridge/DBus.py Sun Jan 10 18:40:45 2010 +1100 +++ b/sat_bridge/DBus.py Thu Jan 28 02:32:27 2010 +1100 @@ -105,6 +105,18 @@ ### methods ### + @dbus.service.method(const_INT_PREFIX+const_REQ_SUFFIX, + in_signature='', out_signature='as') + def getProfilesList(self): + info ('Profile list asked') + return self.cb["getProfilesList"]() + + @dbus.service.method(const_INT_PREFIX+const_REQ_SUFFIX, + in_signature='sb', out_signature='') + def createProfile(self, name, default=False): + info ('Profile creation asked') + return self.cb["createProfile"](name, default) + @dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX, in_signature='sssi', out_signature='s') def registerNewAccount(self, login, password, host, port=5222):
--- a/tools/memory.py Sun Jan 10 18:40:45 2010 +1100 +++ b/tools/memory.py Thu Jan 28 02:32:27 2010 +1100 @@ -41,15 +41,19 @@ #TODO: mettre Watched dans un plugin default_xml = u""" <params> - <category name="Connection"> - <param name="JabberID" value="goffi@necton2.int/TestScript" type="string" /> - <param name="Password" value="toto" type="password" /> - <param name="Server" value="necton2.int" type="string" /> - <param name="NewAccount" value="Register new account" type="button" callback="registerNewAccount"/> - </category> - <category name="Misc"> - <param name="Watched" value="test@Jabber.goffi.int" type="string" /> - </category> + <general> + </general> + <individual> + <category name="Connection"> + <param name="JabberID" value="goffi@necton2.int/TestScript" type="string" /> + <param name="Password" value="toto" type="password" /> + <param name="Server" value="necton2.int" type="string" /> + <param name="NewAccount" value="Register new account" type="button" callback="registerNewAccount"/> + </category> + <category name="Misc"> + <param name="Watched" value="test@Jabber.goffi.int" type="string" /> + </category> + </individual> </params> """ @@ -64,17 +68,47 @@ """Save parameters to xml file""" with open(file, 'wb') as xml_file: self.dom.writexml(xml_file) - + def __init__(self, host): debug("Parameters init") self.host = host + self.default_profile = None + self.params = {'goffi':{}} #gof: + self.params_gen = {} host.set_const('savefile_param', SAVEFILE_PARAM) - host.set_const('savefile_history', SAVEFILE_HISTORY) - host.set_const('savefile_private', SAVEFILE_PRIVATE) host.registerGeneralCB("registerNewAccount", host.registerNewAccountCB) + def getProfilesList(self): + return self.params.keys() + + def createProfile(self, name, default=False): + """Create a new profile + @param name: Name of the profile + @param default: True if default value""" + if self.params.has_key(name): + info ('The profile name already exists') + return 1 + self.params[name]={} + return 0 + + def getProfileName(self, profile_key): + """return profile according to profile_key + @param profile_key: profile name or key which can be + @ALL@ for all profiles + @DEFAULT@ for default profile + @return: requested profile name or None if it doesn't exist""" + if profile_key=='@DEFAULT@': + if not self.params: + return None + info('No default profile, returning first one') #TODO: manage real default profile + return self.params.keys()[0] #FIXME: gof: temporary, must use real default value, and fallback to first one if it doesn't exists + if not self.params.has_key(profile_key): + error ('Trying to access an unknown profile') + return None + return profile_key + def __get_unique_node(self, parent, tag, name): - """return node with given tag, create a new one if the node doesn't exist + """return node with given tag @param parent: parent of nodes to check (e.g. documentElement) @param tag: tag to check (e.g. "category") @param name: name to check (e.g. "JID") @@ -119,47 +153,133 @@ @param errback: must manage the error with args failure, name, category """ #TODO: send signal param update if value changed - node = self.__getParamNode(name, category) + node = self.__getParamNode(name, category, '@ALL@') if not node: error("Requested param [%s] in category [%s] doesn't exist !" , name, category) return - if node.getAttribute('default_cb') == 'yes': - del node.attributes['default_cb'] + if node[1].getAttribute('default_cb') == 'yes': + del node[1].attributes['default_cb'] d = defer.maybeDeferred(callback) d.addCallback(self.__default_ok, name, category) d.addErrback(errback or self.__default_ko, name, category) - def getParamA(self, name, category, attr="value"): + def getParamA(self, name, category, attr="value", profile_key="@DEFAULT@"): """Helper method to get a specific attribute @param name: name of the parameter @param category: category of the parameter @param attr: name of the attribute (default: "value") + @param profile: owner of the param (@ALL@ for everyone) @return: attribute""" node = self.__getParamNode(name, category) if not node: error("Requested param [%s] in category [%s] doesn't exist !" , name, category) return None - return node.getAttribute(attr) + + if node[0] == 'general': + value = self.__getParam(None, category, name, 'general') + return value or node[1].getAttribute(attr) + + assert(node[0] == 'individual') + + profile = self.getProfileName(profile_key) + if not profile: + error('Requesting a param for an non-existant profile') + return None + + return self.__getParam(profile, category, name) or node[1].getAttribute(attr) + - def getParams(self): - """Return the whole params XML""" - return self.dom.toxml() + def __getParam(self, profile, category, name, type='individual'): + """Return the param, or None if it doesn't exist + @param profile: the profile name (not profile key, i.e. name and not something like @DEFAULT@) + @param category: param category + @param name: param name + """ + if type == 'general': + if self.params_gen.has_key((category, name)): + return self.params_gen[(category, name)] + return None #This general param has the default value + assert (type == 'individual') + if not self.params.has_key(profile) or not self.params[profile].has_key((category, name)): + return None + return self.params[profile][(category, name)] - def getParamsForCategory(self, category): + def __constructProfileXml(self, profile): + """Construct xml for asked profile, filling values when needed + /!\ as noticed in doc, don't forget to unlink the minidom.Document + @param profile: profile name (not key !) + @return: minidom.Document of the profile xml (cf warning above) + """ + prof_xml = minidom.parseString('<params/>') + + for type_node in self.dom.documentElement.childNodes: + if type_node.nodeName == 'general' or type_node.nodeName == 'individual': #we use all params, general and individual + for cat_node in type_node.childNodes: + if cat_node.nodeName == 'category': + category = cat_node.getAttribute('name') + cat_copy = cat_node.cloneNode(True) #we make a copy for the new xml + params = cat_copy.getElementsByTagName("param") + for param_node in params: + name = param_node.getAttribute('name') + profile_value = self.__getParam(profile, category, name, type_node.nodeName) + if profile_value: #there is a value for this profile, we must change the default + param_node.setAttribute('value', profile_value) + prof_xml.documentElement.appendChild(cat_copy) + return prof_xml + + + + def getParams(self, profile_key='@DEFAULT@'): + """Construct xml for asked profile + Take params xml as skeleton""" + profile = self.getProfileName(profile_key) + if not profile: + error("Asking params for inexistant profile") + return "" + prof_xml = self.__constructProfileXml(profile) + return_xml = prof_xml.toxml() + prof_xml.unlink() + + return return_xml + + def getParamsForCategory(self, category, profile_key='@DEFAULT@'): """Return node's xml for selected category""" - for node in self.dom.documentElement.childNodes: + #TODO: manage category of general type (without existant profile) + profile = self.getProfileName(profile_key) + if not profile: + error("Asking params for inexistant profile") + return "" + prof_xml = self.__constructProfileXml(profile) + + for node in prof_xml.getElementsByTagName("category"): if node.nodeName == "category" and node.getAttribute("name") == category: - return node.toxml() + result = node.toxml() + prof_xml.unlink() + return result + + prof_xml.unlink() return "<category />" - def __getParamNode(self, name, category): - for node in self.dom.documentElement.childNodes: - if node.nodeName == "category" and node.getAttribute("name") == category: - params = node.getElementsByTagName("param") - for param in params: - if param.getAttribute("name") == name: - return param + def __getParamNode(self, name, category, type="@ALL@"): #FIXME: is type useful ? + """Return a node from the param_xml + @param name: name of the node + @param category: category of the node + @type: keyword for search: + @ALL@ search everywhere + @GENERAL@ only search in general type + @INDIVIDUAL@ only search in individual type + @return: a tuple with the node type and the the node, or None if not found""" + + for type_node in self.dom.documentElement.childNodes: + if ( ((type == "@ALL@" or type == "@GENERAL@") and type_node.nodeName == 'general') + or ( (type == "@ALL@" or type == "@INDIVIDUAL@") and type_node.nodeName == 'individual') ): + for node in type_node.getElementsByTagName('category'): + if node.getAttribute("name") == category: + params = node.getElementsByTagName("param") + for param in params: + if param.getAttribute("name") == name: + return (type_node.nodeName, param) return None def getParamsCategories(self): @@ -169,16 +289,31 @@ categories.append(cat.getAttribute("name")) return categories - def setParam(self, name, value, category): - node = self.__getParamNode(name, category) + def setParam(self, name, value, category, profile_key='@DEFAULT@'): + """Set a parameter, return None if the parameter is not in param xml""" + + node = self.__getParamNode(name, category, '@ALL@') if not node: + error('Requesting an unknown parameter (%s/%s)' % (category, name)) + return + + if node[0] == 'general': + self.params_gen[(category, name)] = value + return + + assert (node[0] == 'individual') + + profile = self.getProfileName(profile_key) + if not profile: + error('Trying to set parameter for an unknown profile') return #TODO: throw an error + type = node.getAttribute("type") if type=="button": print "clique",node.toxml() else: - node.setAttribute("value", value) - self.host.bridge.paramUpdate(name, value, category) + self.params[profile][(category, name)] = value + self.host.bridge.paramUpdate(name, value, category) #TODO: add profile in signal class Memory: """This class manage all persistent informations""" @@ -194,6 +329,8 @@ self.private={} #used to store private value self.disco={} #XXX: maybe best in a separate class self.features={} + host.set_const('savefile_history', SAVEFILE_HISTORY) + host.set_const('savefile_private', SAVEFILE_PRIVATE) self.load() def load(self): @@ -254,6 +391,16 @@ pickle.dump(self.private, private_pickle) debug("private values saved") + def getProfilesList(self): + return self.params.getProfilesList() + + def createProfile(self, name, default=False): + """Create a new profile + @param name: Profile name + @param default: True if default profile (replace previous default) + """ + return self.params.createProfile(name, default) + def addToHistory(self, me_jid, from_jid, to_jid, type, message): me_short=me_jid.userhost() from_short=from_jid.userhost()