changeset 55:d5266c41ca24

Roster list update, contact deletion + some refactoring
author Goffi <goffi@goffi.org>
date Sun, 29 May 2011 02:13:53 +0200
parents f25c4077f6b9
children 5fa710058e2d
files browser_side/card_game.py browser_side/contact.py browser_side/dialog.py browser_side/panels.py libervia.py libervia.tac
diffstat 6 files changed, 140 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/browser_side/card_game.py	Sat May 28 20:18:14 2011 +0200
+++ b/browser_side/card_game.py	Sun May 29 02:13:53 2011 +0200
@@ -39,7 +39,7 @@
 from pyjamas.dnd import makeDraggable
 from pyjamas.ui.DragWidget import DragWidget, DragContainer
 from jid import JID
-from dialog import ConfirmDialog, SimpleDialog
+from dialog import ConfirmDialog, InfoDialog
 from tools import html_sanitize
 from datetime import datetime
 from time import time
@@ -414,4 +414,4 @@
             else:
                 title = "You <b>loose</b> :("
         body = re.sub(r'<.*?>',lambda x:'<br />' if '/elem' in x.group(0) else '', xml_data) #Q&D conversion to simple HTML text
-        SimpleDialog(title, body, _new_game).show()
+        InfoDialog(title, body, _new_game).show()
--- a/browser_side/contact.py	Sat May 28 20:18:14 2011 +0200
+++ b/browser_side/contact.py	Sun May 29 02:13:53 2011 +0200
@@ -90,31 +90,53 @@
         _item.addMouseListener(self._parent)
         DOM.setStyleAttribute(_item.getElement(), "cursor", "pointer")
         VerticalPanel.add(self, _item)
+
+    def remove(self, group):
+        for wid in self:
+            if isinstance(wid, GroupLabel) and wid.group == group:
+                VerticalPanel.remove(self, wid)
     
 class ContactList(VerticalPanel):
 
     def __init__(self):
         VerticalPanel.__init__(self)
-        self.contacts=[]
+        self.contacts = set()
 
-    def __iter__(self):
-        return self.contacts.__iter__()
-    
     def add(self, jid, name=None):
+        if jid in self.contacts:
+            return
+        self.contacts.add(jid)
         _item = ContactLabel(jid, name)
         DOM.setStyleAttribute(_item.getElement(), "cursor", "pointer")
         VerticalPanel.add(self, _item)
-        self.contacts.append(_item)
+
+    def remove(self, jid):
+        wid = self.getContactLabel(jid)
+        if not wid:
+            return
+        VerticalPanel.remove(self, wid)
+        self.contacts.remove(jid)
+
+    def isContactPresent(self, contact_jid):
+        """Return True if a contact is present in the panel"""
+        return contact_jid in self.contacts
+
+    def getContacts(self):
+        return self.contacts
+
+    def getContactLabel(self, contact_jid):
+        """get contactList widget of a contact
+        @return: ContactLabel item if present, else None"""
+        for wid in self:
+            if isinstance(wid, ContactLabel) and wid.jid == contact_jid:
+                return wid
+        return None
 
     def setState(self, jid, state):
         """Change the appearance of the contact, according to the state
         @param jid: jid which need to change state
         @param state: 'unavailable' if not connected, else presence like RFC6121 #4.7.2.1"""
-        _item = None
-        for contact in self.contacts:
-            if contact.jid == jid:
-                _item = contact
-                break
+        _item = self.getContactLabel(jid)
         if _item:
             if state == 'unavailable':
                 _item.removeStyleName('contactConnected')
@@ -151,19 +173,40 @@
         self.add(self.vPanel)
         self.setStyleName('contactBox')
 
