Mercurial > libervia-backend
changeset 33:b9bb5d8e0cc7
In-band-registration: data form 2 xml conversion
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 08 Dec 2009 09:54:44 +0100 |
parents | c4badbf3dd97 |
children | a544b376b6f0 |
files | frontends/sortilege/sortilege.py frontends/wix/main_window.py frontends/wix/wix.py plugins/plugin_xep_0077.py sat.tac tools/xml_tools.py |
diffstat | 6 files changed, 63 insertions(+), 437 deletions(-) [+] |
line wrap: on
line diff
--- a/frontends/sortilege/sortilege.py Tue Dec 08 08:13:12 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,387 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -""" -sortilege: a SAT frontend -Copyright (C) 2009 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/>. -""" - - -import curses -import pdb -from window import Window -from editbox import EditBox -from statusbar import StatusBar -from chat import Chat -from tools.jid import JID -import logging -from logging import debug, info, error -import locale -import sys, os -import gobject -import time -from curses import ascii -import locale -from signal import signal, SIGWINCH -import fcntl -import struct -import termios -from boxsizer import BoxSizer -from quick_frontend.quick_chat_list import QuickChatList -from quick_frontend.quick_contact_list import QuickContactList -from quick_frontend.quick_app import QuickApp - -### logging configuration FIXME: put this elsewhere ### -logging.basicConfig(level=logging.CRITICAL, #TODO: configure it top put messages in a log file - format='%(message)s') -### - -const_APP_NAME = "Sortilège" -const_CONTACT_WIDTH = 30 - -def ttysize(): - """This function return term size. - Comes from Donn Cave from python list mailing list""" - buf = 'abcdefgh' - buf = fcntl.ioctl(0, termios.TIOCGWINSZ, buf) - row, col, rpx, cpx = struct.unpack('hhhh', buf) - return row, col - -def C(k): - """return the value of Ctrl+key""" - return ord(ascii.ctrl(k)) - -class ChatList(QuickChatList): - """This class manage the list of chat windows""" - - def __init__(self, host): - QuickChatList.__init__(self, host) - self.sizer=host.sizer - - def createChat(self, name): - chat = Chat(name, self.host) - self.sizer.appendColum(0,chat) - self.sizer.update() - return chat - - -class ContactList(Window, QuickContactList): - - def __init__(self): - QuickContactList.__init__(self) - self.__index=0 #indicate which contact is selected (ie: where we are) - Window.__init__(self, stdscr, stdscr.getmaxyx()[0]-2,const_CONTACT_WIDTH,0,0, True, "Contact List", code=code) - - def resize(self, height, width, y, x): - Window.resize(self, height, width, y, x) - self.update() - - def resizeAdapt(self): - """Adapt window size to stdscr size. - Must be called when stdscr is resized.""" - self.resize(stdscr.getmaxyx()[0]-2,const_CONTACT_WIDTH,0,0) - self.update() - - def registerEnterCB(self, CB): - self.__enterCB=CB - - def replace(self, jid, name="", show="", status="", group=""): - """add a contact to the list""" - self.jid_ids[jid] = name or jid - self.update() - - def indexUp(self): - """increment select contact index""" - if self.__index < len(self.jid_ids)-1: #we dont want to select a missing contact - self.__index = self.__index + 1 - self.update() - - def indexDown(self): - """decrement select contact index""" - if self.__index > 0: - self.__index = self.__index - 1 - self.update() - - def remove(self, jid): - """remove a contact from the list""" - del self.jid_ids[jid] - if self.__index >= len(self.jid_ids) and self.__index > 0: #if select index is out of border, we put it on the last contact - self.__index = len(self.jid_ids)-1 - self.update() - - def update(self): - """redraw all the window""" - if self.isHidden(): - return - Window.update(self) - viewList=[] - for jid in self.jid_ids: - viewList.append(self.jid_ids[jid]) - viewList.sort() - begin=0 if self.__index<self.rHeight else self.__index-self.rHeight+1 - idx=0 - for item in viewList[begin:self.rHeight+begin]: - attr = curses.A_REVERSE if ( self.isActive() and (idx+begin) == self.__index ) else 0 - centered = item.center(self.rWidth) ## it's nicer in the center :) - self.addYXStr(idx, 0, centered, attr) - idx = idx + 1 - - self.noutrefresh() - - def handleKey(self, k): - if k == curses.KEY_UP: - self.indexDown() - elif k == curses.KEY_DOWN: - self.indexUp() - elif k == ascii.NL: - if not self.jid_ids: - return - try: - self.__enterCB(self.jid_ids.keys()[self.__index]) - except NameError: - pass # TODO: thrown an error here - -class SortilegeApp(QuickApp): - - def __init__(self): - #debug(const_APP_NAME+" init...") - - ## unicode support ## - locale.setlocale(locale.LC_ALL, '') - global code - code = locale.getpreferredencoding() - self.code=code - - ## main loop setup ## - self.loop=gobject.MainLoop() - gobject.io_add_watch(0, gobject.IO_IN, self.loopCB) - - ## misc init stuff ## - self.listWins=[] - self.chatParams={'timestamp':True, - 'color':True, - 'short_nick':False} - - def start(self): - curses.wrapper(self.start_curses) - - def start_curses(self, win): - global stdscr - stdscr = win - self.stdscr = stdscr - curses.raw() #we handle everything ourself - curses.curs_set(False) - stdscr.nodelay(True) - - ## colours ## - self.color(True) - - ## windows ## - self.contactList = ContactList() - self.editBar = EditBox(stdscr, "> ", self.code) - self.editBar.activate(False) - self.statusBar = StatusBar(stdscr, self.code) - self.statusBar.hide(True) - self.addWin(self.contactList) - self.addWin(self.editBar) - self.addWin(self.statusBar) - self.sizer=BoxSizer(stdscr) - self.sizer.appendRow(self.contactList) - self.sizer.appendRow(self.statusBar) - self.sizer.appendRow(self.editBar) - self.currentChat=None - - self.contactList.registerEnterCB(self.onContactChoosed) - self.editBar.registerEnterCB(self.onTextEntered) - - self.chat_wins=ChatList(self) - - QuickApp.__init__(self) #XXX: yes it's an unusual place for the constructor of a parent class, but the init order is important - - signal (SIGWINCH, self.onResize) #we manage SIGWINCH ourselves, because the loop is not called otherwise - - #last but not least, we adapt windows' sizes - self.sizer.update() - self.editBar.replace_cur() - curses.doupdate() - - self.loop.run() - - def addWin(self, win): - self.listWins.append(win) - - def color(self, activate=True): - if activate: - debug ("Activation des couleurs") - curses.init_pair(1, curses.COLOR_BLUE, curses.COLOR_BLACK) - curses.init_pair(2, curses.COLOR_RED, curses.COLOR_BLACK) - else: - debug ("Desactivation des couleurs") - curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLACK) - curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_BLACK) - - - def showChat(self, chat): - debug ("show chat") - if self.currentChat: - debug ("hide de %s", self.currentChat) - self.chat_wins[self.currentChat].hide() - self.currentChat=chat - debug ("show de %s", self.currentChat) - self.chat_wins[self.currentChat].show() - self.chat_wins[self.currentChat].update() - - - ### EVENTS ### - - def onContactChoosed(self, jid_txt): - """Called when a contact is selected in contact list.""" - jid=JID(jid_txt) - debug ("contact choose: %s", jid) - self.showChat(jid.short) - self.statusBar.remove_item(jid.short) - if len(self.statusBar)==0: - self.statusBar.hide() - self.sizer.update() - - - def onTextEntered(self, text): - jid=JID(self.whoami) - self.bridge.sendMessage(self.currentChat, text) - - def showDialog(self, message, title, type="info"): - if type==question: - raise NotImplementedError - pass - - - def presenceUpdate(self, jabber_id, type, show, status, priority): - QuickApp.presenceUpdate(self, jabber_id, type, show, status, priority) - 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) - addr=JID(to_jid) - win = addr if sender.short == self.whoami.short else sender #FIXME: duplicate code with QuickApp - if (self.currentChat==None): - self.currentChat=win.short - self.showChat(win.short) - - # we show the window in the status bar - if not self.currentChat == win.short: - self.statusBar.add_item(win.short) - self.statusBar.show() - self.sizer.update() - self.statusBar.update() - - self.editBar.replace_cur() - curses.doupdate() - - def onResize(self, sig, stack): - """Called on SIGWINCH. - resize the screen and launch the loop""" - height, width = ttysize() - curses.resizeterm(height, width) - gobject.idle_add(self.callOnceLoop) - - def callOnceLoop(self): - """Call the loop and return false (for not being called again by gobject mainloop). - Usefull for calling loop when there is no input in stdin""" - self.loopCB() - return False - - def __key_handling(self, k): - """Handle key and transmit to active window.""" - - ### General keys, handled _everytime_ ### - if k == C('x'): - if os.getenv('TERM')=='screen': - os.system('screen -X remove') - else: - self.loop.quit() - - ## windows navigation - elif k == C('l') and not self.contactList.isHidden(): - """We go to the contact list""" - self.contactList.activate(not self.contactList.isActive()) - if self.currentChat: - self.editBar.activate(not self.contactList.isActive()) - - elif k == curses.KEY_F2: - self.contactList.hide(not self.contactList.isHidden()) - if self.contactList.isHidden(): - self.contactList.activate(False) #TODO: auto deactivation when hiding ? - if self.currentChat: - self.editBar.activate(True) - self.sizer.update() - - ## Chat Params ## - elif k == C('c'): - self.chatParams["color"] = not self.chatParams["color"] - self.color(self.chatParams["color"]) - elif k == C('t'): - self.chatParams["timestamp"] = not self.chatParams["timestamp"] - self.chat_wins[self.currentChat].update() - elif k == C('s'): - self.chatParams["short_nick"] = not self.chatParams["short_nick"] - self.chat_wins[self.currentChat].update() - - ## misc ## - elif k == curses.KEY_RESIZE: - stdscr.erase() - height, width = stdscr.getmaxyx() - if height<5 and width<35: - stdscr.addstr("Pleeeeasse, I can't even breathe !") - else: - for win in self.listWins: - win.resizeAdapt() - for win in self.chat_wins.keys(): - self.chat_wins[win].resizeAdapt() - self.sizer.update() # FIXME: everything need to be managed by the sizer - - ## we now throw the key to win handlers ## - else: - for win in self.listWins: - if win.isActive(): - win.handleKey(k) - if self.currentChat: - self.chat_wins[self.currentChat].handleKey(k) - - def loopCB(self, source="", cb_condition=""): - """This callback is called by the main loop""" - #pressed = self.contactList.window.getch() - pressed = stdscr.getch() - if pressed != curses.ERR: - self.__key_handling(pressed) - self.editBar.replace_cur() - curses.doupdate() - - - return True - - -sat = SortilegeApp() -sat.start()
--- a/frontends/wix/main_window.py Tue Dec 08 08:13:12 2009 +0100 +++ b/frontends/wix/main_window.py Tue Dec 08 09:54:44 2009 +0100 @@ -327,6 +327,9 @@ ) dlg.ShowModal() dlg.Destroy() + elif type == "FORM": + self.current_action_ids.remove(id) + info ("Form received !") elif type == "DICT_DICT": self.current_action_ids.remove(id) if self.current_action_ids_cb.has_key(id):
--- a/frontends/wix/wix.py Tue Dec 08 08:13:12 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -""" -wix: a SAT frontend -Copyright (C) 2009 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/>. -""" - - -import wx -from sat_bridge_frontend.DBus import DBusBridgeFrontend -import pdb -import logging -from logging import debug, info, error -from main_window import MainWindow - -### logging configuration FIXME: put this elsewhere ### -logging.basicConfig(level=logging.DEBUG, - format='%(message)s') -### - - -class SATApp(wx.App): - def __init__(self, redirect=False, filename=None, useBestVisual=False, clearSigInt=True): - super(SATApp,self).__init__(redirect, filename, useBestVisual, clearSigInt) - - def OnInit(self): - self.main = MainWindow() - self.main.Show(True) - self.SetTopWindow(self.main) - return True - - -sat = SATApp() -sat.MainLoop()
--- a/plugins/plugin_xep_0077.py Tue Dec 08 08:13:12 2009 +0100 +++ b/plugins/plugin_xep_0077.py Tue Dec 08 09:54:44 2009 +0100 @@ -23,8 +23,11 @@ from twisted.words.protocols.jabber import client, jid, xmlstream, error from twisted.words.protocols.jabber.xmlstream import IQ from twisted.internet import reactor +from tools.xml_tools import XMLTools import pdb +from wokkel import data_form + NS_REG = 'jabber:iq:register' PLUGIN_INFO = { @@ -46,6 +49,9 @@ def reg_ok(self, answer): """Called after the first get IQ""" print "answer:",answer + form = data_form.Form.fromElement(answer.firstChildElement().firstChildElement()) + xml_data = XMLTools.dataForm2xml(form) + self.host.bridge.actionResult("FORM", answer['id'], {"type":"registration", "xml":xml_data}) def reg_err(self, failure): """Called when something is wrong with registration"""
--- a/sat.tac Tue Dec 08 08:13:12 2009 +0100 +++ b/sat.tac Tue Dec 08 09:54:44 2009 +0100 @@ -524,7 +524,7 @@ 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") + @type: result type ("PARAM", "SUCCESS", "ERROR", "FORM") @data: dictionary """ self.bridge.actionResult(type, id, data) @@ -532,7 +532,7 @@ def actionResultExt(self, id, type, data): """Send the result of an action, extended version @param id: same id used with action - @type: result type ("PARAM", "SUCCESS", "ERROR") + @type: result type /!\ only "DICT_DICT" for this method @data: dictionary of dictionaries """ if type != "DICT_DICT":
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xml_tools.py Tue Dec 08 09:54:44 2009 +0100 @@ -0,0 +1,52 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +""" +SAT: a jabber client +Copyright (C) 2009 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 +import pdb + +class XMLTools: + """This class help manage XML used in SàT (parameters, registration, etc) """ + + + @staticmethod + def dataForm2xml(form): + """Take a data form (xep-0004, Wokkel's implementation) and convert it to a SàT xml""" + result_xml = ["<form>", "</form>"] + if form.instructions: + result_xml.insert(1,"<elem name='instructions' value='%s' type='text' />" % '\n'.join(form.instructions)) + for field in form.fieldList: + if field.fieldType == 'text-single': + __field_type = "string" + elif field.fieldType == 'text-private': + __field_type = "password" + else: + error (u"FIXME FIXME FIXME: Type [%s] is not managed yet by SàT" % field.fieldType) + __field_type = "string_field" + + result_xml.insert(-1,"<elem name='%s' type='%s' label='%s'>" % (field.var, __field_type, field.label)) + + return '\n'.join(result_xml) + + + + + pdb.set_trace()