source: ralphm-patches/roster_item.patch @ 58:4b02ff624ab2

Last change on this file since 58:4b02ff624ab2 was 58:4b02ff624ab2, checked in by Ralph Meijer <ralphm@…>, 9 years ago

Move roster patches to xmppim.py, add backwards compat and new pushReceived.

File size: 26.8 KB
  • wokkel/test/test_xmppim.py

    Clean up of RosterItem and RosterClientProtocol.
    
    `RosterItem`:
     * Renamed attributes `jid` and `ask` to `entity` and
       `pendingOut` respectively.
     * Can represent roster items to be removed or that have been removed.
     * Now has `fromElement` and `toElement` methods.
    
    `RosterClientProtocol`:
     * Roster returned from `getRoster` is now indexed by `JID`s (instead of
       the `unicode` representation of the JID.
     * `onRosterSet` and `onRosterRemove` are deprecated in favor of
       `pushReceived`, which is called with a `RosterItem` for all roster pushes.
    
    diff --git a/wokkel/test/test_xmppim.py b/wokkel/test/test_xmppim.py
    a b  
    77
    88from twisted.internet import defer
    99from twisted.trial import unittest
     10from twisted.words.protocols.jabber import error
    1011from twisted.words.protocols.jabber.jid import JID
    1112from twisted.words.protocols.jabber.xmlstream import toResponse
    1213from twisted.words.xish import domish, utility
    1314
    1415from wokkel import xmppim
    1516from wokkel.generic import ErrorStanza, parseXml
    16 from wokkel.test.helpers import XmlStreamStub
     17from wokkel.test.helpers import TestableRequestHandlerMixin, XmlStreamStub
    1718
    1819NS_XML = 'http://www.w3.org/XML/1998/namespace'
    1920NS_ROSTER = 'jabber:iq:roster'
     
    449450
    450451
    451452
    452 class RosterClientProtocolTest(unittest.TestCase):
     453class RosterItemTest(unittest.TestCase):
     454    """
     455    Tests for L{xmppim.RosterItem}.
     456    """
     457
     458    def test_toElement(self):
     459        item = xmppim.RosterItem(JID('user@example.org'))
     460        element = item.toElement()
     461        self.assertEquals('item', element.name)
     462        self.assertEquals(NS_ROSTER, element.uri)
     463        self.assertFalse(element.hasAttribute('subscription'))
     464        self.assertFalse(element.hasAttribute('ask'))
     465        self.assertFalse(element.hasAttribute('name'))
     466        self.assertFalse(element.hasAttribute('approved'))
     467        self.assertEquals(0, len(list(element.elements())))
     468
     469
     470    def test_toElementMinimal(self):
     471        item = xmppim.RosterItem(JID('user@example.org'))
     472        element = item.toElement()
     473        self.assertEquals(u'user@example.org', element.getAttribute('jid'))
     474
     475
     476    def test_toElementSubscriptionNone(self):
     477        item = xmppim.RosterItem(JID('user@example.org'),
     478                                 subscriptionTo=False,
     479                                 subscriptionFrom=False)
     480        element = item.toElement()
     481        self.assertIdentical(None, element.getAttribute('subscription'))
     482
     483
     484    def test_toElementSubscriptionTo(self):
     485        item = xmppim.RosterItem(JID('user@example.org'),
     486                                 subscriptionTo=True,
     487                                 subscriptionFrom=False)
     488        element = item.toElement()
     489        self.assertEquals('to', element.getAttribute('subscription'))
     490
     491
     492    def test_toElementSubscriptionFrom(self):
     493        item = xmppim.RosterItem(JID('user@example.org'),
     494                                 subscriptionTo=False,
     495                                 subscriptionFrom=True)
     496        element = item.toElement()
     497        self.assertEquals('from', element.getAttribute('subscription'))
     498
     499
     500    def test_toElementSubscriptionBoth(self):
     501        item = xmppim.RosterItem(JID('user@example.org'),
     502                                 subscriptionTo=True,
     503                                 subscriptionFrom=True)
     504        element = item.toElement()
     505        self.assertEquals('both', element.getAttribute('subscription'))
     506
     507
     508    def test_toElementSubscriptionRemove(self):
     509        item = xmppim.RosterItem(JID('user@example.org'))
     510        item.remove = True
     511        element = item.toElement()
     512        self.assertEquals('remove', element.getAttribute('subscription'))
     513
     514
     515    def test_toElementAsk(self):
     516        item = xmppim.RosterItem(JID('user@example.org'))
     517        item.pendingOut = True
     518        element = item.toElement()
     519        self.assertEquals('subscribe', element.getAttribute('ask'))
     520
     521
     522    def test_toElementName(self):
     523        item = xmppim.RosterItem(JID('user@example.org'),
     524                                 name='Joe User')
     525        element = item.toElement()
     526        self.assertEquals(u'Joe User', element.getAttribute('name'))
     527
     528
     529    def test_toElementGroups(self):
     530        groups = set(['Friends', 'Jabber'])
     531        item = xmppim.RosterItem(JID('user@example.org'),
     532                                 groups=groups)
     533
     534        element = item.toElement()
     535        foundGroups = set()
     536        for child in element.elements():
     537            if child.uri == NS_ROSTER and child.name == 'group':
     538                foundGroups.add(unicode(child))
     539
     540        self.assertEqual(groups, foundGroups)
     541
     542
     543    def test_toElementApproved(self):
     544        """
     545        A pre-approved subscription for a roster item has an 'approved' flag.
     546        """
     547        item = xmppim.RosterItem(JID('user@example.org'))
     548        item.approved = True
     549        element = item.toElement()
     550        self.assertEquals(u'true', element.getAttribute('approved'))
     551
     552
     553    def test_fromElementMinimal(self):
     554        """
     555        A minimal roster item has a reference to the JID of the contact.
     556        """
     557
     558        xml = """
     559            <item xmlns="jabber:iq:roster"
     560                  jid="test@example.org"/>
     561        """
     562
     563        item = xmppim.RosterItem.fromElement(parseXml(xml))
     564        self.assertEqual(JID(u"test@example.org"), item.entity)
     565        self.assertIdentical(None, item.name)
     566        self.assertFalse(item.subscriptionTo)
     567        self.assertFalse(item.subscriptionFrom)
     568        self.assertFalse(item.pendingOut)
     569        self.assertFalse(item.approved)
     570        self.assertEquals(set(), item.groups)
     571
     572
     573    def test_fromElementName(self):
     574        """
     575        A roster item may have an optional name.
     576        """
     577
     578        xml = """
     579            <item xmlns="jabber:iq:roster"
     580                  jid="test@example.org"
     581                  name="Test User"/>
     582        """
     583
     584        item = xmppim.RosterItem.fromElement(parseXml(xml))
     585        self.assertEqual(u"Test User", item.name)
     586
     587
     588    def test_fromElementGroups(self):
     589        """
     590        A roster item may have one or more groups.
     591        """
     592
     593        xml = """
     594            <item xmlns="jabber:iq:roster"
     595                  jid="test@example.org">
     596              <group>Friends</group>
     597              <group>Twisted</group>
     598            </item>
     599        """
     600
     601        item = xmppim.RosterItem.fromElement(parseXml(xml))
     602        self.assertIn(u"Twisted", item.groups)
     603        self.assertIn(u"Friends", item.groups)
     604
     605
     606    def test_fromElementSubscriptionNone(self):
     607        """
     608        Subscription 'none' sets both attributes to False.
     609        """
     610
     611        xml = """
     612            <item xmlns="jabber:iq:roster"
     613                  jid="test@example.org"
     614                  subscription="none"/>
     615        """
     616
     617        item = xmppim.RosterItem.fromElement(parseXml(xml))
     618        self.assertFalse(item.remove)
     619        self.assertFalse(item.subscriptionTo)
     620        self.assertFalse(item.subscriptionFrom)
     621
     622
     623    def test_fromElementSubscriptionTo(self):
     624        """
     625        Subscription 'to' sets the corresponding attribute to True.
     626        """
     627
     628        xml = """
     629            <item xmlns="jabber:iq:roster"
     630                  jid="test@example.org"
     631                  subscription="to"/>
     632        """
     633
     634        item = xmppim.RosterItem.fromElement(parseXml(xml))
     635        self.assertFalse(item.remove)
     636        self.assertTrue(item.subscriptionTo)
     637        self.assertFalse(item.subscriptionFrom)
     638
     639
     640    def test_fromElementSubscriptionFrom(self):
     641        """
     642        Subscription 'from' sets the corresponding attribute to True.
     643        """
     644
     645        xml = """
     646            <item xmlns="jabber:iq:roster"
     647                  jid="test@example.org"
     648                  subscription="from"/>
     649        """
     650
     651        item = xmppim.RosterItem.fromElement(parseXml(xml))
     652        self.assertFalse(item.remove)
     653        self.assertFalse(item.subscriptionTo)
     654        self.assertTrue(item.subscriptionFrom)
     655
     656
     657    def test_fromElementSubscriptionBoth(self):
     658        """
     659        Subscription 'both' sets both attributes to True.
     660        """
     661
     662        xml = """
     663            <item xmlns="jabber:iq:roster"
     664                  jid="test@example.org"
     665                  subscription="both"/>
     666        """
     667
     668        item = xmppim.RosterItem.fromElement(parseXml(xml))
     669        self.assertFalse(item.remove)
     670        self.assertTrue(item.subscriptionTo)
     671        self.assertTrue(item.subscriptionFrom)
     672
     673
     674    def test_fromElementSubscriptionRemove(self):
     675        """
     676        Subscription 'remove' sets the remove attribute.
     677        """
     678
     679        xml = """
     680            <item xmlns="jabber:iq:roster"
     681                  jid="test@example.org"
     682                  subscription="remove"/>
     683        """
     684
     685        item = xmppim.RosterItem.fromElement(parseXml(xml))
     686        self.assertTrue(item.remove)
     687
     688
     689    def test_fromElementPendingOut(self):
     690        """
     691        The ask attribute, if set to 'subscription', means pending out.
     692        """
     693
     694        xml = """
     695            <item xmlns="jabber:iq:roster"
     696                  jid="test@example.org"
     697                  ask="subscribe"/>
     698        """
     699
     700        item = xmppim.RosterItem.fromElement(parseXml(xml))
     701        self.assertTrue(item.pendingOut)
     702
     703
     704    def test_fromElementApprovedTrue(self):
     705        """
     706        The approved attribute (true) signals a pre-approved subscription.
     707        """
     708
     709        xml = """
     710            <item xmlns="jabber:iq:roster"
     711                  jid="test@example.org"
     712                  approved="true"/>
     713        """
     714
     715        item = xmppim.RosterItem.fromElement(parseXml(xml))
     716        self.assertTrue(item.approved)
     717
     718
     719    def test_fromElementApproved1(self):
     720        """
     721        The approved attribute (1) signals a pre-approved subscription.
     722        """
     723
     724        xml = """
     725            <item xmlns="jabber:iq:roster"
     726                  jid="test@example.org"
     727                  approved="1"/>
     728        """
     729
     730        item = xmppim.RosterItem.fromElement(parseXml(xml))
     731        self.assertTrue(item.approved)
     732
     733
     734    def test_jidDeprecationGet(self):
     735        """
     736        Getting the jid attribute works as entity and warns deprecation.
     737        """
     738        item = xmppim.RosterItem(JID('user@example.org'))
     739        entity = self.assertWarns(DeprecationWarning,
     740                                  "wokkel.xmppim.RosterItem.jid is deprecated. "
     741                                  "Use RosterItem.entity instead.",
     742                                  xmppim.__file__,
     743                                  getattr, item, 'jid')
     744        self.assertIdentical(entity, item.entity)
     745
     746
     747    def test_jidDeprecationSet(self):
     748        """
     749        Setting the jid attribute works as entity and warns deprecation.
     750        """
     751        item = xmppim.RosterItem(JID('user@example.org'))
     752        self.assertWarns(DeprecationWarning,
     753                         "wokkel.xmppim.RosterItem.jid is deprecated. "
     754                         "Use RosterItem.entity instead.",
     755                         xmppim.__file__,
     756                         setattr, item, 'jid',
     757                         JID('other@example.org'))
     758        self.assertEquals(JID('other@example.org'), item.entity)
     759
     760
     761    def test_askDeprecationGet(self):
     762        """
     763        Getting the ask attribute works as entity and warns deprecation.
     764        """
     765        item = xmppim.RosterItem(JID('user@example.org'))
     766        item.pendingOut = True
     767        ask = self.assertWarns(DeprecationWarning,
     768                               "wokkel.xmppim.RosterItem.ask is deprecated. "
     769                               "Use RosterItem.pendingOut instead.",
     770                               xmppim.__file__,
     771                               getattr, item, 'ask')
     772        self.assertTrue(ask)
     773
     774
     775    def test_askDeprecationSet(self):
     776        """
     777        Setting the ask attribute works as entity and warns deprecation.
     778        """
     779        item = xmppim.RosterItem(JID('user@example.org'))
     780        self.assertWarns(DeprecationWarning,
     781                         "wokkel.xmppim.RosterItem.ask is deprecated. "
     782                         "Use RosterItem.pendingOut instead.",
     783                         xmppim.__file__,
     784                         setattr, item, 'ask',
     785                         True)
     786        self.assertTrue(item.pendingOut)
     787
     788
     789
     790class RosterClientProtocolTest(unittest.TestCase, TestableRequestHandlerMixin):
    453791    """
    454792    Tests for L{xmppim.RosterClientProtocol}.
    455793    """
    456794
    457795    def setUp(self):
    458796        self.stub = XmlStreamStub()
    459         self.protocol = xmppim.RosterClientProtocol()
    460         self.protocol.xmlstream = self.stub.xmlstream
    461         self.protocol.connectionInitialized()
     797        self.service = xmppim.RosterClientProtocol()
     798        self.service.makeConnection(self.stub.xmlstream)
     799        self.service.connectionInitialized()
    462800
    463801
    464802    def test_removeItem(self):
    465803        """
    466804        Removing a roster item is setting an item with subscription C{remove}.
    467805        """
    468         d = self.protocol.removeItem(JID('test@example.org'))
     806        d = self.service.removeItem(JID('test@example.org'))
    469807
    470808        # Inspect outgoing iq request
    471809
     
    479817        self.assertEquals(1, len(children))
    480818        child = children[0]
    481819        self.assertEquals('test@example.org', child['jid'])
    482         self.assertEquals('remove', child['subscription'])
     820        self.assertEquals('remove', child.getAttribute('subscription'))
    483821
    484822        # Fake successful response
    485823
    486824        response = toResponse(iq, 'result')
    487825        self.stub.send(response)
    488826        return d
     827
     828
     829    def test_getRoster(self):
     830        def cb(roster):
     831            self.assertIn(JID('user@example.org'), roster)
     832
     833
     834        d = self.service.getRoster()
     835        d.addCallback(cb)
     836
     837        # Inspect outgoing iq request
     838
     839        iq = self.stub.output[-1]
     840        self.assertEquals('get', iq.getAttribute('type'))
     841        self.assertNotIdentical(None, iq.query)
     842        self.assertEquals(NS_ROSTER, iq.query.uri)
     843
     844        # Fake successful response
     845        response = toResponse(iq, 'result')
     846        query = response.addElement((NS_ROSTER, 'query'))
     847        item = query.addElement('item')
     848        item['jid'] = 'user@example.org'
     849
     850        self.stub.send(response)
     851        return d
     852
     853
     854    def test_onRosterSet(self):
     855        """
     856        A roster push causes onRosterSet to be called with the parsed item.
     857        """
     858        xml = """
     859          <iq type='set'>
     860            <query xmlns='jabber:iq:roster'>
     861              <item jid='user@example.org'/>
     862            </query>
     863          </iq>
     864        """
     865
     866        items = []
     867
     868        def onRosterSet(item):
     869            items.append(item)
     870
     871        def cb(result):
     872            self.assertEquals(1, len(items))
     873            self.assertEquals(JID('user@example.org'), items[0].entity)
     874
     875        self.service.onRosterSet = onRosterSet
     876
     877        d = self.assertWarns(DeprecationWarning,
     878                             "wokkel.xmppim.RosterClientProtocol.onRosterSet "
     879                             "is deprecated. "
     880                             "Use RosterClientProtocol.pushReceived instead.",
     881                             xmppim.__file__,
     882                             self.handleRequest, xml)
     883        d.addCallback(cb)
     884        return d
     885
     886
     887    def test_onRosterRemove(self):
     888        """
     889        A roster push causes onRosterSet to be called with the parsed item.
     890        """
     891        xml = """
     892          <iq type='set'>
     893            <query xmlns='jabber:iq:roster'>
     894              <item jid='user@example.org' subscription='remove'/>
     895            </query>
     896          </iq>
     897        """
     898
     899        entities = []
     900
     901        def onRosterRemove(entity):
     902            entities.append(entity)
     903
     904        def cb(result):
     905            self.assertEquals([JID('user@example.org')], entities)
     906
     907        self.service.onRosterRemove = onRosterRemove
     908
     909        d = self.assertWarns(DeprecationWarning,
     910                             "wokkel.xmppim.RosterClientProtocol.onRosterRemove "
     911                             "is deprecated. "
     912                             "Use RosterClientProtocol.pushReceived instead.",
     913                             xmppim.__file__,
     914                             self.handleRequest, xml)
     915        d.addCallback(cb)
     916        return d
     917
     918
     919    def test_pushReceived(self):
     920        """
     921        A roster push causes pushReceived to be called with the parsed item.
     922        """
     923        xml = """
     924          <iq type='set'>
     925            <query xmlns='jabber:iq:roster'>
     926              <item jid='user@example.org'/>
     927            </query>
     928          </iq>
     929        """
     930
     931        items = []
     932
     933        def pushReceived(item):
     934            items.append(item)
     935
     936        def cb(result):
     937            self.assertEquals(1, len(items), "pushReceived was not called")
     938            self.assertEquals(JID('user@example.org'), items[0].entity)
     939
     940        self.service.pushReceived = pushReceived
     941
     942        d = self.handleRequest(xml)
     943        d.addCallback(cb)
     944        return d
  • wokkel/xmppim.py

    diff --git a/wokkel/xmppim.py b/wokkel/xmppim.py
    a b  
    77XMPP IM protocol support.
    88
    99This module provides generic implementations for the protocols defined in
    10 U{RFC 3921<http://xmpp.org/rfcs/rfc3921.html>} (XMPP IM).
    11 
    12 All of it should eventually move to Twisted.
     10U{RFC 6121<http://www.xmpp.org/rfcs/rfc6121.html>} (XMPP IM).
    1311"""
    1412
     13import warnings
     14
     15from twisted.internet import defer
     16from twisted.words.protocols.jabber import error
    1517from twisted.words.protocols.jabber.jid import JID
    1618from twisted.words.xish import domish
    1719
    1820from wokkel.compat import IQ
    1921from wokkel.generic import ErrorStanza, Stanza
     22from wokkel.subprotocols import IQHandlerMixin
    2023from wokkel.subprotocols import XMPPHandler
    2124
    2225NS_XML = 'http://www.w3.org/XML/1998/namespace'
    2326NS_ROSTER = 'jabber:iq:roster'
    2427
     28XPATH_ROSTER_SET = "/iq[@type='set']/query[@xmlns='%s']" % NS_ROSTER
     29
     30
     31
    2532class Presence(domish.Element):
    2633    def __init__(self, to=None, type=None):
    2734        domish.Element.__init__(self, (None, "presence"))
     
    605612
    606613    This represents one contact from an XMPP contact list known as roster.
    607614
    608     @ivar jid: The JID of the contact.
    609     @type jid: L{JID}
     615    @ivar entity: The JID of the contact.
     616    @type entity: L{JID}
    610617    @ivar name: The optional associated nickname for this contact.
    611618    @type name: C{unicode}
    612619    @ivar subscriptionTo: Subscription state to contact's presence. If C{True},
     
    616623    @ivar subscriptionFrom: Contact's subscription state. If C{True}, the
    617624                            contact is subscribed to the presence information
    618625                            of the roster owner.
    619     @type subscriptionTo: C{bool}
    620     @ivar ask: Whether subscription is pending.
    621     @type ask: C{bool}
     626    @type subscriptionFrom: C{bool}
     627    @ivar pendingOut: Whether the subscription request to this contact is
     628        pending.
     629    @type pendingOut: C{bool}
    622630    @ivar groups: Set of groups this contact is categorized in. Groups are
    623631                  represented by an opaque identifier of type C{unicode}.
    624632    @type groups: C{set}
     633    @ivar approved: Signals pre-approved subscription.
     634    @type approved: C{bool}
     635    @ivar remove: Signals roster item removal.
     636    @type remove: C{bool}
    625637    """
    626638
    627     def __init__(self, jid):
    628         self.jid = jid
    629         self.name = None
    630         self.subscriptionTo = False
    631         self.subscriptionFrom = False
    632         self.ask = None
    633         self.groups = set()
     639    __subscriptionStates = {(False, False): None,
     640                            (True, False): 'to',
     641                            (False, True): 'from',
     642                            (True, True): 'both'}
    634643
     644    def __init__(self, entity, subscriptionTo=False, subscriptionFrom=False,
     645                       name=None, groups=None):
     646        self.entity = entity
     647        self.subscriptionTo = subscriptionTo
     648        self.subscriptionFrom = subscriptionFrom
     649        self.name = name
     650        self.groups = groups or set()
    635651
    636 class RosterClientProtocol(XMPPHandler):
     652        self.pendingOut = False
     653        self.approved = False
     654        self.remove = False
     655
     656
     657    def __getJID(self):
     658        warnings.warn(
     659            "wokkel.xmppim.RosterItem.jid is deprecated. "
     660            "Use RosterItem.entity instead.",
     661            DeprecationWarning)
     662        return self.entity
     663
     664
     665    def __setJID(self, value):
     666        warnings.warn(
     667            "wokkel.xmppim.RosterItem.jid is deprecated. "
     668            "Use RosterItem.entity instead.",
     669            DeprecationWarning)
     670        self.entity = value
     671
     672
     673    jid = property(__getJID, __setJID, doc="""
     674            JID of the contact. Deprecated in favour of C{entity}.""")
     675
     676
     677    def __getAsk(self):
     678        warnings.warn(
     679            "wokkel.xmppim.RosterItem.ask is deprecated. "
     680            "Use RosterItem.pendingOut instead.",
     681            DeprecationWarning)
     682        return self.pendingOut
     683
     684
     685    def __setAsk(self, value):
     686        warnings.warn(
     687            "wokkel.xmppim.RosterItem.ask is deprecated. "
     688            "Use RosterItem.pendingOut instead.",
     689            DeprecationWarning)
     690        self.pendingOut = value
     691
     692
     693    ask = property(__getAsk, __setAsk, doc="""
     694            Pending out subscription. Deprecated in favour of C{pendingOut}.""")
     695
     696
     697    def toElement(self):
     698        element = domish.Element((NS_ROSTER, 'item'))
     699        element['jid'] = self.entity.full()
     700
     701        if self.remove:
     702            subscription = 'remove'
     703        else:
     704            subscription = self.__subscriptionStates[self.subscriptionTo,
     705                                                     self.subscriptionFrom]
     706
     707            if self.pendingOut:
     708                element['ask'] = u'subscribe'
     709
     710            if self.name:
     711                element['name'] = self.name
     712
     713            if self.approved:
     714                element['approved'] = u'true'
     715
     716            if self.groups:
     717                for group in self.groups:
     718                    element.addElement('group', content=group)
     719
     720        if subscription:
     721            element['subscription'] = subscription
     722
     723        return element
     724
     725
     726    @classmethod
     727    def fromElement(Class, element):
     728        entity = JID(element['jid'])
     729        item = Class(entity)
     730        subscription = element.getAttribute('subscription')
     731        if subscription == 'remove':
     732            item.remove = True
     733        else:
     734            item.name = element.getAttribute('name')
     735            item.subscriptionTo = subscription in ('to', 'both')
     736            item.subscriptionFrom = subscription in ('from', 'both')
     737            item.pendingOut = element.getAttribute('ask') == 'subscribe'
     738            item.approved = element.getAttribute('approved') in ('true', '1')
     739            for subElement in domish.generateElementsQNamed(element.children,
     740                                                            'group', NS_ROSTER):
     741                item.groups.add(unicode(subElement))
     742        return item
     743
     744
     745
     746class RosterClientProtocol(XMPPHandler, IQHandlerMixin):
    637747    """
    638748    Client side XMPP roster protocol.
    639749    """
    640750
     751    iqHandlers = {XPATH_ROSTER_SET: "_onRosterSet"}
     752
     753
    641754    def connectionInitialized(self):
    642         ROSTER_SET = "/iq[@type='set']/query[@xmlns='%s']" % NS_ROSTER
    643         self.xmlstream.addObserver(ROSTER_SET, self._onRosterSet)
     755        self.xmlstream.addObserver(XPATH_ROSTER_SET, self.handleRequest)
    644756
    645     def _parseRosterItem(self, element):
    646         jid = JID(element['jid'])
    647         item = RosterItem(jid)
    648         item.name = element.getAttribute('name')
    649         subscription = element.getAttribute('subscription')
    650         item.subscriptionTo = subscription in ('to', 'both')
    651         item.subscriptionFrom = subscription in ('from', 'both')
    652         item.ask = element.getAttribute('ask') == 'subscribe'
    653         for subElement in domish.generateElementsQNamed(element.children,
    654                                                         'group', NS_ROSTER):
    655             item.groups.add(unicode(subElement))
    656 
    657         return item
    658757
    659758    def getRoster(self):
    660759        """
     
    668767            roster = {}
    669768            for element in domish.generateElementsQNamed(result.query.children,
    670769                                                         'item', NS_ROSTER):
    671                 item = self._parseRosterItem(element)
    672                 roster[item.jid.userhost()] = item
     770                item = RosterItem.fromElement(element)
     771                roster[item.entity] = item
    673772
    674773            return roster
    675774
     
    690789        """
    691790        iq = IQ(self.xmlstream, 'set')
    692791        iq.addElement((NS_ROSTER, 'query'))
    693         item = iq.query.addElement('item')
    694         item['jid'] = entity.full()
    695         item['subscription'] = 'remove'
     792        item = RosterItem(entity)
     793        item.remove = True
     794        iq.query.addChild(item.toElement())
    696795        return iq.send()
    697796
    698797
    699798    def _onRosterSet(self, iq):
    700         if iq.handled or \
    701            iq.hasAttribute('from') and iq['from'] != self.xmlstream:
    702             return
     799        item = RosterItem.fromElement(iq.query.item)
    703800
    704         iq.handled = True
     801        d = defer.maybeDeferred(self.pushReceived, item)
     802        return d
    705803
    706         itemElement = iq.query.item
    707804
    708         if unicode(itemElement['subscription']) == 'remove':
    709             self.onRosterRemove(JID(itemElement['jid']))
    710         else:
    711             item = self._parseRosterItem(iq.query.item)
    712             self.onRosterSet(item)
     805    def pushReceived(self, item):
     806        """
     807        Called when a roster push was received.
    713808
    714     def onRosterSet(self, item):
    715         """
    716         Called when a roster push for a new or update item was received.
     809        Override this to handle roster pushes.
     810
     811        For backwards compatibility, the default implementation calls
     812        the deprecated C{onRosterSet} or C{onRosterRemove} if defined on
     813        C{self}.
    717814
    718815        @param item: The pushed roster item.
    719816        @type item: L{RosterItem}
    720817        """
    721 
    722     def onRosterRemove(self, entity):
    723         """
    724         Called when a roster push for the removal of an item was received.
    725 
    726         @param entity: The entity for which the roster item has been removed.
    727         @type entity: L{JID}
    728         """
     818        if item.remove:
     819            if hasattr(self, 'onRosterRemove'):
     820                warnings.warn(
     821                    "wokkel.xmppim.RosterClientProtocol.onRosterRemove "
     822                    "is deprecated. "
     823                    "Use RosterClientProtocol.pushReceived instead.",
     824                    DeprecationWarning)
     825                return defer.maybeDeferred(self.onRosterRemove, item.entity)
     826        else:
     827            if hasattr(self, 'onRosterSet'):
     828                warnings.warn(
     829                    "wokkel.xmppim.RosterClientProtocol.onRosterSet "
     830                    "is deprecated. "
     831                    "Use RosterClientProtocol.pushReceived instead.",
     832                    DeprecationWarning)
     833                return defer.maybeDeferred(self.onRosterSet, item)
    729834
    730835
    731836
Note: See TracBrowser for help on using the repository browser.