added action cb mechanism for buttons. Tested with a temporary new user registration button.

This commit is contained in:
Goffi 2009-12-01 04:56:08 +01:00
commit a103e8b41b
11 changed files with 226 additions and 66 deletions

View file

@ -201,6 +201,9 @@ class JP():
#we just accept one file
self.loop.quit()
def actionResult(self, type, id, data):
#FIXME
info ("FIXME: actionResult not implemented")
def wait_file(self):
"""Wait for a file and write it on local dir"""

View file

@ -40,12 +40,14 @@ class QuickApp():
self.bridge.register("paramUpdate", self.paramUpdate)
self.bridge.register("contactDeleted", self.contactDeleted)
self.bridge.register("askConfirmation", self.askConfirmation, "request")
self.bridge.register("actionResult", self.actionResult, "request")
###now we get the essential params###
self.whoami=JID(self.bridge.getParamV("JabberID","Connection"))
self.watched=self.bridge.getParamV("Watched", "Misc").split() #TODO: put this in a plugin
self.whoami=JID(self.bridge.getParamA("JabberID","Connection"))
self.watched=self.bridge.getParamA("Watched", "Misc").split() #TODO: put this in a plugin
## misc ##
self.current_action_ids = set()
self.onlineContact = set() #FIXME: temporary
if self.bridge.isConnected():
@ -156,3 +158,6 @@ class QuickApp():
def askConfirmation(self, type, id, data):
raise NotImplementedError
def actionResult(self, type, id, data):
raise NotImplementedError

View file

@ -64,8 +64,8 @@ class DBusBridgeFrontend(BridgeFrontend):
def setParam(self, name, value, category):
return self.db_comm_iface.setParam(name, value, category)
def getParamV(self, name, category):
return self.db_comm_iface.getParamV(name, category)
def getParamA(self, name, category):
return self.db_comm_iface.getParamA(name, category)
def getParams(self):
return self.db_comm_iface.getParams()
@ -88,6 +88,9 @@ class DBusBridgeFrontend(BridgeFrontend):
def isConnected(self):
return self.db_comm_iface.isConnected()
def launchAction(self, type, data):
return self.db_req_iface.launchAction(type, data)
def confirmationAnswer(self, id, accepted, data):
return self.db_req_iface.confirmationAnswer(id, accepted, data)

View file

@ -273,6 +273,14 @@ class SortilegeApp(QuickApp):
self.editBar.replace_cur()
curses.doupdate()
def askConfirmation(self, type, id, data):
#FIXME
info ("FIXME: askConfirmation not implemented")
def actionResult(self, type, id, data):
#FIXME
info ("FIXME: actionResult not implemented")
def newMessage(self, from_jid, msg, type, to_jid):
QuickApp.newMessage(self, from_jid, msg, type, to_jid)
sender=JID(from_jid)

View file

@ -284,11 +284,46 @@ class MainWindow(wx.Frame, QuickApp):
answer = wx.ID_NO
if answer==wx.ID_NO:
self.bridge.confirmationAnswer(id, False, answer_data)
dlg.Destroy()
elif type == "YES/NO":
debug ("Yes/No confirmation asked")
dlg = wx.MessageDialog(self, data["message"],
'Confirmation',
wx.YES_NO | wx.ICON_QUESTION
)
answer=dlg.ShowModal()
if answer==wx.ID_YES:
self.bridge.confirmationAnswer(id, True, {})
if answer==wx.ID_NO:
self.bridge.confirmationAnswer(id, False, {})
dlg.Destroy()
def actionResult(self, type, id, data):
debug ("actionResult: type = [%s] id = [%s] data =[%s]" % (type, id, data))
if type == "SUPPRESS":
self.current_action_ids.remove(id)
elif type == "SUCCESS":
dlg = wx.MessageDialog(self, data["message"],
'Success',
wx.OK | wx.ICON_INFORMATION
)
dlg.ShowModal()
dlg.Destroy()
elif type == "ERROR":
dlg = wx.MessageDialog(self, data["message"],
'Error',
wx.OK | wx.ICON_ERROR
)
dlg.ShowModal()
dlg.Destroy()
else:
error ("FIXME FIXME FIXME: type [%s] not implemented" % type)
raise NotImplementedError
def progressCB(self, id, title, message):
data = self.bridge.getProgress(id)
@ -340,7 +375,7 @@ class MainWindow(wx.Frame, QuickApp):
def onParam(self, e):
debug("Param request")
param=Param(self.bridge)
param=Param(self)
def onExit(self, e):
self.Close()

