PubSub support: browse, subscribe and unsubscribe; posting atom entries to groups.

This commit is contained in:
Tomasz Melcer 2006-08-20 10:18:20 +00:00
parent 4682b5bed7
commit 8f4b972a62
3 changed files with 214 additions and 8 deletions

View File

@ -35,6 +35,7 @@ from common import gajim
from common import dataforms
from common import atom
from common.commands import ConnectionCommands
from common.pubsub import ConnectionPubSub
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
'invisible']
@ -1057,11 +1058,12 @@ class ConnectionVcard:
else:
self.dispatch('VCARD', vcard)
class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, ConnectionCommands):
class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, ConnectionCommands, ConnectionPubSub):
def __init__(self):
ConnectionVcard.__init__(self)
ConnectionBytestream.__init__(self)
ConnectionCommands.__init__(self)
ConnectionPubSub.__init__(self)
# List of IDs we are waiting answers for {id: (type_of_request, data), }
self.awaiting_answers = {}
# List of IDs that will produce a timeout is answer doesn't arrive
@ -1857,6 +1859,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
common.xmpp.NS_DISCO_INFO)
con.RegisterHandler('iq', self._DiscoverItemsGetCB, 'get',
common.xmpp.NS_DISCO_ITEMS)
con.RegisterHandler('iq', self._PubSubCB, 'result')
con.RegisterHandler('iq', self._ErrorCB, 'error')
con.RegisterHandler('iq', self._IqCB)
con.RegisterHandler('iq', self._StanzaArrivedCB)

51
src/common/pubsub.py Normal file
View File

@ -0,0 +1,51 @@
import xmpp
import gajim
class ConnectionPubSub:
def __init__(self):
self.__callbacks={}
def send_pb_subscription_query(self, jid, cb, *args, **kwargs):
query = xmpp.Iq('get', to=jid)
pb = query.addChild('pubsub', {'xmlns': xmpp.NS_PUBSUB})
pb.addChild('subscriptions')
id = self.connection.send(query)
self.__callbacks[id]=(cb, args, kwargs)
def send_pb_subscribe(self, jid, node, cb, *args, **kwargs):
our_jid = gajim.get_jid_from_account(self.name)
query = xmpp.Iq('set', to=jid)
pb = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB)
pb.addChild('subscribe', {'node': node, 'jid': our_jid})
id = self.connection.send(query)
self.__callbacks[id]=(cb, args, kwargs)
def send_pb_unsubscribe(self, jid, node, cb, *args, **kwargs):
our_jid = gajim.get_jid_from_account(self.name)
query = xmpp.Iq('set', to=jid)
pb = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB)
pb.addChild('unsubscribe', {'node': node, 'jid': our_jid})
id = self.connection.send(query)
self.__callbacks[id]=(cb, args, kwargs)
def send_pb_publish(self, jid, node, item, id):
'''Publish item to a node.'''
query = xmpp.Iq('set', to=jid)
e = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB)
e = e.addChild('publish', {'node': node})
e = e.addChild('item', {'id': id}, [item]) # TODO: we should generate id... or we shouldn't?
self.connection.send(query)
def _PubSubCB(self, conn, stanza):
try:
cb, args, kwargs = self.__callbacks.pop(stanza.getID())
cb(conn, stanza, *args, **kwargs)
except KeyError:
pass

View File