-    def addContact(self, jid, attributes, groups):
-        """Add a contact to the panel
+    def updateContact(self, jid, attributes, groups):
+        """Add a contact to the panel if it doesn't exist, update it else
         @param jid: jid
         @attributes: cf SàT Bridge API's newContact
         @param groups: list of groups"""
-        for group in groups:
+        _current_groups = self.getContactGroups(jid)
+        _new_groups = set(groups)
+        _key = "@%s: "
+
+        for group in _current_groups.difference(_new_groups):
+            #We remove the contact from the groups where he isn't anymore
+            self.groups[group].remove(jid)
+            if not self.groups[group]:
+                #The group is now empty, we must remove it
+                del self.groups[group]
+                self._groupList.remove(group)
+                self.host.uni_box.removeKey(_key % group)
+
+        for group in _new_groups.difference(_current_groups):
+            #We add the contact to the groups he joined
             if not self.groups.has_key(group):
                 self.groups[group] = set()
                 self._groupList.add(group)
-                self.host.uni_box.addKey("@%s: " % group)
+                self.host.uni_box.addKey(_key % group)
             self.groups[group].add(jid)
+        
+        #We add the contact to contact list, it will check if contact already exists
         self._contact_list.add(jid)
 
+    def removeContact(self, jid):
+        """Remove contacts from groups where he is and contact list"""
+        self.updateContact(jid, {}, []) #we remove contact from every group
+        self._contact_list.remove(jid)
+
     def setConnected(self, jid, resource, availability, priority, statuses):
         """Set connection status"""
         if availability=='unavailable':
@@ -182,12 +225,23 @@
         """return a list of all jid (bare jid) connected"""
         return self.connected.keys()
 
+    def getContactGroups(self, contact_jid):
+        """Get groups where contact is
+       @param group: string of single group, or list of string
+       @param contact_jid: jid to test
+        """
+        result=set()
+        for group in self.groups:
+            if self.isContactInGroup(group, contact_jid):
+                result.add(group)
+        return result
+
     def isContactInGroup(self, group, contact_jid):
        """Test if the contact_jid is in the group
        @param group: string of single group, or list of string
        @param contact_jid: jid to test
        @return: True if contact_jid is in on of the groups"""
-       if self.groups.has_key(group) and contact_jid in self.groups[group]:
+       if group in self.groups and contact_jid in self.groups[group]:
            return True
        return False
 
@@ -198,6 +252,9 @@
                 return True
         return False
 
+    def getContacts(self):
+        return self._contact_list.getContacts()
+
     def getGroups(self):
         return self.groups.keys()
 
--- a/browser_side/dialog.py	Sat May 28 20:18:14 2011 +0200
+++ b/browser_side/dialog.py	Sun May 29 02:13:53 2011 +0200
@@ -87,9 +87,9 @@
     def onCancel(self):
         self.hide()
 
-class ConfirmDialog(DialogBox):
+class GenericConfirmDialog(DialogBox):
 
-    def __init__(self, callback, text='Are you sure ?'):
+    def __init__(self, widgets, callback, title='Confirmation'):
         """
         Dialog to confirm an action
         @param callback: method to call when contacts have been choosed
@@ -99,12 +99,14 @@
 
         content = VerticalPanel()
         content.setWidth('100%')
+        for wid in widgets:
+            content.add(wid)
         button_panel = HorizontalPanel()
         self.confirm_button = Button("OK", self.onConfirm)
         button_panel.add(self.confirm_button)
         button_panel.add(Button("Cancel", self.onCancel))
         content.add(button_panel)
-        self.setHTML(text)
+        self.setHTML(title)
         self.setWidget(content)	
     
     def onConfirm(self):
@@ -115,7 +117,12 @@
         self.hide()
         self.callback(False)
 
-class InfoDialog(DialogBox):
+class ConfirmDialog(GenericConfirmDialog):
+
+    def __init__(self, callback, text='Are you sure ?', title='Confirmation'):
+        GenericConfirmDialog.__init__(self,[HTML(text)], callback, title)
+
+class GenericDialog(DialogBox):
     """Dialog which just show a widget and a close button"""
 
     def __init__(self, title, widget, callback = None):
@@ -141,10 +148,10 @@
         if self.callback:
             self.callback()
 
-class SimpleDialog(InfoDialog):
+class InfoDialog(GenericDialog):
 
     def __init__(self, title, body, callback = None):
-        InfoDialog.__init__(self, title, HTML(body), callback)
+        GenericDialog.__init__(self, title, HTML(body), callback)
 
 class PopupPanelWrapper(PopupPanel):
     """This wrapper catch Escape event to avoid request cancellation by Firefox"""
--- a/browser_side/panels.py	Sat May 28 20:18:14 2011 +0200
+++ b/browser_side/panels.py	Sun May 29 02:13:53 2011 +0200
@@ -79,6 +79,8 @@
 
         menu_contacts = MenuBar(vertical=True)
         menu_contacts.addItem("add contact", MenuCmd(self, "onAddContact"))
+        menu_contacts.addItem("update contact", MenuCmd(self, "onUpdateContact"))
+        menu_contacts.addItem("remove contact", MenuCmd(self, "onRemoveContact"))
 
         menu_group = MenuBar(vertical=True)
         menu_group.addItem("join room", MenuCmd(self, "onJoinRoom"))
@@ -134,6 +136,31 @@
         _dialog.setHTML('<b>Adding contact</b>')
         _dialog.show()
 
+    def onUpdateContact(self):
+        pass
+
+    def onRemoveContact(self):
+        _dialog = None
+        _contacts_list = ListBox()
+       
+        def secondConfirmCb(confirm):
+            if confirm:
+                for contact in _contacts_list.getSelectedValues():
+                    self.host.bridge.call('delContact', None, contact)
+            else:
+                _dialog.show()
+
+        def dialogCb(confirm):
+            if confirm:
+                html_list = ''.join(['<li>%s</li>' % html_sanitize(contact) for contact in _contacts_list.getSelectedValues()])
+                html_body = "Are you sure to remove the following contacts from your contact list ?<ul>%s</ul>" % html_list
+                dialog.ConfirmDialog(secondConfirmCb, html_body).show()
+
+        for contact in self.host.contact_panel.getContacts():
+            _contacts_list.addItem(contact)
+        _dialog = dialog.GenericConfirmDialog([_contacts_list], dialogCb, "Who do you want to remove from your contacts ?")
+        _dialog.show()
+
     #Group menu
     def onJoinRoom(self):
         _dialog = None