View file

@ -28,10 +28,10 @@ from logging import debug, info, error
class Param(wx.Frame):
def __init__(self, bridge, title="Configuration"):
def __init__(self, host, title="Configuration"):
super(Param, self).__init__(None, title=title)
self.bridge = bridge
self.host = host
self.modified={} # dict of modified data (i.e. what we have to save)
@ -46,7 +46,7 @@ class Param(wx.Frame):
self.MakeModal()
for category in self.bridge.getParamsCategories():
for category in self.host.bridge.getParamsCategories():
self.addCategory(category)
self.Show()
@ -55,7 +55,7 @@ class Param(wx.Frame):
panel=wx.Panel(self.notebook)
panel.sizer = wx.BoxSizer(wx.VERTICAL)
cat_dom = minidom.parseString(self.bridge.getParamsForCategory(category))
cat_dom = minidom.parseString(self.host.bridge.getParamsForCategory(category))
for param in cat_dom.documentElement.getElementsByTagName("param"):
name = param.getAttribute("name")
@ -96,8 +96,12 @@ class Param(wx.Frame):
def onButtonClicked(self, event):
"""Called when a paramated is modified"""
print "Button Clicked (%s/%s)" % event.GetEventObject().param_id#TODO: gof: appeler callback
#self.modified[event.GetEventObject().param_id]=event.GetString()
print "Button Clicked (%s/%s)" % event.GetEventObject().param_id
name, category = event.GetEventObject().param_id
data = {"name":name, "category":category}
id = self.host.bridge.launchAction("button", data)
self.host.current_action_ids.add(id)
print "action id:",id
event.Skip()
def onClose(self, event):
@ -105,7 +109,7 @@ class Param(wx.Frame):
debug("close")
#now we save the modifier params
for param in self.modified:
self.bridge.setParam(param[0], self.modified[param], param[1])
self.host.bridge.setParam(param[0], self.modified[param], param[1])
self.MakeModal(False)
event.Skip()

View file

@ -473,7 +473,7 @@ class XEP_0065(XMPPHandler):
host.memory.importParams(self, XEP_0065.params)
host.memory.setDefault("IP", "File Transfert", self.getExternalIP)
port = int(self.host.memory.getParamV("Port", "File Transfert"))
port = int(self.host.memory.getParamA("Port", "File Transfert"))
info("Launching Socks5 Stream server on port %d", port)
reactor.listenTCP(port, self.server_factory)

View file

@ -143,8 +143,8 @@ class XEP_0096(XMPPHandler):
query=offer.addElement('query', 'http://jabber.org/protocol/bytestreams')
query['mode']='tcp'
streamhost=query.addElement('streamhost')
streamhost['host']=self.host.memory.getParamV("IP", "File Transfert")
streamhost['port']=self.host.memory.getParamV("Port", "File Transfert")
streamhost['host']=self.host.memory.getParamA("IP", "File Transfert")
streamhost['port']=self.host.memory.getParamA("Port", "File Transfert")
streamhost['jid']=self.host.me.full()
offer.send()

147
sat.tac
View file

