[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:
parent
3676c55e9d
commit
38fa0be12a
|
@ -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"><b>someone@somewhere.com</b> would like you to <b>add</b> 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>
|
|
@ -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',
|
||||
|
|
|
@ -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 = {}
|
||||
|
|
193
src/dialogs.py
193
src/dialogs.py
|
@ -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'''
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue