we can now get meta contacts from server and display them

This commit is contained in:
Yann Leboulanger 2006-01-26 11:23:15 +00:00
parent e134d7b8cd
commit 4f21775305
4 changed files with 231 additions and 62 deletions

View File

@ -57,6 +57,7 @@ USE_GPG = GnuPG.USE_GPG
from common import i18n
_ = i18n._
send_sha_in_gc_presence = True
# determine which DNS resolution library is available
HAS_DNSPYTHON = False
@ -1454,35 +1455,49 @@ class Connection:
Private Data (JEP 048 and 049)
'''
gajim.log.debug('PrivateCB')
storage = iq_obj.getTag('query').getTag('storage')
try:
query = iq_obj.getTag('query')
storage = query.getTag('storage')
if storage:
ns = storage.getNamespace()
except AttributeError:
#Its a result for a 'set' Iq, so we don't do anything here
return
if ns == 'storage:bookmarks':
# Bookmarked URLs and Conferences
# http://www.jabber.org/jeps/jep-0048.html
confs = storage.getTags('conference')
for conf in confs:
autojoin_val = conf.getAttr('autojoin')
if autojoin_val is None: # not there (it's optional)
autojoin_val = False
bm = { 'name': conf.getAttr('name'),
'jid': conf.getAttr('jid'),
'autojoin': autojoin_val,
'password': conf.getTagData('password'),
'nick': conf.getTagData('nick') }
if ns == 'storage:bookmarks':
#Bookmarked URLs and Conferences
#http://www.jabber.org/jeps/jep-0048.html
confs = storage.getTags('conference')
for conf in confs:
autojoin_val = conf.getAttr('autojoin')
if autojoin_val is None: # not there (it's optional)
autojoin_val = False
bm = { 'name': conf.getAttr('name'),
'jid': conf.getAttr('jid'),
'autojoin': autojoin_val,
'password': conf.getTagData('password'),
'nick': conf.getTagData('nick') }
self.bookmarks.append(bm)
self.dispatch('BOOKMARKS', self.bookmarks)
self.bookmarks.append(bm)
self.dispatch('BOOKMARKS', self.bookmarks)
gajim_tag = query.getTag('gajim')
if gajim_tag:
ns = gajim_tag.getNamespace()
if ns == 'gajim:metacontacts':
# Meta contacts list
children_list = {}
for child in gajim_tag.getChildren():
parent_jid = child.getAttr('name')
if not parent_jid:
continue
children_list[parent_jid] = []
for cchild in child.getChildren():
children_list[parent_jid].append(cchild.getAttr('name'))
self.dispatch('META_CONTACTS', children_list)
# We can now continue connection by requesting the roster
self.connection.initRoster()
elif ns == 'gajim:prefs':
#Preferences data
#http://www.jabber.org/jeps/jep-0049.html
#TODO: implement this
pass
elif ns == 'gajim:prefs':
# Preferences data
# http://www.jabber.org/jeps/jep-0049.html
#TODO: implement this
pass
def build_http_auth_answer(self, iq_obj, answer):
if answer == 'yes':
@ -1950,7 +1965,8 @@ class Connection:
self.continue_connect_info = [show, msg, signed]
self.connection = self.connect_and_auth()
if self.connection:
self.connection.initRoster()
# Ask meta_contacts before roster
self.get_meta_contacts()
def change_status(self, show, msg, sync = False, auto = False):
if sync:
@ -2298,13 +2314,13 @@ class Connection:
self.to_be_sent.append(iq)
def get_bookmarks(self):
''' Get Bookmarks from storage as described in JEP 0048 '''
'''Get Bookmarks from storage as described in JEP 0048'''
self.bookmarks = [] #avoid multiple bookmarks when re-connecting
if not self.connection:
return
iq = common.xmpp.Iq(typ='get')
iq2 = iq.addChild(name="query", namespace="jabber:iq:private")
iq2.addChild(name="storage", namespace="storage:bookmarks")
iq2 = iq.addChild(name='query', namespace='jabber:iq:private')
iq2.addChild(name='storage', namespace='storage:bookmarks')
self.to_be_sent.append(iq)
def store_bookmarks(self):
@ -2328,6 +2344,27 @@ class Connection:
iq5 = iq4.setTagData('password', bm['password'])
self.to_be_sent.append(iq)
def get_meta_contacts(self):
'''Get meta_contacts list from storage as described in JEP 0049'''
if not self.connection:
return
iq = common.xmpp.Iq(typ='get')
iq2 = iq.addChild(name='query', namespace='jabber:iq:private')
iq2.addChild(name='gajim', namespace='gajim:metacontacts')
self.to_be_sent.append(iq)
def store_meta_contacts(self, children_list):
''' Send meta contacts to the storage namespace '''
if not self.connection:
return
iq = common.xmpp.Iq(typ='set')
iq2 = iq.addChild(name='query', namespace='jabber:iq:private')
iq3 = iq2.addChild(name='gajim', namespace='gajim:metacontacts')
for parent_jid in children_list:
parent_tag = iq3.addChild(name='parent', attrs = {'name': parent_jid})
for child_jid in children_list[parent_tag]:
parent_tag.addChild(name='child', attrs = {'name': child_jid})
def send_agent_status(self, agent, ptype):
if not self.connection:
return

View File

@ -85,17 +85,27 @@ class Contacts:
def __init__(self):
self._contacts = {} # list of contacts {acct: {jid1: [C1, C2]}, } one Contact per resource
self._gc_contacts = {} # list of contacts that are in gc {acct: {room_jid: {nick: C}}}
self._sub_contacts = {} # {acct: {jid1: jid2}} means jid1 is sub of jid2
# For meta contacts:
self._children_meta_contacts = {}
self._parent_meta_contacts = {}
def change_account_name(self, old_name, new_name):
self._contacts[new_name] = self._contacts[old_name]
self._gc_contacts[new_name] = self._gc_contacts[old_name]
self._children_meta_contacts[new_name] = self._children_meta_contacts[old_name]
self._parent_meta_contacts[new_name] = self._parent_meta_contacts[old_name]
del self._contacts[old_name]
del self._gc_contacts[old_name]
del self._children_meta_contacts[old_name]
del self._parent_meta_contacts[old_name]
def add_account(self, account):
self._contacts[account] = {}
self._gc_contacts[account] = {}
if not self._children_meta_contacts.has_key(account):
self._children_meta_contacts[account] = {}
self._parent_meta_contacts[account] = {}
def get_accounts(self):
return self._contacts.keys()
@ -103,6 +113,8 @@ class Contacts:
def remove_account(self, account):
del self._contacts[account]
del self._gc_contacts[account]
del self._children_meta_contacts[account]
del self._parent_meta_contacts[account]
def create_contact(self, jid='', name='', groups=[], show='', status='',
sub='', ask='', resource='', priority=5, keyID='', our_chatstate=None,
@ -167,10 +179,6 @@ class Contacts:
return c
return None
def is_subcontact(self, account, contact):
if contact.jid in self._sub_contacts[account]:
return True
def get_contacts_from_jid(self, account, jid):
''' we may have two or more resources on that jid '''
if jid in self._contacts[account]:
@ -201,22 +209,52 @@ class Contacts:
return self._contacts[account][jid][0]
return None
def define_meta_contacts(self, account, children_list):
self._children_meta_contacts[account] = children_list
self._parent_meta_contacts[account] = {}
for parent_jid in children_list:
for children_jid in children_list[parent_jid]:
self._parent_meta_contacts[account][children_jid] = parent_jid
def is_subcontact(self, account, contact):
jid = contact.jid
if jid in self._parent_meta_contacts[account] and \
self._contacts[account].has_key(
self._parent_meta_contacts[account][jid]):
return True
def has_children(self, account, contact):
jid = contact.jid
if jid in self._children_meta_contacts[account]:
for c in self._children_meta_contacts[account][jid]:
if self._contacts[account].has_key(c):
return True
def get_children_contacts(self, account, contact):
'''Returns the children contacts of contact if it's a parent-contact,
else []'''
jid = contact.jid
if jid not in self._children_meta_contacts[account]:
return []
contacts = []
for j in self._children_meta_contacts[account][jid]:
contacts.append(self.get_contact_with_highest_priority(account, j))
return contacts
def get_parent_contact(self, account, contact):
'''Returns the parent contact of contact if it's a sub-contact,
else contact'''
if is_subcontact(account, contact):
parrent_jid = self._sub_contacts[account][contact.jid]
return self.get_contact_with_highest_priority(account,
parrent_jid)
if self.is_subcontact(account, contact):
parent_jid = self._parent_meta_contacts[account][contact.jid]
return self.get_contact_with_highest_priority(account, parent_jid)
return contact
def get_master_contact(self, account, contact):
'''Returns the master contact of contact (parent of parent...) if it's a
sub-contact, else contact'''
while is_subcontact(account, contact):
parrent_jid = self._sub_contacts[account][contact.jid]
contact = self.get_contact_with_highest_priority(account,
parrent_jid)
while self.is_subcontact(account, contact):
parent_jid = self._parent_meta_contacts[account][contact.jid]
contact = self.get_contact_with_highest_priority(account, parent_jid)
return contact
def is_pm_from_jid(self, account, jid):

View File

@ -1085,6 +1085,9 @@ class Interface:
password = gajim.gc_passwords[room_jid]
gajim.connections[account].join_gc(nick, room, server, password)
def handle_event_meta_contacts(self, account, children_list):
gajim.contacts.define_meta_contacts(account, children_list)
def read_sleepy(self):
'''Check idle status and change that status if needed'''
if not self.sleeper.poll():
@ -1327,6 +1330,7 @@ class Interface:
'VCARD_NOT_PUBLISHED': self.handle_event_vcard_not_published,
'ASK_NEW_NICK': self.handle_event_ask_new_nick,
'SIGNED_IN': self.handle_event_signed_in,
'META_CONTACTS': self.handle_event_meta_contacts,
}
def exec_event(self, account):

View File

@ -113,6 +113,12 @@ class RosterWindow:
if jid == model[contact_iter][C_JID].decode('utf-8') and \
account == model[contact_iter][C_ACCOUNT].decode('utf-8'):
found.append(contact_iter)
sub_contact_iter = model.iter_children(contact_iter)
while sub_contact_iter:
if jid == model[sub_contact_iter][C_JID].decode('utf-8') and \
account == model[sub_contact_iter][C_ACCOUNT].decode('utf-8'):
found.append(sub_contact_iter)
sub_contact_iter = model.iter_next(sub_contact_iter)
contact_iter = model.iter_next(contact_iter)
group_iter = model.iter_next(group_iter)
return found
@ -147,16 +153,39 @@ class RosterWindow:
gajim.newly_added[account].remove(jid)
self.draw_contact(jid, account)
def add_contact_to_roster(self, jid, account):
def add_contact_to_roster(self, jid, account, force = False):
'''Add a contact to the roster and add groups if they aren't in roster'''
showOffline = gajim.config.get('showoffline')
contact = gajim.contacts.get_first_contact_from_jid(account, jid)
if not contact:
return
# If contact already in roster, do not add it
if len(self.get_contact_iter(contact.jid, account)):
return
if contact.jid.find('@') <= 0:
# if not '@' or '@' starts the jid ==> agent
contact.groups = [_('Transports')]
model = self.tree.get_model()
if gajim.contacts.is_subcontact(account, contact):
if contact.show in ('offline', 'error') and \
not showOffline and not gajim.awaiting_events[account].has_key(jid):
return
parent_contact = gajim.contacts.get_parent_contact(account, contact)
# is parent shown ?
parent_iters = self.get_contact_iter(parent_contact.jid, account)
if not len(parent_iters):
# Add parent and children
self.add_contact_to_roster(parent_contact.jid, account, True)
return
name = contact.get_shown_name()
for i in parent_iters:
# we add some values here. see draw_contact for more
model.append(i, (None, name, 'contact', contact.jid, account,
False, None))
self.draw_contact(contact.jid, account)
self.draw_avatar(contact.jid, account)
return
# JEP-0162
hide = True
if contact.sub in ('both', 'to'):
@ -171,12 +200,17 @@ class RosterWindow:
observer = True
if (contact.show in ('offline', 'error') or hide) and \
not showOffline and (not _('Transports') in contact.groups or \
gajim.connections[account].connected < 2) and \
not gajim.awaiting_events[account].has_key(jid):
not showOffline and (not _('Transports') in contact.groups or \
gajim.connections[account].connected < 2) and \
not gajim.awaiting_events[account].has_key(jid) and not force:
return
model = self.tree.get_model()
# Remove child contacts that are already in roster to add them
# under this iter
children_contacts = gajim.contacts.get_children_contacts(account,
contact)
for cc in children_contacts:
self.remove_contact(cc, account)
groups = contact.groups
if observer:
groups = [_('Observers')]
@ -212,6 +246,9 @@ class RosterWindow:
self.tree.expand_row(model.get_path(iterG), False)
self.draw_contact(jid, account)
self.draw_avatar(jid, account)
# put the children under this iter
for cc in children_contacts:
self.add_contact_to_roster(cc.jid, account)
def add_transport_to_roster(self, account, transport):
c = gajim.contacts.create_contact(jid = transport, name = transport,
@ -227,7 +264,14 @@ class RosterWindow:
return
if contact.jid in gajim.to_be_removed[account]:
gajim.to_be_removed[account].remove(contact.jid)
if gajim.config.get('showoffline'):
has_connected_children = False
children_contacts = gajim.contacts.get_children_contacts(account,
contact)
for cc in children_contacts:
if cc.show not in ('offline', 'error'):
has_connected_children = True
break
if gajim.config.get('showoffline') or has_connected_children:
self.draw_contact(contact.jid, account)
return
self.remove_contact(contact, account)
@ -239,8 +283,23 @@ class RosterWindow:
model = self.tree.get_model()
for i in self.get_contact_iter(contact.jid, account):
parent_i = model.iter_parent(i)
group = model.get_value(parent_i, 3).decode('utf-8')
model.remove(i)
if gajim.contacts.is_subcontact(account, contact):
# Is it the last subcontact with offline parent?
parent_contact = gajim.contacts.get_parent_contact(account, contact)
if parent_contact.show in ('offline', 'error'):
has_another_child = False
children_contacts = gajim.contacts.get_children_contacts(account,
contact)
for cc in children_contacts:
if len(self.get_contact_iter(cc.jid, account)):
has_another_child = True
break
if not has_another_child:
# Remove parent contact
self.remove_contact(parent_contact, account)
return
group = model[parent_i][C_JID].decode('utf-8')
if model.iter_n_children(parent_i) == 0:
model.remove(parent_i)
# We need to check all contacts, even offline contacts
@ -306,13 +365,19 @@ class RosterWindow:
name += '\n<span size="small" style="italic" foreground="%s">%s</span>'\
% (colorstring, gtkgui_helpers.escape_for_pango_markup(status))
icon_name = helpers.get_icon_name_to_show(contact, account)
state_images = self.get_appropriate_state_images(jid, size = '16')
img = state_images[icon_name]
for iter in iters:
icon_name = helpers.get_icon_name_to_show(contact, account)
path = model.get_path(iter)
if icon_name in ('error', 'offline') and gajim.contacts.has_children(
account, contact) and not self.tree.row_expanded(path):
# get children icon
#FIXME: improve that
cc = gajim.contacts.get_children_contacts(account, contact)[0]
icon_name = helpers.get_icon_name_to_show(cc)
state_images = self.get_appropriate_state_images(jid, size = '16')
img = state_images[icon_name]
model[iter][C_IMG] = img
model[iter][C_NAME] = name
@ -1047,6 +1112,8 @@ class RosterWindow:
add_to_roster_menuitem.connect('activate',
self.on_add_to_roster, contact, account)
#TODO create menu for sub contacts
event_button = self.get_possible_button_event(event)
roster_contact_context_menu.popup(None, None, None, event_button,
@ -1384,6 +1451,7 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
iter = model.get_iter(path)
type = model[iter][C_TYPE]
if type in ('agent', 'contact'):
#TODO
account = model[iter][C_ACCOUNT].decode('utf-8')
jid = model[iter][C_JID].decode('utf-8')
win = None
@ -1427,8 +1495,8 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
model = self.tree.get_model()
iter = model.get_iter(path)
type = model[iter][C_TYPE]
if type == 'group':
if x < 20: # first cell in 1st column (the arrow SINGLE clicked)
if type in ('group', 'contact'):
if x < 27: # first cell in 1st column (the arrow SINGLE clicked)
if (self.tree.row_expanded(path)):
self.tree.collapse_row(path)
else:
@ -2094,6 +2162,10 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
if groupIter and gajim.groups[account][g]['expand']:
pathG = model.get_path(groupIter)
self.tree.expand_row(pathG, False)
elif type == 'contact':
jid = model[iter][C_JID].decode('utf-8')
account = model[iter][C_ACCOUNT].decode('utf-8')
self.draw_contact(jid, account)
def on_roster_treeview_row_collapsed(self, widget, iter, path):
'''When a row is collapsed :
@ -2116,6 +2188,10 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
account = accounts[0] # There is only one cause we don't use merge
if not account in self.collapsed_rows:
self.collapsed_rows.append(account)
elif type == 'contact':
jid = model[iter][C_JID].decode('utf-8')
account = model[iter][C_ACCOUNT].decode('utf-8')
self.draw_contact(jid, account)
def on_editing_started(self, cell, event, row):
''' start editing a cell in the tree'''
@ -2288,7 +2364,7 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
renderer.set_property('cell-background', color)
else:
renderer.set_property('cell-background', None)
renderer.set_property('xalign', 0.5)
renderer.set_property('xalign', 0.2)
else:
jid = model[iter][C_JID].decode('utf-8')
account = model[iter][C_ACCOUNT].decode('utf-8')
@ -2302,8 +2378,12 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
renderer.set_property('cell-background', color)
else:
renderer.set_property('cell-background', None)
renderer.set_property('xalign', 1)
renderer.set_property('width', 20)
parent_iter = model.iter_parent(iter)
if model[parent_iter][C_TYPE] == 'contact':
renderer.set_property('xalign', 1)
else:
renderer.set_property('xalign', 0.4)
renderer.set_property('width', 26)
def nameCellDataFunc(self, column, renderer, model, iter, data = None):
'''When a row is added, set properties for name renderer'''
@ -2357,7 +2437,11 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
renderer.set_property('cell-background', None)
renderer.set_property('font',
gtkgui_helpers.get_theme_font_for_option(theme, 'contactfont'))
renderer.set_property('xpad', 8)
parent_iter = model.iter_parent(iter)
if model[parent_iter][C_TYPE] == 'contact':
renderer.set_property('xpad', 16)
else:
renderer.set_property('xpad', 8)
def fill_secondary_pixbuf_rederer(self, column, renderer, model, iter, data=None):
'''When a row is added, set properties for secondary renderer (avatar or padlock)'''
@ -2737,6 +2821,12 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
showOffline)
# columns
# col = gtk.TreeViewColumn()
# render_image = cell_renderer_image.CellRendererImage(0, 0)
# col.pack_start(render_image, expand = False)
# col.add_attribute(render_image, 'image', C_IMG)
# self.tree.append_column(col)
# self.tree.set_expander_column(col)
# this col has two cells: first one img, second one text
col = gtk.TreeViewColumn()
@ -2770,7 +2860,7 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
self.tree.append_column(col)
col.set_visible(False)
self.tree.set_expander_column(col)
#signals
self.TARGET_TYPE_URI_LIST = 80
TARGETS = [('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0)]