@ -205,6 +205,7 @@ class RegisteringAuthenticator(xmlstream.ConnectAuthenticator):
self.user_login = user_login
self.user_pass = user_pass
self.answer_id = answer_id
print "Registration asked for",user_login, user_pass, jabber_host
def connectionMade(self):
print "connectionMade"
@ -223,33 +224,37 @@ class RegisteringAuthenticator(xmlstream.ConnectAuthenticator):
def registrationAnswer(self, answer):
debug ("registration answer: %s" % answer.toXml())
answer_type = "success"
answer_data={"human":"Registration successfull"}
self.host.bridge.sendAnswer(answer_type, self.answer_id, answer_data)
answer_type = "SUCCESS"
answer_data={"message":"Registration successfull"}
self.host.bridge.actionResult(answer_type, self.answer_id, answer_data)
self.xmlstream.sendFooter()
def registrationFailure(self, error):
info ("Registration failure: %s" %str(error))
answer_type = "error"
answer_data={"human":"Registration failed"}
info ("Registration failure: %s" % str(error.value))
answer_type = "ERROR"
answer_data = {}
if error.value.condition == 'conflict':
answer_data['reason'] = 'conflict'
answer_data={"message":"Username already exists, please choose an other one"}
else:
answer_data['reason'] = 'unknown'
self.host.bridge.sendAnswer(answer_type, self.answer_id, answer_data)
answer_data={"message":"Registration failed"}
self.host.bridge.actionResult(answer_type, self.answer_id, answer_data)
self.xmlstream.sendFooter()
class SAT(service.Service):
def __init__(self):
#self.reactor=reactor
self.memory=Memory()
self.server_features=[] #XXX: temp dic, need to be transfered into self.memory in the future
self._waiting_conf = {} #callback called when a confirmation is received
self._progress_cb_map = {} #callback called when a progress is requested (key = progress id)
#TODO: standardize callback system
self.__waiting_conf = {} #callback called when a confirmation is received
self.__progress_cb_map = {} #callback called when a progress is requested (key = progress id)
self.__general_cb_map = {} #callback called for general reasons (key = name)
self.__private_data = {} #used for internal callbacks (key = id)
self.plugins = {}
self.memory=Memory(self)
self.server_features=[] #XXX: temp dic, need to be transfered into self.memory in the future
self.bridge=DBusBridge()
self.bridge.register("registerNewAccount", self.registerNewAccount)
@ -259,7 +264,7 @@ class SAT(service.Service):
self.bridge.register("getPresenceStatus", self.memory.getPresenceStatus)
self.bridge.register("sendMessage", self.sendMessage)
self.bridge.register("setParam", self.setParam)
self.bridge.register("getParamV", self.memory.getParamV)
self.bridge.register("getParamA", self.memory.getParamA)
self.bridge.register("getParams", self.memory.getParams)
self.bridge.register("getParamsForCategory", self.memory.getParamsForCategory)
self.bridge.register("getParamsCategories", self.memory.getParamsCategories)
@ -268,6 +273,7 @@ class SAT(service.Service):
self.bridge.register("addContact", self.addContact)
self.bridge.register("delContact", self.delContact)
self.bridge.register("isConnected", self.isConnected)
self.bridge.register("launchAction", self.launchAction)
self.bridge.register("confirmationAnswer", self.confirmationAnswer)
self.bridge.register("getProgress", self.getProgress)
@ -295,9 +301,9 @@ class SAT(service.Service):
info("already connected !")
return
print "connecting..."
self.me = jid.JID(self.memory.getParamV("JabberID", "Connection"))
self.xmppclient = SatXMPPClient(self.me, self.memory.getParamV("Password", "Connection"),
self.memory.getParamV("Server", "Connection"), 5222)
self.me = jid.JID(self.memory.getParamA("JabberID", "Connection"))
self.xmppclient = SatXMPPClient(self.me, self.memory.getParamA("Password", "Connection"),
self.memory.getParamA("Server", "Connection"), 5222)
self.xmppclient.streamInitialized = self.streamInitialized
self.messageProt = SatMessageProtocol(self)
@ -362,27 +368,52 @@ class SAT(service.Service):
self.presence.available()
self.disco.requestInfo(jid.JID(self.memory.getParamV("Server", "Connection"))).addCallback(self.serverDisco)
self.disco.requestInfo(jid.JID(self.memory.getParamA("Server", "Connection"))).addCallback(self.serverDisco)
## Misc methods ##
def registerNewAccount(self, login, password, server, port = 5222):
def registerNewAccount(self, login, password, server, port = 5222, id = None):
"""Connect to a server and create a new account using in-band registration"""
next_id = sat_next_id() #the id is used to send server's answer
next_id = id or sat_next_id() #the id is used to send server's answer
serverRegistrer = xmlstream.XmlStreamFactory(RegisteringAuthenticator(self, server, login, password, next_id))
connector = reactor.connectTCP(server, port, serverRegistrer)
serverRegistrer.clientConnectionLost = lambda conn, reason: connector.disconnect()
return next_id
def registerNewAccountCB(self, id, data):
user = jid.parse(self.memory.getParamA("JabberID", "Connection"))[0]
server = self.memory.getParamA("Server", "Connection")
confirm_id = sat_next_id()
self.__private_data[confirm_id]=id
self.askConfirmation(confirm_id, "YES/NO",
{"message":"Are you sure to register new account [%s] to server %s ?" % (user, server)},
self.regisConfirmCB)
print ("===============+++++++++++ REGISTER NEW ACCOUNT++++++++++++++============")
print "id=",id
print "data=",data
def regisConfirmCB(self, id, accepted, data):
print "register Confirmation CB ! (%s)" % str(accepted)
action_id = self.__private_data[id]
del self.__private_data[id]
user = jid.parse(self.memory.getParamA("JabberID", "Connection"))[0]
password = self.memory.getParamA("Password", "Connection")
server = self.memory.getParamA("Server", "Connection")
if accepted:
self.registerNewAccount(user, password, server, id=action_id)
else:
self.actionResult(action_id, "SUPPRESS", {})
## Client management ##
def setParam(self, name, value, category):
"""set wanted paramater and notice observers"""
info ("setting param: %s=%s in category %s", name, value, category)
self.memory.setParam(name, value, category)
self.bridge.paramUpdate(name, value, category)
def failed(self,xmlstream):
debug("failed: %s", xmlstream.getErrorMessage())
@ -397,6 +428,27 @@ class SAT(service.Service):
pass
return False
def launchAction(self, type, data):
"""Launch a specific action asked by client
@param type: action type (button)
@data: needed data to launch the action
@return: action id for result, or empty string in case or error
"""
if type=="button":
try:
cb_name = self.memory.getParamA(data["name"], data["category"], "callback")
except KeyError:
error ("Incomplete data")
return ""
id = sat_next_id()
self.callGeneralCB(cb_name, id, data)
return id
else:
error ("Unknown action type")
return ""
## jabber methods ##
def sendMessage(self,to,msg,type='chat'):
@ -457,36 +509,51 @@ class SAT(service.Service):
debug ("Identity found: [%s/%s] %s" % (cat, type, disco.identities[(cat,type)]))
## Generic HMI ##
def actionResult(self, id, type, data):
"""Send the result of an action
@param id: same id used with action
@type: result type ("PARAM", "SUCCESS", "ERROR")
@data: data (depend of result type)
"""
self.bridge.actionResult(type, id, data)
def askConfirmation(self, id, type, data, cb):
"""Add a confirmation callback"""
if self._waiting_conf.has_key(id):
"""Add a confirmation callback
@param id: id used to get answer
@type: confirmation type ("YES/NO", "FILE_TRANSFERT")
@data: data (depend of confirmation type)
@cb: callback called with the answer
"""
if self.__waiting_conf.has_key(id):
error ("Attempt to register two callbacks for the same confirmation")
else:
self._waiting_conf[id] = cb
self.__waiting_conf[id] = cb
self.bridge.askConfirmation(type, id, data)
def confirmationAnswer(self, id, accepted, data):
"""Called by frontends to answer confirmation requests"""
debug ("Received confirmation answer for id [%s]: %s", id, "accepted" if accepted else "refused")
if not self._waiting_conf.has_key(id):
if not self.__waiting_conf.has_key(id):
error ("Received an unknown confirmation")
else:
cb = self._waiting_conf[id]
del self._waiting_conf[id]
cb = self.__waiting_conf[id]
del self.__waiting_conf[id]
cb(id, accepted, data)
def registerProgressCB(self, id, CB):
"""Register a callback called when progress is requested for id"""
self._progress_cb_map[id] = CB
self.__progress_cb_map[id] = CB
def removeProgressCB(self, id):
"""Remove a progress callback"""
if not self._progress_cb_map.has_key(id):
if not self.__progress_cb_map.has_key(id):
error ("Trying to remove an unknow progress callback")
else:
del self._progress_cb_map[id]
del self.__progress_cb_map[id]
def getProgress(self, id):
"""Return a dict with progress information
@ -495,12 +562,30 @@ class SAT(service.Service):
"""
data = {}
try:
self._progress_cb_map[id](data)
self.__progress_cb_map[id](data)
except KeyError:
pass
#debug("Requested progress for unknown id")
return data
def registerGeneralCB(self, name, CB):
"""Register a callback called for general reason"""
self.__general_cb_map[name] = CB
def removeGeneralCB(self, name):
"""Remove a general callback"""
if not self.__general_cb_map.has_key(name):
error ("Trying to remove an unknow general callback")
else:
del self.__general_cb_map[name]
def callGeneralCB(self, name, *args, **kwargs):
"""Call general function back"""
try:
return self.__general_cb_map[name](*args, **kwargs)
except KeyError:
error("Trying to call unknown function")
return None
application = service.Application('SàT')
service = SAT()

