[roidelapluie] Added initial support for Roster Item Exchange (XEP-0144) based on ticket #378. Only handling NS_ROSTERX 'set' operations is supported and tested as of now.

This commit is contained in:
Mateusz Biliński 2009-06-21 03:43:57 +02:00
parent 3676c55e9d
commit 38fa0be12a
5 changed files with 613 additions and 288 deletions

View File

@ -0,0 +1,99 @@
<?xml version="1.0"?>
<glade-interface>
<!-- interface-requires gtk+ 2.16 -->
<!-- interface-naming-policy project-wide -->
<widget class="GtkWindow" id="roster_item_exchange_window">
<property name="title" translatable="yes">Roster Item Exchange</property>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<widget class="GtkLabel" id="type_label">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;someone@somewhere.com&lt;/b&gt; would like you to &lt;b&gt;add&lt;/b&gt; some contacts in your roster.</property>
<property name="use_markup">True</property>
<property name="justify">center</property>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="body_scrolledwindow">
<property name="can_focus">True</property>
<property name="no_show_all">True</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<child>
<widget class="GtkTextView" id="body_textview">
<property name="can_focus">True</property>
<property name="no_show_all">True</property>
<property name="editable">False</property>
<property name="justification">center</property>
<property name="cursor_visible">False</property>
<property name="text" translatable="yes">Message Body</property>
</widget>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<child>
<widget class="GtkTreeView" id="items_list_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
</widget>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="spacing">3</property>
<child>
<widget class="GtkButton" id="cancel_button">
<property name="label" translatable="yes">gtk-cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="on_cancel_button_clicked"/>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="accept_button">
<property name="label" translatable="yes">gtk-ok</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="on_accept_button_clicked"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">3</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@ -1771,6 +1771,27 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
self.dispatch('GMAIL_NOTIFY', (jid, newmsgs, gmail_messages_list))
raise common.xmpp.NodeProcessed
def _rosterItemExchangeCB(self, con, msg):
''' XEP-0144 Roster Item Echange '''
exchange_items_list = {}
jid_from = msg.getAttr('from')
items_list = msg.getTag('x').getChildren()
action = items_list[0].getAttr('action')
if action == None:
action = 'add'
for item in msg.getTag('x').getChildren():
jid = item.getAttr('jid')
name = item.getAttr('name')
groups=[]
for group in item.getChildren():
groups.append(group.getData())
exchange_items_list[jid] = []
exchange_items_list[jid].append(name)
exchange_items_list[jid].append(groups)
self.dispatch('ROSTERX', (action, exchange_items_list, jid_from))
def _messageCB(self, con, msg):
'''Called when we receive a message'''
log.debug('MessageCB')
@ -1781,6 +1802,11 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
self._pubsubEventCB(con, msg)
return
# check if the message is a roster item exchange (XEP-0144)
#if msg.getTag('x') and msg.getTag('x').namespace == common.xmpp.NS_ROSTERX:
#self._rosterItemExchangeCB(con, msg)
#return
# check if the message is a XEP-0070 confirmation request
if msg.getTag('confirm', namespace=common.xmpp.NS_HTTP_AUTH):
self._HttpAuthCB(con, msg)
@ -2579,6 +2605,8 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
common.xmpp.NS_ROSTER)
con.RegisterHandler('iq', self._siSetCB, 'set',
common.xmpp.NS_SI)
con.RegisterHandler('iq', self._rosterItemExchangeCB, 'set',
common.xmpp.NS_ROSTERX)
con.RegisterHandler('iq', self._siErrorCB, 'error',
common.xmpp.NS_SI)
con.RegisterHandler('iq', self._siResultCB, 'result',

View File

@ -195,7 +195,7 @@ gajim_common_features = [xmpp.NS_BYTESTREAM, xmpp.NS_SI, xmpp.NS_FILE,
'jabber:iq:gateway', xmpp.NS_LAST, xmpp.NS_PRIVACY, xmpp.NS_PRIVATE,
xmpp.NS_REGISTER, xmpp.NS_VERSION, xmpp.NS_DATA, xmpp.NS_ENCRYPTED, 'msglog',
'sslc2s', 'stringprep', xmpp.NS_PING, xmpp.NS_TIME_REVISED, xmpp.NS_SSN,
xmpp.NS_MOOD, xmpp.NS_ACTIVITY, xmpp.NS_NICK]
xmpp.NS_MOOD, xmpp.NS_ACTIVITY, xmpp.NS_NICK, xmpp.NS_ROSTERX]
# Optional features gajim supports per account
gajim_optional_features = {}

