diff --git a/sleekxmpp/stanza/roster.py b/sleekxmpp/stanza/roster.py index eda65ec..292c895 100644 --- a/sleekxmpp/stanza/roster.py +++ b/sleekxmpp/stanza/roster.py @@ -5,51 +5,105 @@ See the file LICENSE for copying permission. """ -from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID -import logging + +from sleekxmpp.stanza import Iq +from sleekxmpp.xmlstream import JID +from sleekxmpp.xmlstream.stanzabase import registerStanzaPlugin +from sleekxmpp.xmlstream.stanzabase import ET, ElementBase + class Roster(ElementBase): - namespace = 'jabber:iq:roster' - name = 'query' - plugin_attrib = 'roster' - interfaces = set(('items',)) - sub_interfaces = set() - def setItems(self, items): - self.delItems() - for jid in items: - ijid = str(jid) - item = ET.Element('{jabber:iq:roster}item', {'jid': ijid}) - if 'subscription' in items[jid]: - item.attrib['subscription'] = items[jid]['subscription'] - if 'name' in items[jid]: - name = items[jid]['name'] - if name is not None: - item.attrib['name'] = name - if 'groups' in items[jid]: - for group in items[jid]['groups']: - groupxml = ET.Element('{jabber:iq:roster}group') - groupxml.text = group - item.append(groupxml) - self.xml.append(item) - return self - - def getItems(self): - items = {} - itemsxml = self.xml.findall('{jabber:iq:roster}item') - if itemsxml is not None: - for itemxml in itemsxml: - item = {} - item['name'] = itemxml.get('name', '') - item['subscription'] = itemxml.get('subscription', '') - item['groups'] = [] - groupsxml = itemxml.findall('{jabber:iq:roster}group') - if groupsxml is not None: - for groupxml in groupsxml: - item['groups'].append(groupxml.text) - items[itemxml.get('jid')] = item - return items - - def delItems(self): - for child in self.xml.getchildren(): - self.xml.remove(child) + """ + Example roster stanzas: + + + + Friends + + + + + Stanza Inteface: + items -- A dictionary of roster entries contained + in the stanza. + + Methods: + getItems -- Return a dictionary of roster entries. + setItems -- Add elements. + delItems -- Remove all elements. + """ + + namespace = 'jabber:iq:roster' + name = 'query' + plugin_attrib = 'roster' + interfaces = set(('items',)) + + def setItems(self, items): + """ + Set the roster entries in the stanza. + + Uses a dictionary using JIDs as keys, where each entry is itself + a dictionary that contains: + name -- An alias or nickname for the JID. + subscription -- The subscription type. Can be one of 'to', + 'from', 'both', 'none', or 'remove'. + groups -- A list of group names to which the JID + has been assigned. + + Arguments: + items -- A dictionary of roster entries. + """ + self.delItems() + for jid in items: + ijid = str(jid) + item = ET.Element('{jabber:iq:roster}item', {'jid': ijid}) + if 'subscription' in items[jid]: + item.attrib['subscription'] = items[jid]['subscription'] + if 'name' in items[jid]: + name = items[jid]['name'] + if name is not None: + item.attrib['name'] = name + if 'groups' in items[jid]: + for group in items[jid]['groups']: + groupxml = ET.Element('{jabber:iq:roster}group') + groupxml.text = group + item.append(groupxml) + self.xml.append(item) + return self + + def getItems(self): + """ + Return a dictionary of roster entries. + + Each item is keyed using its JID, and contains: + name -- An assigned alias or nickname for the JID. + subscription -- The subscription type. Can be one of 'to', + 'from', 'both', 'none', or 'remove'. + groups -- A list of group names to which the JID has + been assigned. + """ + items = {} + itemsxml = self.xml.findall('{jabber:iq:roster}item') + if itemsxml is not None: + for itemxml in itemsxml: + item = {} + item['name'] = itemxml.get('name', '') + item['subscription'] = itemxml.get('subscription', '') + item['groups'] = [] + groupsxml = itemxml.findall('{jabber:iq:roster}group') + if groupsxml is not None: + for groupxml in groupsxml: + item['groups'].append(groupxml.text) + items[itemxml.get('jid')] = item + return items + + def delItems(self): + """ + Remove all elements from the roster stanza. + """ + for child in self.xml.getchildren(): + self.xml.remove(child) + + +registerStanzaPlugin(Iq, Roster) diff --git a/tests/test_roster.py b/tests/test_roster.py new file mode 100644 index 0000000..6f9fa3d --- /dev/null +++ b/tests/test_roster.py @@ -0,0 +1,84 @@ +from . sleektest import * +from sleekxmpp.stanza.roster import Roster + + +class TestRosterStanzas(SleekTest): + + def testAddItems(self): + """Test adding items to a roster stanza.""" + iq = self.Iq() + iq['roster'].setItems({ + 'user@example.com': { + 'name': 'User', + 'subscription': 'both', + 'groups': ['Friends', 'Coworkers']}, + 'otheruser@example.com': { + 'name': 'Other User', + 'subscription': 'both', + 'groups': []}}) + self.checkIq(iq, """ + + + + Friends + Coworkers + + + + + """) + + def testGetItems(self): + """Test retrieving items from a roster stanza.""" + xml_string = """ + + + + Friends + Coworkers + + + + + """ + iq = self.Iq(ET.fromstring(xml_string)) + expected = { + 'user@example.com': { + 'name': 'User', + 'subscription': 'both', + 'groups': ['Friends', 'Coworkers']}, + 'otheruser@example.com': { + 'name': 'Other User', + 'subscription': 'both', + 'groups': []}} + debug = "Roster items don't match after retrieval." + debug += "\nReturned: %s" % str(iq['roster']['items']) + debug += "\nExpected: %s" % str(expected) + self.failUnless(iq['roster']['items'] == expected, debug) + + def testDelItems(self): + """Test clearing items from a roster stanza.""" + xml_string = """ + + + + Friends + Coworkers + + + + + """ + iq = self.Iq(ET.fromstring(xml_string)) + del iq['roster']['items'] + self.checkIq(iq, """ + + + + """) + + +suite = unittest.TestLoader().loadTestsFromTestCase(TestRosterStanzas)