View file

@ -73,10 +73,10 @@ class DbusObject(dbus.service.Object):
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_COMM_SUFFIX,
@dbus.service.signal(const_INT_PREFIX+const_REQ_SUFFIX,
signature='ssa{ss}')
def sendAnswer(self, type, id, data):
debug("sending answer: id = [%s] type = %s data = %s", id, type, data)
def actionResult(self, type, id, data):
debug("result of action: id = [%s] type = %s data = %s", id, type, data)
### methods ###
@ -128,8 +128,8 @@ class DbusObject(dbus.service.Object):
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='ss', out_signature='s')
def getParamV(self, name, category="default"):
return self.cb["getParamV"](name, category)
def getParamA(self, name, category="default"):
return self.cb["getParamA"](name, category)
@dbus.service.method(const_INT_PREFIX+const_COMM_SUFFIX,
in_signature='', out_signature='s')
@ -170,6 +170,11 @@ class DbusObject(dbus.service.Object):
debug("Connection status requested")
return self.cb["isConnected"]()
@dbus.service.method(const_INT_PREFIX+const_REQ_SUFFIX,
in_signature='sa{ss}', out_signature='s')
def launchAction(self, type, data):
return self.cb["launchAction"](type, data)
@dbus.service.method(const_INT_PREFIX+const_REQ_SUFFIX,
in_signature='sba{ss}', out_signature='')
def confirmationAnswer(self, id, accepted, data):
@ -247,8 +252,8 @@ class DBusBridge(Bridge):
def askConfirmation(self, type, id, data):
self.dbus_bridge.askConfirmation(type, id, data)
def sendAnswer(self, type, id, data):
self.dbus_bridge.sendAnswer(type, id, data)
def actionResult(self, type, id, data):
self.dbus_bridge.actionResult(type, id, data)
def register(self, name, callback):
debug("registering DBus bridge method [%s]",name)

