files reorganisation

This commit is contained in:
Goffi 2011-01-04 19:30:27 +01:00
commit 0a72186117
9 changed files with 0 additions and 0 deletions

0
src/__init__.py Normal file
View file

401
src/bridge/DBus.py Executable file
View file

@ -0,0 +1,401 @@
#!/usr/bin/python
#-*- coding: utf-8 -*-
"""
SAT: a jabber client
Copyright (C) 2009, 2010 Jérôme Poisson (goffi@goffi.org)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from bridge import Bridge
import dbus
import dbus.service
import dbus.mainloop.glib
import pdb
from logging import debug, info, error
const_INT_PREFIX = "org.goffi.SAT" #Interface prefix
const_COMM_SUFFIX = ".communication"
const_REQ_SUFFIX = ".request"
class DbusObject(dbus.service.Object):
def __init__(self, bus, path):
dbus.service.Object.__init__(self, bus, path)
debug("Init DbusObject...")
self.cb={}
def register(self, name, cb):
self.cb[name]=cb
### signals ###
@dbus.service.signal(const_INT_PREFIX+const_COMM_SUFFIX,
signature='s')
def connected(self, profile):
debug("Connected signal")
@dbus.service.signal(const_INT_PREFIX+const_COMM_SUFFIX,
signature='s')
def disconnected(self, profile):
debug("Disconnected signal")
@dbus.service.signal(const_INT_PREFIX+const_COMM_SUFFIX,
signature='sa{ss}ass')
def newContact(self, contact, attributes, groups, profile):
debug("new contact signal (%s) sended (profile: %s)", contact, profile)
@dbus.service.signal(const_INT_PREFIX+const_COMM_SUFFIX,
signature='sssss')
def newMessage(self, from_jid, msg, type, to, profile):
debug("new message signal (from:%s msg:%s type:%s to:%s) sended", from_jid, msg, type, to)
@dbus.service.signal(const_INT_PREFIX+const_COMM_SUFFIX,
signature='ssss')
def newAlert(self, msg, title, type, profile):
debug("new alert signal (title:%s type:%s msg:%s profile:%s) sended", type, title, msg, profile)
@dbus.service.signal(const_INT_PREFIX+const_COMM_SUFFIX,
signature='ssia{ss}s')
def presenceUpdate(self, entity, show, priority, statuses, profile):
debug("presence update signal (from:%s show:%s priority:%d statuses:%s profile:%s) sended" , entity, show, priority, statuses, profile)
@dbus.service.signal(const_INT_PREFIX+const_COMM_SUFFIX,
signature='sss')
def subscribe(self, type, entity, profile):
debug("subscribe (type: [%s] from:[%s] profile:[%s])" , type, entity, profile)
@dbus.service.signal(const_INT_PREFIX+const_COMM_SUFFIX,
signature='ssss')
def paramUpdate(self, name, value, category, profile):
debug("param update signal: %s=%s in category %s (profile: %s)", name, value, category, profile)
@dbus.service.signal(const_INT_PREFIX+const_COMM_SUFFIX,
signature='ss')
def contactDeleted(self, entity, profile):
debug("contact deleted signal: %s (profile: %s)", entity, profile)
@dbus.service.signal(const_INT_PREFIX+const_REQ_SUFFIX,
signature='ssa{ss}')
def askConfirmation(self, type, id, data):
debug("asking for confirmation: id = [%s] type = %s data = %s", id, type, data)
@dbus.service.signal(const_INT_PREFIX+const_REQ_SUFFIX,
signature='ssa{ss}')
def actionResult(self, type, id, data):
debug("result of action: id = [%s] type = %s data = %s", id, type, data)
@dbus.service.signal(const_INT_PREFIX+const_REQ_SUFFIX,
signature='ssa{sa{ss}}')
def actionResultExt(self, type, id, data):
debug("extended result of action: id = [%s] type = %s data = %s", id, type, data)
@dbus.service.signal(const_INT_PREFIX+const_REQ_SUFFIX,
signature='sa{ss}')
def updatedValue(self, name, value):
debug("updated value: %s = %s", name, value)
### methods ###
@dbus.service.method(const_INT_PREFIX+const_REQ_SUFFIX,
in_signature='', out_signature='s')
def getVersion(self):
return self.cb["getVersion"]()
@dbus.service.method(const_INT_PREFIX+const_REQ_SUFFIX,
in_signature='s', out_signature='s')
def getProfileName(self, profile_key):
return self.cb["getProfileName"](profile_key)
@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='s', out_signature='i')
def createProfile(self, name):
info ('Profile creation asked')
return self.cb["createProfile"](unicode(name))
@dbus.service.method(const_INT_PREFIX+const_REQ_SUFFIX,
in_signature='s', out_signature='i')
def deleteProfile(self, name):
info ('Profile deletion asked')
return self.cb["deleteProfile"](str(name))
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='sssi', out_signature='s')
def registerNewAccount(self, login, password, host, port=5222):
info ("New account registration asked")
return self.cb["registerNewAccount"](login, password, host, port)
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='s', out_signature='')
def connect(self, profile_key='@DEFAULT@'):
info ("Connection asked")
return self.cb["connect"](profile_key)
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='s', out_signature='')
def disconnect(self, profile_key='@DEFAULT@'):
info ("Disconnection asked")
return self.cb["disconnect"](profile_key)
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='', out_signature='b')
def isConnected(self, profile_key='@DEFAULT@'):
info ("Connection status asked")
return self.cb["isConnected"](profile_key)
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='s', out_signature='a(sa{ss}as)')
def getContacts(self, profile_key='@DEFAULT@'):
debug("getContacts...")
return self.cb["getContacts"](profile_key)
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='s', out_signature='a{sa{s(sia{ss})}}')
def getPresenceStatus(self, profile_key='@DEFAULT@'):
debug("getPresenceStatus...")
return self.cb["getPresenceStatus"](profile_key)
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='s', out_signature='a{ss}')
def getWaitingSub(self, profile_key='@DEFAULT@'):
debug("getWaitingSub...")
return self.cb["getWaitingSub"](profile_key)
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='ssss', out_signature='')
def sendMessage(self, to, message, type='chat', profile_key='@DEFAULT@'):
debug("sendMessage...")
print "sendtype=", type #gof
self.cb["sendMessage"](to, message, type, profile_key)
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='ssia{ss}s', out_signature='')
def setPresence(self, to="", show="", priority=0, statuses={}, profile_key='@DEFAULT@'):
self.cb["setPresence"](to, show, priority, statuses, profile_key)
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='sss', out_signature='')
def subscription(self, type, entity, profile_key='@DEFAULT@'):
self.cb["subscription"](type, entity, profile_key)
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='ssss', out_signature='')
def setParam(self, name, value, category, profile_key='@DEFAULT@'):
self.cb["setParam"](unicode(name), unicode(value), unicode(category), profile_key)
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='sss', out_signature='s')
def getParamA(self, name, category="default", profile_key='@DEFAULT@'):
return self.cb["getParamA"](name, category, profile_key = profile_key)
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='s', out_signature='s')
def getParamsUI(self, profile_key='@DEFAULT@'):
return self.cb["getParamsUI"](profile_key)
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='s', out_signature='s')
def getParams(self, profile_key='@DEFAULT@'):
return self.cb["getParams"](profile_key)
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='ss', out_signature='s')
def getParamsForCategory(self, category, profile_key='@DEFAULT@'):
return self.cb["getParamsForCategory"](category, profile_key)
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='', out_signature='as')
def getParamsCategories(self):
return self.cb["getParamsCategories"]()
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='ssi', out_signature='a{i(ss)}')
def getHistory(self, from_jid, to_jid, size):
debug("History asked for %s", to_jid)
return self.cb["getHistory"](from_jid, to_jid, size)
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='ss', out_signature='')
def addContact(self, entity, profile_key='@DEFAULT@'):
debug("Subscription asked for %s (profile %s)", entity, profile_key)
return self.cb["addContact"](entity, profile_key)
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='ss', out_signature='')
def delContact(self, entity, profile_key='@DEFAULT@'):
debug("Unsubscription asked for %s (profile %s)", entity, profile_key)
return self.cb["delContact"](entity, profile_key)
@dbus.service.method(const_INT_PREFIX+const_REQ_SUFFIX,
in_signature='sa{ss}s', out_signature='s')
def launchAction(self, type, data, profile_key='@DEFAULT@'):
return self.cb["launchAction"](type, data, profile_key)
@dbus.service.method(const_INT_PREFIX+const_REQ_SUFFIX,
in_signature='sba{ss}', out_signature='')
def confirmationAnswer(self, id, accepted, data):
debug("Answer for confirmation [%s]: %s", id, "Accepted" if accepted else "Refused")
return self.cb["confirmationAnswer"](id, accepted, data)
@dbus.service.method(const_INT_PREFIX+const_REQ_SUFFIX,
in_signature='s', out_signature='a{ss}')
def getProgress(self, id):
#debug("Progress asked for %s", id)
return self.cb["getProgress"](id)
@dbus.service.method(const_INT_PREFIX+const_REQ_SUFFIX,
in_signature='', out_signature='a(sss)')
def getMenus(self):
return self.cb["getMenus"]()
@dbus.service.method(const_INT_PREFIX+const_REQ_SUFFIX,
in_signature='sss', out_signature='s')
def getMenuHelp(self, category, name, type="NORMAL"):
return self.cb["getMenuHelp"](category, name, type)
@dbus.service.method(const_INT_PREFIX+const_REQ_SUFFIX,
in_signature='ssss', out_signature='s')
def callMenu(self, category, name, type, profile_key):
return self.cb["callMenu"](category, name, type, profile_key)
def __attribute_string(self, in_sign):
i=0
idx=0
attr_string=""
while i<len(in_sign):
if in_sign[i] not in ['b','y','n','i','x','q','u','t','d','s','a']:
raise Exception #FIXME: create an exception here (unmanaged attribute type)
attr_string += ("" if idx==0 else ",") + ("arg_%i" % idx)
idx+=1
if in_sign[i] == 'a':
i+=1
if in_sign[i]!='{' and in_sign[i]!='(': #FIXME: must manage tuples out of arrays
i+=1
continue #we have a simple type for the array
while (True): #we have a dict or a list of tuples
i+=1
if i>=len(in_sign):
raise Exception #FIXME: create an exception here (the '}' is not presend)
if in_sign[i] == '}' or in_sign[i] == ')':
break
i+=1
return attr_string
def addMethod(self, name, int_suffix, in_sign, out_sign):
"""Dynamically add a method to Dbus Bridge"""
#FIXME: Better way ???
attributes = self.__attribute_string(in_sign)
code = compile ('def '+name+' (self,'+attributes+'): return self.cb["'+name+'"]('+attributes+')', '<DBus bridge>','exec')
exec (code)
method = locals()[name]
setattr(DbusObject, name, dbus.service.method(
const_INT_PREFIX+int_suffix, in_signature=in_sign, out_signature=out_sign)(method))
def addSignal(self, name, int_suffix, signature):
"""Dynamically add a signal to Dbus Bridge"""
#FIXME: Better way ???
attributes = self.__attribute_string(signature)
code = compile ('def '+name+' (self,'+attributes+'): debug ("'+name+' signal")', '<DBus bridge>','exec')
exec (code)
signal = locals()[name]
setattr(DbusObject, name, dbus.service.signal(
const_INT_PREFIX+int_suffix, signature=signature)(signal))
class DBusBridge(Bridge):
def __init__(self):
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
Bridge.__init__(self)
info ("Init DBus...")
self.session_bus = dbus.SessionBus()
self.dbus_name = dbus.service.BusName(const_INT_PREFIX, self.session_bus)
self.dbus_bridge = DbusObject(self.session_bus, '/org/goffi/SAT/bridge')
def connected(self, profile):
self.dbus_bridge.connected(profile)
def disconnected(self, profile):
self.dbus_bridge.disconnected(profile)
def newContact(self, contact, attributes, groups, profile):
self.dbus_bridge.newContact(contact, attributes, groups, profile)
def newMessage(self, from_jid, msg, type='chat', to='', profile='@NONE@'):
debug("sending message...")
self.dbus_bridge.newMessage(from_jid, msg, type, to, profile)
def newAlert(self, msg, title="", alert_type="INFO", profile='@NONE@'):
self.dbus_bridge.newAlert(msg, title, alert_type, profile)
def presenceUpdate(self, entity, show, priority, statuses, profile):
debug("updating presence for %s",entity)
self.dbus_bridge.presenceUpdate(entity, show, priority, statuses, profile)
def roomJoined(self, room_id, room_service, room_nicks, user_nick, profile):
self.dbus_bridge.roomJoined(room_id, room_service, room_nicks, user_nick, profile)
def subscribe(self, sub_type, entity, profile):
debug("subscribe request for %s",entity)
self.dbus_bridge.subscribe(sub_type, entity, profile)
def paramUpdate(self, name, value, category, profile):
debug("updating param [%s] %s ", category, name)
self.dbus_bridge.paramUpdate(name, value, category, profile)
def contactDeleted(self, entity, profile):
debug("sending contact deleted signal %s ", entity)
self.dbus_bridge.contactDeleted(entity, profile)
def askConfirmation(self, type, id, data):
self.dbus_bridge.askConfirmation(type, id, data)
def actionResult(self, type, id, data):
self.dbus_bridge.actionResult(type, id, data)
def actionResultExt(self, type, id, data):
self.dbus_bridge.actionResultExt(type, id, data)
def updatedValue(self, name, value):
self.dbus_bridge.updatedValue(name, value)
def register(self, name, callback):
debug("registering DBus bridge method [%s]", name)
self.dbus_bridge.register(name, callback)
def addMethod(self, name, int_suffix, in_sign, out_sign, method):
"""Dynamically add a method to Dbus Bridge"""
print ("Adding method [%s] to DBus bridge" % name)
self.dbus_bridge.addMethod(name, int_suffix, in_sign, out_sign)
self.register(name, method)
def addSignal(self, name, int_suffix, signature):
self.dbus_bridge.addSignal(name, int_suffix, signature)
setattr(DBusBridge, name, getattr(self.dbus_bridge, name))

0
src/bridge/__init__.py Normal file
View file

68
src/bridge/bridge.py Executable file
View file

@ -0,0 +1,68 @@
#!/usr/bin/python
#-*- coding: utf-8 -*-
"""
SAT: a jabber client
Copyright (C) 2009, 2010 Jérôme Poisson (goffi@goffi.org)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from logging import debug, info, error
class Bridge:
def __init__(self):
info ("Bridge initialization")
##signals
def newContact(self, contact):
raise NotImplementedError
def newMessage(self, from_jid, msg, type='chat'):
raise NotImplementedError
def presenceUpdate(self, type, jid, show, status, priority):
raise NotImplementedError
def paramUpdate(self, name, value):
raise NotImplementedError
##methods
def connect(self):
raise NotImplementedError
def getContacts(self):
raise NotImplementedError
def getPresenceStatus(self):
raise NotImplementedError
def sendMessage(self):
raise NotImplementedError
def setPresence(self, to="", type="", show="", status="", priority=0):
raise NotImplementedError
def setParam(self, name, value, namespace):
raise NotImplementedError
def getParam(self, name, namespace):
raise NotImplementedError
def getParams(self, namespace):
raise NotImplementedError
def getHistory(self, from_jid, to_jid, size):
raise NotImplementedError