@@ -313,6 +340,13 @@
     def addKey(self, key):
         self.getCompletionItems().completions.append(key)
 
+    def removeKey(self, key):
+        try:
+            self.getCompletionItems().completions.remove(key)
+        except KeyError:
+            print "WARNING: trying to remove an unknown key"
+            
+
     def showWarning(self, target_data):
         type, target = target_data
         if type == "PUBLIC":
--- a/libervia.py	Sat May 28 20:18:14 2011 +0200
+++ b/libervia.py	Sun May 29 02:13:53 2011 +0200
@@ -68,7 +68,7 @@
         LiberviaJsonProxy.__init__(self, "/json_api",
                         ["getContacts", "addContact", "sendMessage", "sendMblog", "getMblogNodes", "getProfileJid", "getHistory", "getPresenceStatus",
                          "joinMUC", "getRoomJoined", "launchTarotGame", "getTarotCardsPaths", "tarotGameReady", "tarotGameContratChoosed",
-                         "tarotGamePlayCards", "getWaitingSub", "subscription"])
+                         "tarotGamePlayCards", "getWaitingSub", "subscription", "delContact"])
 
 class BridgeSignals(LiberviaJsonProxy):
     def __init__(self):
@@ -160,7 +160,7 @@
     def _getContactsCB(self, contacts_data):
         for contact in contacts_data:
             jid, attributes, groups = contact
-            self.contact_panel.addContact(jid, attributes, groups)
+            self._newContactCb(jid, attributes, groups)
 
     def _getSignalsCB(self, signal_data):
         bridge_signals = BridgeSignals()
@@ -191,6 +191,10 @@
             self._tarotGameGenericCb(name, args[0], args[1:])
         elif name == 'subscribe':
             self._subscribeCb(*args)
+        elif name == 'contactDeleted':
+            self._contactDeletedCb(*args)
+        elif name == 'newContact':
+            self._newContactCb(*args)
 
     def _getProfileJidCB(self, jid):
         self.whoami = JID(jid)
@@ -283,10 +287,10 @@
 
     def _subscribeCb(self, sub_type, entity):
         if sub_type == 'subscribed':
-            dialog.SimpleDialog('Subscription confirmation', 'The contact <b>%s</b> has added you to his/her contact list' % html_sanitize(entity)).show()
+            dialog.InfoDialog('Subscription confirmation', 'The contact <b>%s</b> has added you to his/her contact list' % html_sanitize(entity)).show()
 
         elif sub_type == 'unsubscribed':
-            dialog.SimpleDialog('Subscription refusal', 'The contact <b>%s</b> has refused to add you in his/her contact list' % html_sanitize(entity)).show()
+            dialog.InfoDialog('Subscription refusal', 'The contact <b>%s</b> has refused to add you in his/her contact list' % html_sanitize(entity)).show()
 
         elif sub_type == 'subscribe':
             #The user want to subscribe to our presence
@@ -302,7 +306,11 @@
             _dialog.setHTML('<b>Add contact request</b>')
             _dialog.show()
 
-            
+    def _contactDeletedCb(self, entity):
+        self.contact_panel.removeContact(entity)
+
+    def _newContactCb(self, contact, attributes, groups):
+        self.contact_panel.updateContact(contact, attributes, groups)
 
 if __name__ == '__main__':
     pyjd.setup("http://localhost:8080/libervia.html")
--- a/libervia.tac	Sat May 28 20:18:14 2011 +0200
+++ b/libervia.tac	Sun May 29 02:13:53 2011 +0200
@@ -148,6 +148,11 @@
         self.sat_host.bridge.addContact(entity, profile)
         self.sat_host.bridge.updateContact(entity, name, groups, profile)
 
+    def jsonrpc_delContact(self, entity):
+        """Remove contact from contacts list"""
+        profile = ISATSession(self.session).profile
+        self.sat_host.bridge.delContact(entity, profile)
+
     def jsonrpc_subscription(self, sub_type, entity, name, groups):
         """Confirm (or infirm) subscription,
         and setup user roster in case of subscription"""
@@ -579,7 +584,7 @@
         self.bridge.register("actionResult", self.action_handler.actionResultCb, "request") 
         for signal_name in ['presenceUpdate', 'personalEvent', 'newMessage', 'roomJoined', 'roomUserJoined', 'roomUserLeft', 'tarotGameStarted', 'tarotGameNew',
                             'tarotGameChooseContrat', 'tarotGameShowCards', 'tarotGameInvalidCards', 'tarotGameCardsPlayed', 'tarotGameYourTurn', 'tarotGameScore',
-                            'subscribe']:
+                            'subscribe', 'contactDeleted', 'newContact']:
             self.bridge.register(signal_name, self.signal_handler.getGenericCb(signal_name))
         root.putChild('json_signal_api', self.signal_handler)
         root.putChild('json_api', MethodHandler(self))