View file

@ -54,9 +54,11 @@ class Param():
def load_default_params(self):
self.dom = minidom.parseString(Param.default_xml)
def __init__(self):
def __init__(self, host):
debug("Parameters init")
self.host = host
self.load_default_params()
host.registerGeneralCB("registerNewAccount", host.registerNewAccountCB)
def __get_unique_node(self, parent, tag, name):
"""return node with given tag, create a new one if the node doesn't exist
@ -79,7 +81,6 @@ class Param():
src_dom = minidom.parseString(xml)
def import_node(tgt_parent, src_parent):
print "import_node [%s, %s]" % (tgt_parent.nodeName, src_parent.nodeName)
for child in src_parent.childNodes:
if child.nodeName == '#text':
continue
@ -105,6 +106,7 @@ class Param():
@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)
if not node:
error("Requested param [%s] in category [%s] doesn't exist !" , name, category)
@ -115,14 +117,18 @@ class Param():
d.addCallback(self.__default_ok, name, category)
d.addErrback(errback or self.__default_ko, name, category)
def getParamV(self, name, category):
"""Helper method to get the value in the good type
getParamV stands for GetParamValue"""
def getParamA(self, name, category, attr="value"):
"""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")
@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("value")
return node.getAttribute(attr)
def getParams(self):
"""Return the whole params XML"""
@ -155,16 +161,21 @@ class Param():
node = self.__getParamNode(name, category)
if not node:
return #TODO: throw an error
node.setAttribute("value", value)
type = node.getAttribute("type")
if type=="button":
print "clique",node.toxml()
else:
node.setAttribute("value", value)
self.host.bridge.paramUpdate(name, value, category)
class Memory:
"""This class manage all persistent informations"""
def __init__(self):
def __init__(self, host):
info ("Memory manager init")
self.contact={}
self.presenceStatus={}
self.params=Param()
self.params=Param(host)
self.history={}
self.disco={} #XXX: maybe best in a separate class
self.features={}
@ -176,6 +187,7 @@ class Memory:
#first parameters
if os.path.exists(const_SAVEFILE_PARAM):
try:
#gof: FIXME FIXME FIXME: sauver l'xml et non plus le pickle !!!!!!!
with open(const_SAVEFILE_PARAM, 'r') as params_pickle:
self.params=pickle.load(params_pickle)
debug("params loaded")
@ -260,8 +272,8 @@ class Memory:
debug ("Memory getPresenceStatus (%s)", status)
return status
def getParamV(self, name, category):
return self.params.getParamV(name, category)
def getParamA(self, name, category, attr="value"):
return self.params.getParamA(name, category, attr)
def getParams(self):
return self.params.getParams()