0
src/tools/__init__.py Normal file
View file

80
src/tools/games.py Normal file
View file

@ -0,0 +1,80 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
SAT: a jabber client
Copyright (C) 2009, 2010 Jérôme Poisson (goffi@goffi.org)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from logging import debug, info, error
"""This library help manage general games (e.g. card games)"""
suits_order = ['pique', 'coeur', 'trefle', 'carreau', 'atout'] #I have switched the usual order 'trefle' and 'carreau' because card are more easy to see if suit colour change (black, red, black, red)
values_order = map(str,range(1,11))+["valet","cavalier","dame","roi"]
class TarotCard():
"""This class is used to represent a car logically"""
#TODO: move this in a library in tools, and share this with frontends (e.g. card_game in wix use the same class)
def __init__(self, tuple_card):
"""@param tuple_card: tuple (suit, value)"""
self.suit, self.value = tuple_card
self.bout = True if self.suit=="atout" and self.value in ["1","21","excuse"] else False
if self.bout or self.value == "roi":
self.points = 4.5
elif self.value == "dame":
self.points = 3.5
elif self.value == "cavalier":
self.points = 2.5
elif self.value == "valet":
self.points = 1.5
else:
self.points = 0.5
def get_tuple(self):
return (self.suit,self.value)
@staticmethod
def from_tuples(tuple_list):
result = []
for card_tuple in tuple_list:
result.append(TarotCard(card_tuple))
return result
def __cmp__(self, other):
if other == None:
return 1
if self.suit != other.suit:
idx1 = suits_order.index(self.suit)
idx2 = suits_order.index(other.suit)
return idx1.__cmp__(idx2)
if self.suit == 'atout':
if self.value == other.value == 'excuse':
return 0
if self.value == 'excuse':
return -1
if other.value == 'excuse':
return 1
return int(self.value).__cmp__(int(other.value))
#at this point we have the same suit which is not 'atout'
idx1 = values_order.index(self.value)
idx2 = values_order.index(other.value)
return idx1.__cmp__(idx2)
def __str__(self):
return "[%s,%s]" % (self.suit, self.value)

