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()