View File

@ -2641,6 +2641,199 @@ class XMLConsoleWindow:
# it's expanded!!
self.input_textview.grab_focus()
class RosterItemExchangeWindow:
''' Windows used when someone send you a exchange contact suggestion '''
def __init__(self, account, action, exchange_list, jid_from, message_body=None):
self.account = account
self.action = action
self.exchange_list = exchange_list
self.message_body = message_body
self.jid_from = jid_from
# Connect to glade
self.xml = gtkgui_helpers.get_glade('roster_item_exchange_window.glade')
self.window = self.xml.get_widget('roster_item_exchange_window')
# Add Widgets.
for widget_to_add in ['cancel_button', 'accept_button', 'type_label',
'body_scrolledwindow', 'body_textview', 'items_list_treeview']:
self.__dict__[widget_to_add] = self.xml.get_widget(widget_to_add)
# Set labels
#self.action can be 'add', 'modify' or 'remove'
self.type_label.set_label(\
_('<b>%s</b> would like you to <b>%s</b> some contacts in your roster.') \
% (jid_from, _(self.action)))
if message_body:
buffer = self.body_textview.get_buffer()
buffer.set_text(self.message_body)
else:
self.body_scrolledwindow.hide()
# Treeview
model = gtk.ListStore(bool, str, str, str, str)
self.items_list_treeview.set_model(model)
# columns
renderer1 = gtk.CellRendererToggle()
renderer1.set_property('activatable', True)
renderer1.connect('toggled', self.toggled_callback)
self.items_list_treeview.insert_column_with_attributes(-1,
_(self.action), renderer1, active = 0)
renderer2 = gtk.CellRendererText()
self.items_list_treeview.insert_column_with_attributes(-1,
_('Jabber ID'), renderer2, text = 1)
renderer3 = gtk.CellRendererText()
self.items_list_treeview.insert_column_with_attributes(-1,
_('Name'), renderer3, text = 2)
renderer4 = gtk.CellRendererText()
self.items_list_treeview.insert_column_with_attributes(-1,
_('Groups'), renderer4, text = 3)
# Init contacts
model = self.items_list_treeview.get_model()
model.clear()
if action == 'add':
for jid in self.exchange_list:
groups = ''
is_in_roster = True
contact = gajim.contacts.get_contact_with_highest_priority(\
self.account, jid)
if not contact:
is_in_roster = False
name = self.exchange_list[jid][0]
num_list = len(self.exchange_list[jid][1])
current = 0
for group in self.exchange_list[jid][1]:
current += 1
if contact and not group in contact.groups:
is_in_roster = False
if current == num_list:
groups = groups + group
else:
groups = groups + group + ', '
if not is_in_roster:
iter = model.append()
model.set(iter, 0, True, 1, jid, 2, name, 3, groups)
elif action == 'modify':
for jid in self.exchange_list:
groups = ''
is_in_roster = True
is_right = True
contact = gajim.contacts.get_contact_with_highest_priority(\
self.account, jid)
name = self.exchange_list[jid][0]
if not contact:
is_in_roster = False
is_right = False
else:
if name != contact.name:
is_right = False
num_list = len(self.exchange_list[jid][1])
current = 0
for group in self.exchange_list[jid][1]:
current += 1
if contact and not group in contact.groups:
is_right = False
if current == num_list:
groups = groups + group
else:
groups = groups + group + ', '
if not is_right and is_in_roster:
iter = model.append()
model.set(iter, 0, True, 1, jid, 2, name, 3, groups)
elif action == 'delete':
for jid in self.exchange_list:
groups = ''
is_in_roster = True
contact = gajim.contacts.get_contact_with_highest_priority(\
self.account, jid)
name = self.exchange_list[jid][0]
if not contact:
is_in_roster = False
num_list = len(self.exchange_list[jid][1])
current = 0
for group in self.exchange_list[jid][1]:
current += 1
if current == num_list:
groups = groups + group
else:
groups = groups + group + ', '
if not is_right and is_in_roster:
iter = model.append()
model.set(iter, 0, True, 1, jid, 2, name, 3, groups)
self.window.show_all()
self.xml.signal_autoconnect(self)
def toggled_callback(self, cell, path):
model = self.items_list_treeview.get_model()
iter = model.get_iter(path)
model[iter][0] = not cell.get_active()
def on_accept_button_clicked(self, widget):
model = self.items_list_treeview.get_model()
iter = model.get_iter_root()
if self.action == 'add':
a = 0
while iter:
if model[iter][0]:
a+=1
# it is selected
#remote_jid = model[iter][1].decode('utf-8')
message = _('%s suggested me to add you in my roster.' % self.jid_from)
# keep same groups and same nickname
groups = model[iter][3].split(', ')
if groups == ['']:
groups = []
gajim.interface.roster.req_sub(self, model[iter][1], message,
self.account, groups = groups,
nickname = model[iter][2], auto_auth = True)
iter = model.iter_next(iter)
InformationDialog('Added %s contacts' % str(a))
elif self.action == 'modify':
a = 0
while iter:
if model[iter][0]:
a+=1
# it is selected
jid = model[iter][1].decode('utf-8')
# message = _('%s suggested me to add you in my roster.' % self.jid_from)
# keep same groups and same nickname
groups = model[iter][3].split(', ')
if groups == ['']:
groups = []
for u in gajim.contacts.get_contact(self.account, jid):
u.name = model[iter][2]
gajim.connections[self.account].update_contact(jid, model[iter][2], groups)
self.draw_contact(jid, account)
# Update opened chat
ctrl = gajim.interface.msg_win_mgr.get_control(jid, self.account)
if ctrl:
ctrl.update_ui()
win = gajim.interface.msg_win_mgr.get_window(jid, self.account)
win.redraw_tab(ctrl)
win.show_title()
iter = model.iter_next(iter)
elif self.action == 'delete':
a = 0
while iter:
if model[iter][0]:
a+=1
# it is selected
jid = model[iter][1].decode('utf-8')
gajim.connections[self.account].unsubscribe(jid)
for c in gajim.contacts.get_contact(self.account, jid):
self.remove_contact(c, self.account)
gajim.contacts.remove_jid(self.account, jid)
iter = model.iter_next(iter)
InformationDialog('Added %s contacts' % str(a))
self.window.destroy()
def on_cancel_button_clicked(self, widget):
self.window.destroy()
class PrivacyListWindow:
'''Window that is used for creating NEW or EDITING already there privacy
lists'''

View File

@ -2060,6 +2060,10 @@ class Interface:
if 'pep_services' in self.instances[account]:
self.instances[account]['pep_services'].config(data[0], data[1])
def handle_event_roster_item_exchange(self, account, data):
# data = (action in [add, delete, modify], exchange_list, jid_from)
dialogs.RosterItemExchangeWindow(account, data[0], data[1], data[2])
def handle_event_unique_room_id_supported(self, account, data):
'''Receive confirmation that unique_room_id are supported'''
# ('UNIQUE_ROOM_ID_SUPPORTED', server, instance, room_id)
@ -2294,6 +2298,7 @@ class Interface:
'SEARCH_FORM': self.handle_event_search_form,
'SEARCH_RESULT': self.handle_event_search_result,
'RESOURCE_CONFLICT': self.handle_event_resource_conflict,
'ROSTERX': self.handle_event_roster_item_exchange,
'PEP_CONFIG': self.handle_event_pep_config,
'UNIQUE_ROOM_ID_UNSUPPORTED': \
self.handle_event_unique_room_id_unsupported,