51
src/tools/jid.py Normal file
View file

@ -0,0 +1,51 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
SAT: a jabber client
Copyright (C) 2009, 2010 Jérôme Poisson (goffi@goffi.org)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
class JID(unicode):
"""This class help manage JID (Node@Domaine/Resource)"""
def __new__(cls, jid):
self = unicode.__new__(cls, jid)
self.__parse()
return self
def __parse(self):
"""find node domaine and resource"""
node_end=self.find('@')
if node_end<0:
node_end=0
domain_end=self.find('/')
if domain_end<1:
domain_end=len(self)
self.node=self[:node_end]
self.domain=self[(node_end+1) if node_end else 0:domain_end]
self.resource=self[domain_end+1:]
if not node_end:
self.short=self
else:
self.short=self.node+'@'+self.domain
def is_valid(self):
"""return True if the jid is xmpp compliant"""
#FIXME: always return True for the moment
return True

655
src/tools/memory.py Normal file
View file

@ -0,0 +1,655 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
SAT: a jabber client
Copyright (C) 2009, 2010 Jérôme Poisson (goffi@goffi.org)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from __future__ import with_statement
import os.path
import time
import cPickle as pickle
from xml.dom import minidom
from logging import debug, info, error
import pdb
from twisted.internet import defer
from twisted.words.protocols.jabber import jid
from sat.tools.xml_tools import paramsXml2xmlUI
SAVEFILE_PARAM_XML="/param" #xml parameters template
SAVEFILE_PARAM_DATA="/param" #individual & general parameters; _ind and _gen suffixes will be added
SAVEFILE_HISTORY="/history"
SAVEFILE_PRIVATE="/private" #file used to store misc values (mainly for plugins)
class Param():
"""This class manage parameters with xml"""
### TODO: add desciption in params
#TODO: move Watched in a plugin
default_xml = u"""
<params>
<general>
</general>
<individual>
<category name="Connection" label="%(category_connection)s">
<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="%(label_NewAccount)s" type="button" callback_id="registerNewAccount"/>
<param name="autoconnect" label="%(label_autoconnect)s" value="true" type="bool" />
<param name="autodisconnect" label="%(label_autodisconnect)s" value="false" type="bool" />
</category>
<category name="Misc" label="%(category_misc)s">
<param name="Watched" value="test@Jabber.goffi.int" type="string" />
</category>
</individual>
</params>
""" % {'category_connection': _("Connection"),
'label_NewAccount': _("Register new account"),
'label_autoconnect': _('Connect on frontend startup'),
'label_autodisconnect': _('Disconnect on frontend closure'),
'category_misc': _("Misc")
}
def load_default_params(self):
self.dom = minidom.parseString(Param.default_xml.encode('utf-8'))
def load_xml(self, file):
"""Load parameters template from file"""
self.dom = minidom.parse(file)
def load_data(self, file):
"""Load parameters data from file"""
file_ind = file + '_ind'
file_gen = file + '_gen'
if os.path.exists(file_gen):
try:
with open(file_gen, 'r') as file_gen_pickle:
self.params_gen=pickle.load(file_gen_pickle)
debug(_("general params data loaded"))
except:
error (_("Can't load general params data !"))
if os.path.exists(file_ind):
try:
with open(file_ind, 'r') as file_ind_pickle:
self.params=pickle.load(file_ind_pickle)
debug(_("individual params data loaded"))
except:
error (_("Can't load individual params data !"))
def save_xml(self, file):
"""Save parameters template to xml file"""
with open(file, 'wb') as xml_file:
xml_file.write(self.dom.toxml('utf-8'))
def save_data(self, file):
"""Save parameters data to file"""
#TODO: save properly in a separate file/database,
# use different behaviour depending of the data type (e.g. password encrypted)
#general params
with open(file+'_gen', 'w') as param_gen_pickle:
pickle.dump(self.params_gen, param_gen_pickle)
#then individual params
with open(file+'_ind', 'w') as param_ind_pickle:
pickle.dump(self.params, param_ind_pickle)
def __init__(self, host):
debug("Parameters init")
self.host = host
self.default_profile = None
self.params = {}
self.params_gen = {}
host.set_const('savefile_param_xml', SAVEFILE_PARAM_XML)
host.set_const('savefile_param_data', SAVEFILE_PARAM_DATA)
host.registerGeneralCB("registerNewAccount", host.registerNewAccountCB)
def getProfilesList(self):
return self.params.keys()
def createProfile(self, name):
"""Create a new profile
@param name: Name of the profile"""
if self.params.has_key(name):
info (_('The profile name already exists'))
return 1
self.params[name]={}
return 0
def deleteProfile(self, name):
"""Delete an existing profile
@param name: Name of the profile"""
if not self.params.has_key(name):
error (_('Trying to delete an unknown profile'))
return 1
del 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 ""
default = self.host.memory.getPrivate('Profile_default')
if not default or not default in self.params:
info(_('No default profile, returning first one')) #TODO: manage real default profile
default = self.params.keys()[0]
self.host.memory.setPrivate('Profile_default', default)
return default #FIXME: temporary, must use real default value, and fallback to first one if it doesn't exists
if not self.params.has_key(profile_key):
info (_('Trying to access an unknown profile'))
return ""
return profile_key
def __get_unique_node(self, parent, tag, name):
"""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")
@return: node if it exist or None
"""
for node in parent.childNodes:
if node.nodeName == tag and node.getAttribute("name") == name:
#the node already exists
return node
#the node is new
return None
def importParams(self, xml):
"""import xml in parameters, do nothing if the param already exist
@param xml: parameters in xml form"""
src_dom = minidom.parseString(xml.encode('utf-8'))
def import_node(tgt_parent, src_parent):
for child in src_parent.childNodes:
if child.nodeName == '#text':
continue
node = self.__get_unique_node(tgt_parent, child.nodeName, child.getAttribute("name"))
if not node: #The node is new
tgt_parent.appendChild(child)
else:
import_node(node, child)
import_node(self.dom.documentElement, src_dom.documentElement)
def __default_ok(self, value, name, category):
#FIXME: gof: will not work with individual parameters
self.setParam(name, value, category) #FIXME: better to set param xml value ???
def __default_ko(self, failure, name, category):
error (_("Can't determine default value for [%(category)s/%(name)s]: %(reason)s") % {'category':category, 'name':name, 'reason':str(failure.value)})
def setDefault(self, name, category, callback, errback=None):
"""Set default value of parameter
'default_cb' attibute of parameter must be set to 'yes'
@param name: name of the parameter
@param category: category of the parameter
@param callback: must return a string with the value (use deferred if needed)
@param errback: must manage the error with args failure, name, category
"""
#TODO: send signal param update if value changed
node = self.__getParamNode(name, category, '@ALL@')
if not node:
error(_("Requested param [%(name)s] in category [%(category)s] doesn't exist !") % {'name':name, 'category':category})
return
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", 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 [%(name)s] in category [%(category)s] doesn't exist !") % {'name':name, 'category':category})
return None
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
if attr == "value":
return self.__getParam(profile, category, name) or node[1].getAttribute(attr)
else:
return node[1].getAttribute(attr)
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 __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 getParamsUI(self, profile_key='@DEFAULT@'):
"""Return a SàT XMLUI for parameters, with given profile"""
profile = self.getProfileName(profile_key)
if not profile:
error(_("Asking params for inexistant profile"))
return ""
param_xml = self.getParams(profile)
return paramsXml2xmlUI(param_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"""
#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:
result = node.toxml()
prof_xml.unlink()
return result
prof_xml.unlink()
return "<category />"
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):
"""return the categories availables"""
categories=[]
for cat in self.dom.getElementsByTagName("category"):
categories.append(cat.getAttribute("name"))
return categories
def setParam(self, name, value, category, profile_key='@DEFAULT@'):
"""Set a parameter, return None if the parameter is not in param xml"""
profile = self.getProfileName(profile_key)
if not profile:
error(_('Trying to set parameter for an unknown profile'))
return #TODO: throw an error
node = self.__getParamNode(name, category, '@ALL@')
if not node:
error(_('Requesting an unknown parameter (%(category)s/%(name)s)') % {'category':category, 'name':name})
return
if node[0] == 'general':
self.params_gen[(category, name)] = value
self.host.bridge.paramUpdate(name, value, category, profile) #TODO: add profile in signal
return
assert (node[0] == 'individual')
type = node[1].getAttribute("type")
if type=="button":
print "clique",node.toxml()
else:
self.params[profile][(category, name)] = value
self.host.bridge.paramUpdate(name, value, category, profile) #TODO: add profile in signal
class Memory:
"""This class manage all persistent informations"""
def __init__(self, host):
info (_("Memory manager init"))
self.host = host
self.contacts={}
self.presenceStatus={}
self.subscriptions={}
self.params=Param(host)
self.history={} #used to store chat history (key: short jid)
self.private={} #used to store private value
host.set_const('savefile_history', SAVEFILE_HISTORY)
host.set_const('savefile_private', SAVEFILE_PRIVATE)
self.load()
def load(self):
"""Load parameters and all memory things from file/db"""
param_file_xml = os.path.expanduser(self.host.get_const('local_dir')+
self.host.get_const('savefile_param_xml'))
param_file_data = os.path.expanduser(self.host.get_const('local_dir')+
self.host.get_const('savefile_param_data'))
history_file = os.path.expanduser(self.host.get_const('local_dir')+
self.host.get_const('savefile_history'))
private_file = os.path.expanduser(self.host.get_const('local_dir')+
self.host.get_const('savefile_private'))
#parameters
if os.path.exists(param_file_xml):
try:
self.params.load_xml(param_file_xml)
debug(_("params template loaded"))
except:
error (_("Can't load params template !"))
self.params.load_default_params()
else:
info (_("No params template, using default template"))
self.params.load_default_params()
try:
self.params.load_data(param_file_data)
debug(_("params loaded"))
except:
error (_("Can't load params !"))
#history
if os.path.exists(history_file):
try:
with open(history_file, 'r') as history_pickle:
self.history=pickle.load(history_pickle)
debug(_("history loaded"))
except:
error (_("Can't load history !"))
#private
if os.path.exists(private_file):
try:
with open(private_file, 'r') as private_pickle:
self.private=pickle.load(private_pickle)
debug(_("private values loaded"))
except:
error (_("Can't load private values !"))
def save(self):
"""Save parameters and all memory things to file/db"""
#TODO: need to encrypt files (at least passwords !) and set permissions
param_file_xml = os.path.expanduser(self.host.get_const('local_dir')+
self.host.get_const('savefile_param_xml'))
param_file_data = os.path.expanduser(self.host.get_const('local_dir')+
self.host.get_const('savefile_param_data'))
history_file = os.path.expanduser(self.host.get_const('local_dir')+
self.host.get_const('savefile_history'))
private_file = os.path.expanduser(self.host.get_const('local_dir')+
self.host.get_const('savefile_private'))
self.params.save_xml(param_file_xml)
self.params.save_data(param_file_data)
debug(_("params saved"))
with open(history_file, 'w') as history_pickle:
pickle.dump(self.history, history_pickle)
debug(_("history saved"))
with open(private_file, 'w') as private_pickle:
pickle.dump(self.private, private_pickle)
debug(_("private values saved"))
def getProfilesList(self):
return self.params.getProfilesList()
def getProfileName(self, profile_key):
"""Return name of profile from keyword
@param profile_key: can be the profile name or a keywork (like @DEFAULT@)
@return: profile name or None if it doesn't exist"""
return self.params.getProfileName(profile_key)
def createProfile(self, name):
"""Create a new profile
@param name: Profile name
"""
return self.params.createProfile(name)
def deleteProfile(self, name):
"""Delete an existing profile
@param name: Name of the profile"""
return self.params.deleteProfile(name)
def addToHistory(self, me_jid, from_jid, to_jid, type, message):
me_short=me_jid.userhost()
from_short=from_jid.userhost()
to_short=to_jid.userhost()
if from_jid==me_jid:
key=to_short
else:
key=from_short
if not self.history.has_key(me_short):
self.history[me_short]={}
if not self.history[me_short].has_key(key):
self.history[me_short][key]={}
self.history[me_short][key][int(time.time())] = (from_jid.full(), message)
def getHistory(self, from_jid, to_jid, size):
ret={}
if not self.history.has_key(from_jid):
error(_("source JID not found !"))
#TODO: throw an error here
return {}
if not self.history[from_jid].has_key(to_jid):
error(_("dest JID not found !"))
#TODO: throw an error here
return {}
stamps=self.history[from_jid][to_jid].keys()
stamps.sort()
for stamp in stamps[-size:]:
ret[stamp]=self.history[from_jid][to_jid][stamp]
return ret
def setPrivate(self, key, value):
"""Save a misc private value (mainly useful for plugins)"""
self.private[key] = value
def getPrivate(self, key):
"""return a private value
@param key: name of wanted value
@return: value or None if value don't exist"""
if self.private.has_key(key):
return self.private[key]
return None
def addContact(self, contact_jid, attributes, groups, profile_key='@DEFAULT@'):
debug("Memory addContact: %s",contact_jid.userhost())
profile = self.getProfileName(profile_key)
if not profile:
error (_('Trying to add a contact to a non-existant profile'))
return
assert(isinstance(attributes,dict))
assert(isinstance(groups,set))
if not self.contacts.has_key(profile):
self.contacts[profile] = {}
self.contacts[profile][contact_jid.userhost()]=[attributes, groups]
def delContact(self, contact_jid, profile_key='@DEFAULT@'):
debug("Memory delContact: %s",contact_jid.userhost())
profile = self.getProfileName(profile_key)
if not profile:
error (_('Trying to delete a contact for a non-existant profile'))
return
if self.contacts.has_key(profile) and self.contacts[profile].has_key(contact_jid.userhost()):
del self.contacts[profile][contact_jid.userhost()]
def getContact(self, contact_jid, profile_key='@DEFAULT@'):
profile = self.getProfileName(profile_key)
if not profile:
error(_('Asking a contact for a non-existant profile'))
return None
if self.contacts.has_key(profile) and self.contacts[profile].has_key(contact_jid.userhost()):
self.contacts[profile][contact_jid.userhost()]
else:
return None
def getContacts(self, profile_key='@DEFAULT@'):
"""Return list of contacts for given profile
@param profile_key: profile key
@return list of [contact, attr, groups]"""
debug ("Memory getContact OK (%s)", self.contacts)
profile = self.getProfileName(profile_key)
if not profile:
error(_('Asking contacts for a non-existant profile'))
return []
ret=[]
for contact in self.contacts[profile]:
attr, groups = self.contacts[profile][contact]
ret.append([contact, attr, groups ])
return ret
def addPresenceStatus(self, contact_jid, show, priority, statuses, profile_key='@DEFAULT@'):
profile = self.getProfileName(profile_key)
if not profile:
error(_('Trying to add presence status to a non-existant profile'))
return
if not self.presenceStatus.has_key(profile):
self.presenceStatus[profile] = {}
if not self.presenceStatus[profile].has_key(contact_jid.userhost()):
self.presenceStatus[profile][contact_jid.userhost()] = {}
resource = jid.parse(contact_jid.full())[2] or ''
self.presenceStatus[profile][contact_jid.userhost()][resource] = (show, priority, statuses)
def addWaitingSub(self, type, contact_jid, profile_key):
"""Called when a subcription request is received"""
profile = self.getProfileName(profile_key)
assert(profile)
if not self.subscriptions.has_key(profile):
self.subscriptions[profile] = {}
self.subscriptions[profile][contact_jid] = type
def delWaitingSub(self, contact_jid, profile_key):
"""Called when a subcription request is finished"""
profile = self.getProfileName(profile_key)
assert(profile)
if self.subscriptions.has_key(profile) and self.subscriptions[profile].has_key(contact_jid):
del self.subscriptions[profile][contact_jid]
def getWaitingSub(self, profile_key='@DEFAULT@'):
"""Called to get a list of currently waiting subscription requests"""
profile = self.getProfileName(profile_key)
if not profile:
error(_('Asking waiting subscriptions for a non-existant profile'))
return {}
if not self.subscriptions.has_key(profile):
return {}
return self.subscriptions[profile]
def getPresenceStatus(self, profile_key='@DEFAULT@'):
profile = self.getProfileName(profile_key)
if not profile:
error(_('Asking contacts for a non-existant profile'))
return {}
if not self.presenceStatus.has_key(profile):
self.presenceStatus[profile] = {}
debug ("Memory getPresenceStatus (%s)", self.presenceStatus[profile])
return self.presenceStatus[profile]
def getParamA(self, name, category, attr="value", profile_key="@DEFAULT@"):
return self.params.getParamA(name, category, attr, profile_key)
def getParamsUI(self, profile_key='@DEFAULT@'):
return self.params.getParamsUI(profile_key)
def getParams(self, profile_key='@DEFAULT@'):
return self.params.getParams(profile_key)
def getParamsForCategory(self, category, profile_key='@DEFAULT@'):
return self.params.getParamsForCategory(category, profile_key)
def getParamsCategories(self):
return self.params.getParamsCategories()
def setParam(self, name, value, category, profile_key='@DEFAULT@'):
return self.params.setParam(name, value, category, profile_key)
def importParams(self, xml):
return self.params.importParams(xml)
def setDefault(self, name, category, callback, errback=None):
return self.params.setDefault(name, category, callback, errback)

302
src/tools/xml_tools.py Normal file
View file

@ -0,0 +1,302 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
SAT: a jabber client
Copyright (C) 2009, 2010 Jérôme Poisson (goffi@goffi.org)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from logging import debug, info, error
from xml.dom import minidom
from wokkel import data_form
import pdb
"""This library help manage XML used in SàT (parameters, registration, etc) """
def dataForm2xml(form):
"""Take a data form (xep-0004, Wokkel's implementation) and convert it to a SàT xml"""
form_ui = XMLUI("form", "vertical")
if form.instructions:
form_ui.addText('\n'.join(form.instructions), 'instructions')
labels = filter(lambda field:field.label,form.fieldList)
if labels:
#if there is no label, we don't need to use pairs
form_ui.changeLayout("pairs")
for field in form.fieldList:
if field.fieldType == 'fixed':
__field_type = 'text'
elif field.fieldType == 'text-single':
__field_type = "string"
elif field.fieldType == 'text-private':
__field_type = "password"
elif field.fieldType == 'list-single':
__field_type = "list"
else:
error (u"FIXME FIXME FIXME: Type [%s] is not managed yet by SàT" % field.fieldType)
__field_type = "string"
if labels:
if field.label:
form_ui.addLabel(field.label)
else:
form_ui.addEmpty()
elem = form_ui.addElement(__field_type, field.var, field.value, [option.value for option in field.options])
return form_ui.toXml()
def tupleList2dataForm(values):
"""convert a list of tuples (name,value) to a wokkel submit data form"""
form = data_form.Form('submit')
for value in values:
field = data_form.Field(var=value[0], value=value[1])
form.addField(field)
return form
def paramsXml2xmlUI(xml):
"""Convert the xml for parameter to a SàT XML User Interface"""
params_doc = minidom.parseString(xml.encode('utf-8'))
top = params_doc.documentElement
if top.nodeName != 'params':
error(_('INTERNAL ERROR: parameters xml not valid'))
assert(False)
param_ui = XMLUI("param", "tabs")
for category in top.getElementsByTagName("category"):
name = category.getAttribute('name')
label = category.getAttribute('label')
if not name:
error(_('INTERNAL ERROR: params categories must have a name'))
assert(False)
param_ui.addCategory(name, 'pairs', label=label)
for param in category.getElementsByTagName("param"):
name = param.getAttribute('name')
label = param.getAttribute('label')
if not name:
error(_('INTERNAL ERROR: params must have a name'))
assert(False)
type = param.getAttribute('type')
value = param.getAttribute('value') or None
callback_id = param.getAttribute('callback_id') or None
if type == "button":
param_ui.addEmpty()
else:
param_ui.addLabel(label or name)
param_ui.addElement(name=name, type=type, value=value, callback_id=callback_id)
return param_ui.toXml()
class XMLUI:
"""This class is used to create a user interface (form/window/parameters/etc) using SàT XML"""
def __init__(self, panel_type, layout="vertical", title=None):
"""Init SàT XML Panel
@param panel_type: one of
- window (new window)
- form (formulaire, depend of the frontend, usually a panel with cancel/submit buttons)
- param (parameters, presentatio depend of the frontend)
@param layout: disposition of elements, one of:
- vertical: elements are disposed up to bottom
- horizontal: elements are disposed left to right
- pairs: elements come on two aligned columns
(usually one for a label, the next for the element)
- tabs: elemens are in categories with tabs (notebook)
@param title: title or default if None
"""
if not panel_type in ['window', 'form', 'param']:
error(_("Unknown panel type [%s]") % panel_type)
assert(False)
self.type = panel_type
impl = minidom.getDOMImplementation()
self.doc = impl.createDocument(None, "sat_xmlui", None)
top_element = self.doc.documentElement
top_element.setAttribute("type", panel_type)
if title:
top_element.setAttribute("title", title)
self.parentTabsLayout = None #used only we have 'tabs' layout
self.currentCategory = None #used only we have 'tabs' layout
self.changeLayout(layout)
def __del__(self):
self.doc.unlink()
def __createLayout(self, layout, parent=None):
"""Create a layout element
@param type: layout type (cf init doc)
@parent: parent element or None
"""
if not layout in ['vertical', 'horizontal', 'pairs', 'tabs']:
error (_("Unknown layout type [%s]") % layout)
assert (False)
layout_elt = self.doc.createElement('layout')
layout_elt.setAttribute('type',layout)
if parent != None:
parent.appendChild(layout_elt)
return layout_elt
def __createElem(self, type, name=None, parent = None):
"""Create an element
@param type: one of
- empty: empty element (usefull to skip something in a layout, e.g. skip first element in a PAIRS layout)
- text: text to be displayed in an multi-line area, e.g. instructions
@param name: name of the element or None
@param parent: parent element or None
"""
elem = self.doc.createElement('elem')
if name:
elem.setAttribute('name', name)
elem.setAttribute('type', type)
if parent != None:
parent.appendChild(elem)
return elem
def changeLayout(self, layout):
"""Change the current layout"""
self.currentLayout = self.__createLayout(layout, self.currentCategory if self.currentCategory else self.doc.documentElement)
if layout == "tabs":
self.parentTabsLayout = self.currentLayout
def addEmpty(self, name=None):
"""Add a multi-lines text"""
elem = self.__createElem('empty', name, self.currentLayout)
def addText(self, text, name=None):
"""Add a multi-lines text"""
elem = self.__createElem('text', name, self.currentLayout)
text = self.doc.createTextNode(text)
elem.appendChild(text)
def addLabel(self, text, name=None):
"""Add a single line text, mainly useful as label before element"""
elem = self.__createElem('label', name, self.currentLayout)
elem.setAttribute('value', text)
def addString(self, name=None, value=None):
"""Add a string box"""
elem = self.__createElem('string', name, self.currentLayout)
if value:
elem.setAttribute('value', value)
def addPassword(self, name=None, value=None):
"""Add a password box"""
elem = self.__createElem('password', name, self.currentLayout)
if value:
elem.setAttribute('value', value)
def addTextBox(self, name=None, value=None):
"""Add a string box"""
elem = self.__createElem('textbox', name, self.currentLayout)
if value:
elem.setAttribute('value', value)
def addBool(self, name=None, value="true"):
"""Add a string box"""
assert value in ["true","false"]
elem = self.__createElem('bool', name, self.currentLayout)
elem.setAttribute('value', value)
def addList(self, options, name=None, value=None, style=set()):
"""Add a list of choices"""
styles = set(style)
assert (options)
assert (styles.issubset(['multi']))
elem = self.__createElem('list', name, self.currentLayout)
self.addOptions(options, elem)
if value:
elem.setAttribute('value', value)
for style in styles:
elem.setAttribute(style, 'yes')
def addButton(self, callback_id, name, value, fields_back=[]):
"""Add a button
@param callback: callback which will be called if button is pressed
@param name: name
@param value: label of the button
@fields_back: list of names of field to give back when pushing the button"""
elem = self.__createElem('button', name, self.currentLayout)
elem.setAttribute('callback_id', callback_id)
elem.setAttribute('value', value)
for field in fields_back:
fback_el = self.doc.createElement('field_back')
fback_el.setAttribute('name', field)
elem.appendChild(fback_el)
def addElement(self, type, name = None, value = None, options = None, callback_id = None):
"""Convenience method to add element, the params correspond to the ones in addSomething methods"""
if type == 'empty':
self.addEmpty(name)
elif type == 'text':
assert(value!=None)
self.addText(value, name)
elif type == 'label':
assert(value)
self.addLabel(value)
elif type == 'string':
self.addString(name, value)
elif type == 'password':
self.addPassword(name, value)
elif type == 'textbox':
self.addTextBox(name, value)
elif type == 'bool':
if not value:
value = "true"
self.addBool(name, value)
elif type == 'list':
self.addList(options, name, value)
elif type == 'button':
assert(callback_id and value)
self.addButton(callback_id, name, value)
def addOptions(self, options, parent):
"""Add options to a multi-values element (e.g. list)
@param parent: multi-values element"""
for option in options:
opt = self.doc.createElement('option')
opt.setAttribute('value', option)
parent.appendChild(opt)
def addCategory(self, name, layout, label=None):
"""Add a category to current layout (must be a tabs layout)"""
assert(layout != 'tabs')
if not self.parentTabsLayout:
error(_("Trying to add a category without parent tabs layout"))
assert(False)
if self.parentTabsLayout.getAttribute('type') != 'tabs':
error(_("parent layout of a category is not tabs"))
assert(False)
if not label:
label = name
self.currentCategory = cat = self.doc.createElement('category')
cat.setAttribute('name', name)
cat.setAttribute('label', label)
self.changeLayout(layout)
self.parentTabsLayout.appendChild(cat)
def toXml(self):
"""return the XML representation of the panel"""
return self.doc.toxml()