@ -56,6 +56,7 @@ import pango
import dialogs
import tooltips
import gtkgui_helpers
import groups
from common import gajim
from common import xmpp
@ -217,6 +218,7 @@ class ServicesCache:
self.account = account
self._items = CacheDictionary(15, getrefresh = False)
self._info = CacheDictionary(15, getrefresh = False)
self._subscriptions = CacheDictionary(5, getrefresh=False)
self._cbs = {}
def _clean_closure(self, cb, type, addr):
@ -573,7 +575,7 @@ _('Without a connection, you can not browse available services'))
self.connect_style_event(opts[0], opts[1])
def destroy(self, chain = False):
'''Close the browser. This can optionally close it's children and
'''Close the browser. This can optionally close its children and
propagate to the parent. This should happen on actions like register,
or join to kill off the entire browser chain.'''
if self.dying:
@ -596,7 +598,8 @@ _('Without a connection, you can not browse available services'))
child.destroy(chain = chain)
self.children.remove(child)
if self.parent:
self.parent.children.remove(self)
if self in self.parent.children:
self.parent.children.remove(self)
if chain and not self.parent.children:
self.parent.destroy(chain = chain)
self.parent = None
@ -1670,10 +1673,24 @@ def PubSubBrowser(account, jid, node):
class DiscussionGroupsBrowser(AgentBrowser):
''' For browsing pubsub-based discussion groups service. '''
def __init__(self, account, jid, node):
AgentBrowser.__init__(self, account, jid, node)
# this will become set object when we get subscriptions; None means
# we don't know yet which groups are subscribed
self.subscriptions = None
# this will become our action widgets when we create them; None means
# we don't have them yet (needed for check in callback)
self.subscribe_button = None
self.unsubscribe_button = None
gajim.connections[account].send_pb_subscription_query(jid, self._subscriptionsCB)
def _create_treemodel(self):
''' Create treemodel for the window. '''
# JID, node, name (with description) - pango markup, subscribed?
model = gtk.ListStore(str, str, str, bool)
# JID, node, name (with description) - pango markup, dont have info?, subscribed?
model = gtk.ListStore(str, str, str, bool, bool)
model.set_sort_column_id(3, gtk.SORT_ASCENDING)
self.window.services_treeview.set_model(model)
@ -1691,24 +1708,53 @@ class DiscussionGroupsBrowser(AgentBrowser):
renderer = gtk.CellRendererToggle()
col = gtk.TreeViewColumn(_('Subscribed'))
col.pack_start(renderer)
col.set_attributes(renderer, active=3)
col.set_attributes(renderer, inconsistent=3, active=4)
col.set_resizable(False)
self.window.services_treeview.insert_column(col, -1)
self.window.services_treeview.set_headers_visible(True)
def _add_item(self, model, jid, node, item, force):
''' Called when we got basic information about new node from query.
Show the item. '''
name = item.get('name', '')
if self.subscriptions is not None:
dunno = False
subscribed = name in self.subscriptions
else:
dunno = True
subscribed = False
name = gtkgui_helpers.escape_for_pango_markup(name)
name = '<b>%s</b>' % name
model.append((jid, node, name, dunno, subscribed))
def _add_actions(self):
self.post_button = gtk.Button(label=_('New post'), use_underline=True)
self.post_button.set_sensitive(False)
self.post_button.connect('clicked', self.on_post_button_clicked)
self.window.action_buttonbox.add(self.post_button)
self.post_button.show_all()
self.subscribe_button = gtk.Button(label=_('_Subscribe'), use_underline=True)
self.subscribe_button.set_sensitive(False)
self.subscribe_button.connect('clicked', self.on_subscribe_button_clicked)
self.window.action_buttonbox.add(self.subscribe_button)
self.subscribe_button.show_all()
self.unsubscribe_button = gtk.Button(label=_('_Unsubscribe'), use_underline=True)
self.unsubscribe_button.set_sensitive(False)
self.unsubscribe_button.connect('clicked', self.on_unsubscribe_button_clicked)
self.window.action_buttonbox.add(self.unsubscribe_button)
self.unsubscribe_button.show_all()
def _clean_actions(self):
if self.post_button is not None:
self.post_button.destroy()
self.post_button = None
if self.subscribe_button is not None:
self.subscribe_button.destroy()
self.subscribe_button = None
@ -1717,8 +1763,114 @@ class DiscussionGroupsBrowser(AgentBrowser):
self.unsubscribe_button.destroy()
self.unsubscribe_button = None
def on_subscribe_button_clicked(*x): pass
def on_unsubscribe_button_clicked(*x): pass
def update_actions(self):
'''Called when user selected a row. Make subscribe/unsubscribe buttons
sensitive appropriatelly.'''
# we have nothing to do if we don't have buttons...
if self.subscribe_button is None: return
model, iter = self.window.services_treeview.get_selection().get_selected()
if not iter or self.subscriptions is None:
# no item selected or no subscriptions info, all buttons are insensitive
self.post_button.set_sensitive(False)
self.subscribe_button.set_sensitive(False)
self.unsubscribe_button.set_sensitive(False)
else:
subscribed = model.get_value(iter, 4) # 4 = subscribed?
self.post_button.set_sensitive(subscribed)
self.subscribe_button.set_sensitive(not subscribed)
self.unsubscribe_button.set_sensitive(subscribed)
def on_post_button_clicked(self, widget):
'''Called when 'post' button is pressed. Open window to create post'''
model, iter = self.window.services_treeview.get_selection().get_selected()
if iter is None: return
groupnode = model.get_value(iter, 1) # 1 = groupnode
groups.GroupsPostWindow(self.account, self.jid, groupnode)
def on_subscribe_button_clicked(self, widget):
'''Called when 'subscribe' button is pressed. Send subscribtion request.'''
model, iter = self.window.services_treeview.get_selection().get_selected()
if iter is None: return
groupnode = model.get_value(iter, 1) # 1 = groupnode
gajim.connections[self.account].send_pb_subscribe(self.jid, groupnode, self._subscribeCB, groupnode)
def on_unsubscribe_button_clicked(self, widget):
'''Called when 'unsubscribe' button is pressed. Send unsubscription request.'''
model, iter = self.window.services_treeview.get_selection().get_selected()
if iter is None: return
groupnode = model.get_value(iter, 1) # 1 = groupnode
gajim.connections[self.account].send_pb_unsubscribe(self.jid, groupnode, self._unsubscribeCB, groupnode)
def _subscriptionsCB(self, conn, request):
''' We got the subscribed groups list stanza. Now, if we already
have items on the list, we should actualize them. '''
print 0
try:
subscriptions = request.getTag('pubsub').getTag('subscriptions')
except:
return
print 1
groups = set()
for child in subscriptions.getTags('subscription'):
print 2, repr(child), str(child)
groups.add(child['node'])
print 3, groups
self.subscriptions = groups
# try to setup existing items in model
model = self.window.services_treeview.get_model()
print 4
for row in model:
print 5
# 1 = group node
# 3 = insensitive checkbox for subscribed
# 4 = subscribed?
groupnode = row[1]
row[3]=False
row[4]=groupnode in groups
print 6
# we now know subscriptions, update button states
self.update_actions()
raise xmpp.NodeProcessed
def _subscribeCB(self, conn, request, groupnode):
'''We have just subscribed to a node. Update UI'''
self.subscriptions.add(groupnode)
model = self.window.services_treeview.get_model()
for row in model:
if row[1] == groupnode: # 1 = groupnode
row[4]=True
break
self.update_actions()
raise xmpp.NodeProcessed
def _unsubscribeCB(self, conn, request, groupnode):
'''We have just unsubscribed from a node. Update UI'''
self.subscriptions.remove(groupnode)
model = self.window.services_treeview.get_model()
for row in model:
if row[1] == groupnode: # 1 = groupnode
row[4]=False
break
self.update_actions()
raise xmpp.NodeProcessed
# Fill the global agent type info dictionary
_agent_type_info = _gen_agent_type_info()