Package common :: Module contacts
[hide private]
[frames] | no frames]

Source Code for Module common.contacts

  1  # -*- coding:utf-8 -*- 
  2  ## src/common/contacts.py 
  3  ## 
  4  ## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com> 
  5  ##                    Travis Shirk <travis AT pobox.com> 
  6  ##                    Nikos Kouremenos <kourem AT gmail.com> 
  7  ## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org> 
  8  ##                         Jean-Marie Traissard <jim AT lapin.org> 
  9  ## Copyright (C) 2007 Lukas Petrovicky <lukas AT petrovicky.net> 
 10  ##                    Tomasz Melcer <liori AT exroot.org> 
 11  ##                    Julien Pivotto <roidelapluie AT gmail.com> 
 12  ## Copyright (C) 2007-2008 Stephan Erb <steve-e AT h3c.de> 
 13  ## Copyright (C) 2008 Brendan Taylor <whateley AT gmail.com> 
 14  ##                    Jonathan Schleifer <js-gajim AT webkeks.org> 
 15  ## 
 16  ## This file is part of Gajim. 
 17  ## 
 18  ## Gajim is free software; you can redistribute it and/or modify 
 19  ## it under the terms of the GNU General Public License as published 
 20  ## by the Free Software Foundation; version 3 only. 
 21  ## 
 22  ## Gajim is distributed in the hope that it will be useful, 
 23  ## but WITHOUT ANY WARRANTY; without even the implied warranty of 
 24  ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 25  ## GNU General Public License for more details. 
 26  ## 
 27  ## You should have received a copy of the GNU General Public License 
 28  ## along with Gajim. If not, see <http://www.gnu.org/licenses/>. 
 29  ## 
 30   
 31   
 32  from common import caps_cache 
 33  from common.account import Account 
 34  import common.gajim 
 35   
36 -class XMPPEntity(object):
37 """ 38 Base representation of entities in XMPP 39 """ 40
41 - def __init__(self, jid, account, resource):
42 self.jid = jid 43 self.resource = resource 44 self.account = account
45
46 -class CommonContact(XMPPEntity):
47
48 - def __init__(self, jid, account, resource, show, status, name, 49 our_chatstate, composing_xep, chatstate, client_caps=None):
50 51 XMPPEntity.__init__(self, jid, account, resource) 52 53 self.show = show 54 self.status = status 55 self.name = name 56 57 self.client_caps = client_caps or caps_cache.NullClientCaps() 58 59 # please read xep-85 http://www.xmpp.org/extensions/xep-0085.html 60 # we keep track of xep85 support with the peer by three extra states: 61 # None, False and 'ask' 62 # None if no info about peer 63 # False if peer does not support xep85 64 # 'ask' if we sent the first 'active' chatstate and are waiting for reply 65 # this holds what WE SEND to contact (our current chatstate) 66 self.our_chatstate = our_chatstate 67 # tell which XEP we're using for composing state 68 # None = have to ask, XEP-0022 = use this xep, 69 # XEP-0085 = use this xep, False = no composing support 70 self.composing_xep = composing_xep 71 # this is contact's chatstate 72 self.chatstate = chatstate
73
74 - def get_full_jid(self):
75 raise NotImplementedError
76
77 - def get_shown_name(self):
78 raise NotImplementedError
79
80 - def supports(self, requested_feature):
81 """ 82 Return True if the contact has advertised to support the feature 83 identified by the given namespace. False otherwise. 84 """ 85 if self.show == 'offline': 86 # Unfortunately, if all resources are offline, the contact 87 # includes the last resource that was online. Check for its 88 # show, so we can be sure it's existant. Otherwise, we still 89 # return caps for a contact that has no resources left. 90 return False 91 else: 92 return caps_cache.client_supports(self.client_caps, requested_feature)
93 94
95 -class Contact(CommonContact):
96 """ 97 Information concerning a contact 98 """
99 - def __init__(self, jid, account, name='', groups=[], show='', status='', 100 sub='', ask='', resource='', priority=0, keyID='', client_caps=None, 101 our_chatstate=None, chatstate=None, last_status_time=None, msg_id= 102 None, composing_xep=None, last_activity_time=None):
103 104 CommonContact.__init__(self, jid, account, resource, show, status, name, 105 our_chatstate, composing_xep, chatstate, client_caps=client_caps) 106 107 self.contact_name = '' # nick choosen by contact 108 self.groups = [i for i in set(groups)] # filter duplicate values 109 110 self.sub = sub 111 self.ask = ask 112 113 self.priority = priority 114 self.keyID = keyID 115 self.msg_id = msg_id 116 self.last_status_time = last_status_time 117 self.last_activity_time = last_activity_time 118 119 self.pep = {}
120
121 - def get_full_jid(self):
122 if self.resource: 123 return self.jid + '/' + self.resource 124 return self.jid
125
126 - def get_shown_name(self):
127 if self.name: 128 return self.name 129 if self.contact_name: 130 return self.contact_name 131 return self.jid.split('@')[0]
132
133 - def get_shown_groups(self):
134 if self.is_observer(): 135 return [_('Observers')] 136 elif self.is_groupchat(): 137 return [_('Groupchats')] 138 elif self.is_transport(): 139 return [_('Transports')] 140 elif not self.groups: 141 return [_('General')] 142 else: 143 return self.groups
144
145 - def is_hidden_from_roster(self):
146 """ 147 If contact should not be visible in roster 148 """ 149 # XEP-0162: http://www.xmpp.org/extensions/xep-0162.html 150 if self.is_transport(): 151 return False 152 if self.sub in ('both', 'to'): 153 return False 154 if self.sub in ('none', 'from') and self.ask == 'subscribe': 155 return False 156 if self.sub in ('none', 'from') and (self.name or len(self.groups)): 157 return False 158 if _('Not in Roster') in self.groups: 159 return False 160 return True
161
162 - def is_observer(self):
163 # XEP-0162: http://www.xmpp.org/extensions/xep-0162.html 164 is_observer = False 165 if self.sub == 'from' and not self.is_transport()\ 166 and self.is_hidden_from_roster(): 167 is_observer = True 168 return is_observer
169
170 - def is_groupchat(self):
171 for account in common.gajim.gc_connected: 172 if self.jid in common.gajim.gc_connected[account]: 173 return True 174 return False
175
176 - def is_transport(self):
177 # if not '@' or '@' starts the jid then contact is transport 178 return self.jid.find('@') <= 0
179 180
181 -class GC_Contact(CommonContact):
182 """ 183 Information concerning each groupchat contact 184 """ 185
186 - def __init__(self, room_jid, account, name='', show='', status='', role='', 187 affiliation='', jid='', resource='', our_chatstate=None, 188 composing_xep=None, chatstate=None):
189 190 CommonContact.__init__(self, jid, account, resource, show, status, name, 191 our_chatstate, composing_xep, chatstate) 192 193 self.room_jid = room_jid 194 self.role = role 195 self.affiliation = affiliation
196
197 - def get_full_jid(self):
198 return self.room_jid + '/' + self.name
199
200 - def get_shown_name(self):
201 return self.name
202
203 - def as_contact(self):
204 """ 205 Create a Contact instance from this GC_Contact instance 206 """ 207 return Contact(jid=self.get_full_jid(), account=self.account, 208 name=self.name, groups=[], show=self.show, status=self.status, 209 sub='none', client_caps=self.client_caps)
210 211
212 -class LegacyContactsAPI:
213 """ 214 This is a GOD class for accessing contact and groupchat information. 215 The API has several flaws: 216 217 * it mixes concerns because it deals with contacts, groupchats, 218 groupchat contacts and metacontacts 219 * some methods like get_contact() may return None. This leads to 220 a lot of duplication all over Gajim because it is not sure 221 if we receive a proper contact or just None. 222 223 It is a long way to cleanup this API. Therefore just stick with it 224 and use it as before. We will try to figure out a migration path. 225 """
226 - def __init__(self):
227 self._metacontact_manager = MetacontactManager(self) 228 self._accounts = {}
229
230 - def change_account_name(self, old_name, new_name):
231 self._accounts[new_name] = self._accounts[old_name] 232 self._accounts[new_name].name = new_name 233 del self._accounts[old_name] 234 235 self._metacontact_manager.change_account_name(old_name, new_name)
236
237 - def add_account(self, account_name):
238 self._accounts[account_name] = Account(account_name, Contacts(), 239 GC_Contacts()) 240 self._metacontact_manager.add_account(account_name)
241
242 - def get_accounts(self):
243 return self._accounts.keys()
244
245 - def remove_account(self, account):
246 del self._accounts[account] 247 self._metacontact_manager.remove_account(account)
248
249 - def create_contact(self, jid, account, name='', groups=[], show='', 250 status='', sub='', ask='', resource='', priority=0, keyID='', 251 client_caps=None, our_chatstate=None, chatstate=None, last_status_time=None, 252 composing_xep=None, last_activity_time=None):
253 # Use Account object if available 254 account = self._accounts.get(account, account) 255 return Contact(jid=jid, account=account, name=name, groups=groups, 256 show=show, status=status, sub=sub, ask=ask, resource=resource, 257 priority=priority, keyID=keyID, client_caps=client_caps, 258 our_chatstate=our_chatstate, chatstate=chatstate, 259 last_status_time=last_status_time, composing_xep=composing_xep, 260 last_activity_time=last_activity_time)
261
262 - def create_self_contact(self, jid, account, resource, show, status, priority, 263 name='', keyID=''):
264 conn = common.gajim.connections[account] 265 nick = name or common.gajim.nicks[account] 266 account = self._accounts.get(account, account) # Use Account object if available 267 self_contact = self.create_contact(jid=jid, account=account, 268 name=nick, groups=['self_contact'], show=show, status=status, 269 sub='both', ask='none', priority=priority, keyID=keyID, 270 resource=resource) 271 self_contact.pep = conn.pep 272 return self_contact
273
274 - def create_not_in_roster_contact(self, jid, account, resource='', name='', keyID=''):
275 account = self._accounts.get(account, account) # Use Account object if available 276 return self.create_contact(jid=jid, account=account, resource=resource, 277 name=name, groups=[_('Not in Roster')], show='not in roster', 278 status='', sub='none', keyID=keyID)
279
280 - def copy_contact(self, contact):
281 return self.create_contact(contact.jid, contact.account, 282 name=contact.name, groups=contact.groups, show=contact.show, 283 status=contact.status, sub=contact.sub, ask=contact.ask, 284 resource=contact.resource, priority=contact.priority, 285 keyID=contact.keyID, client_caps=contact.client_caps, 286 our_chatstate=contact.our_chatstate, chatstate=contact.chatstate, 287 last_status_time=contact.last_status_time, 288 composing_xep=contact.composing_xep, 289 last_activity_time=contact.last_activity_time)
290
291 - def add_contact(self, account, contact):
292 if account not in self._accounts: 293 self.add_account(account) 294 return self._accounts[account].contacts.add_contact(contact)
295
296 - def remove_contact(self, account, contact):
297 if account not in self._accounts: 298 return 299 return self._accounts[account].contacts.remove_contact(contact)
300
301 - def remove_jid(self, account, jid, remove_meta=True):
302 self._accounts[account].contacts.remove_jid(jid) 303 if remove_meta: 304 self._metacontact_manager.remove_metacontact(account, jid)
305
306 - def get_contacts(self, account, jid):
307 return self._accounts[account].contacts.get_contacts(jid)
308
309 - def get_contact(self, account, jid, resource=None):
310 return self._accounts[account].contacts.get_contact(jid, resource=resource)
311
312 - def iter_contacts(self, account):
313 for contact in self._accounts[account].contacts.iter_contacts(): 314 yield contact
315
316 - def get_contact_from_full_jid(self, account, fjid):
317 return self._accounts[account].contacts.get_contact_from_full_jid(fjid)
318
319 - def get_first_contact_from_jid(self, account, jid):
320 return self._accounts[account].contacts.get_first_contact_from_jid(jid)
321
322 - def get_contacts_from_group(self, account, group):
323 return self._accounts[account].contacts.get_contacts_from_group(group)
324
325 - def get_contacts_jid_list(self, account):
326 return self._accounts[account].contacts.get_contacts_jid_list()
327
328 - def get_jid_list(self, account):
329 return self._accounts[account].contacts.get_jid_list()
330
331 - def change_contact_jid(self, old_jid, new_jid, account):
332 return self._accounts[account].change_contact_jid(old_jid, new_jid)
333
334 - def get_highest_prio_contact_from_contacts(self, contacts):
335 if not contacts: 336 return None 337 prim_contact = contacts[0] 338 for contact in contacts[1:]: 339 if int(contact.priority) > int(prim_contact.priority): 340 prim_contact = contact 341 return prim_contact
342
343 - def get_contact_with_highest_priority(self, account, jid):
344 contacts = self.get_contacts(account, jid) 345 if not contacts and '/' in jid: 346 # jid may be a fake jid, try it 347 room, nick = jid.split('/', 1) 348 contact = self.get_gc_contact(account, room, nick) 349 return contact 350 return self.get_highest_prio_contact_from_contacts(contacts)
351
352 - def get_nb_online_total_contacts(self, accounts=[], groups=[]):
353 """ 354 Return the number of online contacts and the total number of contacts 355 """ 356 if accounts == []: 357 accounts = self.get_accounts() 358 nbr_online = 0 359 nbr_total = 0 360 for account in accounts: 361 our_jid = common.gajim.get_jid_from_account(account) 362 for jid in self.get_jid_list(account): 363 if jid == our_jid: 364 continue 365 if common.gajim.jid_is_transport(jid) and not \ 366 _('Transports') in groups: 367 # do not count transports 368 continue 369 if self.has_brother(account, jid, accounts) and not \ 370 self.is_big_brother(account, jid, accounts): 371 # count metacontacts only once 372 continue 373 contact = self.get_contact_with_highest_priority(account, jid) 374 if _('Not in roster') in contact.groups: 375 continue 376 in_groups = False 377 if groups == []: 378 in_groups = True 379 else: 380 for group in groups: 381 if group in contact.get_shown_groups(): 382 in_groups = True 383 break 384 385 if in_groups: 386 if contact.show not in ('offline', 'error'): 387 nbr_online += 1 388 nbr_total += 1 389 return nbr_online, nbr_total
390
391 - def __getattr__(self, attr_name):
392 # Only called if self has no attr_name 393 if hasattr(self._metacontact_manager, attr_name): 394 return getattr(self._metacontact_manager, attr_name) 395 else: 396 raise AttributeError(attr_name)
397
398 - def create_gc_contact(self, room_jid, account, name='', show='', status='', 399 role='', affiliation='', jid='', resource=''):
400 account = self._accounts.get(account, account) # Use Account object if available 401 return GC_Contact(room_jid, account, name, show, status, role, affiliation, jid, 402 resource)
403
404 - def add_gc_contact(self, account, gc_contact):
405 return self._accounts[account].gc_contacts.add_gc_contact(gc_contact)
406
407 - def remove_gc_contact(self, account, gc_contact):
408 return self._accounts[account].gc_contacts.remove_gc_contact(gc_contact)
409
410 - def remove_room(self, account, room_jid):
411 return self._accounts[account].gc_contacts.remove_room(room_jid)
412
413 - def get_gc_list(self, account):
414 return self._accounts[account].gc_contacts.get_gc_list()
415
416 - def get_nick_list(self, account, room_jid):
417 return self._accounts[account].gc_contacts.get_nick_list(room_jid)
418
419 - def get_gc_contact(self, account, room_jid, nick):
420 return self._accounts[account].gc_contacts.get_gc_contact(room_jid, nick)
421
422 - def get_nb_role_total_gc_contacts(self, account, room_jid, role):
423 return self._accounts[account].gc_contacts.get_nb_role_total_gc_contacts(room_jid, role)
424 425
426 -class Contacts():
427 """ 428 This is a breakout of the contact related behavior of the old 429 Contacts class (which is not called LegacyContactsAPI) 430 """
431 - def __init__(self):
432 # list of contacts {jid1: [C1, C2]}, } one Contact per resource 433 self._contacts = {}
434
435 - def add_contact(self, contact):
436 if contact.jid not in self._contacts: 437 self._contacts[contact.jid] = [contact] 438 return 439 contacts = self._contacts[contact.jid] 440 # We had only one that was offline, remove it 441 if len(contacts) == 1 and contacts[0].show == 'offline': 442 # Do not use self.remove_contact: it deteles 443 # self._contacts[account][contact.jid] 444 contacts.remove(contacts[0]) 445 # If same JID with same resource already exists, use the new one 446 for c in contacts: 447 if c.resource == contact.resource: 448 self.remove_contact(c) 449 break 450 contacts.append(contact)
451
452 - def remove_contact(self, contact):
453 if contact.jid not in self._contacts: 454 return 455 if contact in self._contacts[contact.jid]: 456 self._contacts[contact.jid].remove(contact) 457 if len(self._contacts[contact.jid]) == 0: 458 del self._contacts[contact.jid]
459
460 - def remove_jid(self, jid):
461 """ 462 Remove all contacts for a given jid 463 """ 464 if jid in self._contacts: 465 del self._contacts[jid]
466
467 - def get_contacts(self, jid):
468 """ 469 Return the list of contact instances for this jid 470 """ 471 return self._contacts.get(jid, [])
472
473 - def get_contact(self, jid, resource=None):
474 ### WARNING ### 475 # This function returns a *RANDOM* resource if resource = None! 476 # Do *NOT* use if you need to get the contact to which you 477 # send a message for example, as a bare JID in Jabber means 478 # highest available resource, which this function ignores! 479 """ 480 Return the contact instance for the given resource if it's given else the 481 first contact is no resource is given or None if there is not 482 """ 483 if jid in self._contacts: 484 if not resource: 485 return self._contacts[jid][0] 486 for c in self._contacts[jid]: 487 if c.resource == resource: 488 return c
489
490 - def iter_contacts(self):
491 for jid in self._contacts.keys(): 492 for contact in self._contacts[jid][:]: 493 yield contact
494
495 - def get_jid_list(self):
496 return self._contacts.keys()
497
498 - def get_contacts_jid_list(self):
499 return [jid for jid, contact in self._contacts.iteritems() if not 500 contact[0].is_groupchat()]
501
502 - def get_contact_from_full_jid(self, fjid):
503 """ 504 Get Contact object for specific resource of given jid 505 """ 506 barejid, resource = common.gajim.get_room_and_nick_from_fjid(fjid) 507 return self.get_contact(barejid, resource)
508
509 - def get_first_contact_from_jid(self, jid):
510 if jid in self._contacts: 511 return self._contacts[jid][0]
512
513 - def get_contacts_from_group(self, group):
514 """ 515 Return all contacts in the given group 516 """ 517 group_contacts = [] 518 for jid in self._contacts: 519 contacts = self.get_contacts(jid) 520 if group in contacts[0].groups: 521 group_contacts += contacts 522 return group_contacts
523
524 - def change_contact_jid(self, old_jid, new_jid):
525 if old_jid not in self._contacts: 526 return 527 self._contacts[new_jid] = [] 528 for _contact in self._contacts[old_jid]: 529 _contact.jid = new_jid 530 self._contacts[new_jid].append(_contact) 531 del self._contacts[old_jid]
532 533
534 -class GC_Contacts():
535
536 - def __init__(self):
537 # list of contacts that are in gc {room_jid: {nick: C}}} 538 self._rooms = {}
539
540 - def add_gc_contact(self, gc_contact):
541 if gc_contact.room_jid not in self._rooms: 542 self._rooms[gc_contact.room_jid] = {gc_contact.name: gc_contact} 543 else: 544 self._rooms[gc_contact.room_jid][gc_contact.name] = gc_contact
545
546 - def remove_gc_contact(self, gc_contact):
547 if gc_contact.room_jid not in self._rooms: 548 return 549 if gc_contact.name not in self._rooms[gc_contact.room_jid]: 550 return 551 del self._rooms[gc_contact.room_jid][gc_contact.name] 552 # It was the last nick in room ? 553 if not len(self._rooms[gc_contact.room_jid]): 554 del self._rooms[gc_contact.room_jid]
555
556 - def remove_room(self, room_jid):
557 if room_jid in self._rooms: 558 del self._rooms[room_jid]
559
560 - def get_gc_list(self):
561 return self._rooms.keys()
562
563 - def get_nick_list(self, room_jid):
564 gc_list = self.get_gc_list() 565 if not room_jid in gc_list: 566 return [] 567 return self._rooms[room_jid].keys()
568
569 - def get_gc_contact(self, room_jid, nick):
570 nick_list = self.get_nick_list(room_jid) 571 if not nick in nick_list: 572 return None 573 return self._rooms[room_jid][nick]
574
575 - def get_nb_role_total_gc_contacts(self, room_jid, role):
576 """ 577 Return the number of group chat contacts for the given role and the total 578 number of group chat contacts 579 """ 580 if room_jid not in self._rooms: 581 return 0, 0 582 nb_role = nb_total = 0 583 for nick in self._rooms[room_jid]: 584 if self._rooms[room_jid][nick].role == role: 585 nb_role += 1 586 nb_total += 1 587 return nb_role, nb_total
588 589
590 -class MetacontactManager():
591
592 - def __init__(self, contacts):
593 self._metacontacts_tags = {} 594 self._contacts = contacts
595
596 - def change_account_name(self, old_name, new_name):
597 self._metacontacts_tags[new_name] = self._metacontacts_tags[old_name] 598 del self._metacontacts_tags[old_name]
599
600 - def add_account(self, account):
601 if account not in self._metacontacts_tags: 602 self._metacontacts_tags[account] = {}
603
604 - def remove_account(self, account):
605 del self._metacontacts_tags[account]
606
607 - def define_metacontacts(self, account, tags_list):
608 self._metacontacts_tags[account] = tags_list
609
610 - def _get_new_metacontacts_tag(self, jid):
611 if not jid in self._metacontacts_tags: 612 return jid 613 #FIXME: can this append ? 614 assert False
615
616 - def iter_metacontacts_families(self, account):
617 for tag in self._metacontacts_tags[account]: 618 family = self._get_metacontacts_family_from_tag(account, tag) 619 yield family
620
621 - def _get_metacontacts_tag(self, account, jid):
622 """ 623 Return the tag of a jid 624 """ 625 if not account in self._metacontacts_tags: 626 return None 627 for tag in self._metacontacts_tags[account]: 628 for data in self._metacontacts_tags[account][tag]: 629 if data['jid'] == jid: 630 return tag 631 return None
632
633 - def add_metacontact(self, brother_account, brother_jid, account, jid, order=None):
634 tag = self._get_metacontacts_tag(brother_account, brother_jid) 635 if not tag: 636 tag = self._get_new_metacontacts_tag(brother_jid) 637 self._metacontacts_tags[brother_account][tag] = [{'jid': brother_jid, 638 'tag': tag}] 639 if brother_account != account: 640 common.gajim.connections[brother_account].store_metacontacts( 641 self._metacontacts_tags[brother_account]) 642 # be sure jid has no other tag 643 old_tag = self._get_metacontacts_tag(account, jid) 644 while old_tag: 645 self.remove_metacontact(account, jid) 646 old_tag = self._get_metacontacts_tag(account, jid) 647 if tag not in self._metacontacts_tags[account]: 648 self._metacontacts_tags[account][tag] = [{'jid': jid, 'tag': tag}] 649 else: 650 if order: 651 self._metacontacts_tags[account][tag].append({'jid': jid, 652 'tag': tag, 'order': order}) 653 else: 654 self._metacontacts_tags[account][tag].append({'jid': jid, 655 'tag': tag}) 656 common.gajim.connections[account].store_metacontacts( 657 self._metacontacts_tags[account])
658
659 - def remove_metacontact(self, account, jid):
660 if not account in self._metacontacts_tags: 661 return 662 663 found = None 664 for tag in self._metacontacts_tags[account]: 665 for data in self._metacontacts_tags[account][tag]: 666 if data['jid'] == jid: 667 found = data 668 break 669 if found: 670 self._metacontacts_tags[account][tag].remove(found) 671 common.gajim.connections[account].store_metacontacts( 672 self._metacontacts_tags[account]) 673 break
674
675 - def has_brother(self, account, jid, accounts):
676 tag = self._get_metacontacts_tag(account, jid) 677 if not tag: 678 return False 679 meta_jids = self._get_metacontacts_jids(tag, accounts) 680 return len(meta_jids) > 1 or len(meta_jids[account]) > 1
681
682 - def is_big_brother(self, account, jid, accounts):
683 family = self.get_metacontacts_family(account, jid) 684 if family: 685 nearby_family = [data for data in family 686 if account in accounts] 687 bb_data = self._get_metacontacts_big_brother(nearby_family) 688 if bb_data['jid'] == jid and bb_data['account'] == account: 689 return True 690 return False
691
692 - def _get_metacontacts_jids(self, tag, accounts):
693 """ 694 Return all jid for the given tag in the form {acct: [jid1, jid2],.} 695 """ 696 answers = {} 697 for account in self._metacontacts_tags: 698 if tag in self._metacontacts_tags[account]: 699 if account not in accounts: 700 continue 701 answers[account] = [] 702 for data in self._metacontacts_tags[account][tag]: 703 answers[account].append(data['jid']) 704 return answers
705
706 - def get_metacontacts_family(self, account, jid):
707 """ 708 Return the family of the given jid, including jid in the form: 709 [{'account': acct, 'jid': jid, 'order': order}, ] 'order' is optional 710 """ 711 tag = self._get_metacontacts_tag(account, jid) 712 return self._get_metacontacts_family_from_tag(account, tag)
713
714 - def _get_metacontacts_family_from_tag(self, account, tag):
715 if not tag: 716 return [] 717 answers = [] 718 for account in self._metacontacts_tags: 719 if tag in self._metacontacts_tags[account]: 720 for data in self._metacontacts_tags[account][tag]: 721 data['account'] = account 722 answers.append(data) 723 return answers
724
725 - def _compare_metacontacts(self, data1, data2):
726 """ 727 Compare 2 metacontacts 728 729 Data is {'jid': jid, 'account': account, 'order': order} order is 730 optional 731 """ 732 jid1 = data1['jid'] 733 jid2 = data2['jid'] 734 account1 = data1['account'] 735 account2 = data2['account'] 736 contact1 = self._contacts.get_contact_with_highest_priority(account1, jid1) 737 contact2 = self._contacts.get_contact_with_highest_priority(account2, jid2) 738 show_list = ['not in roster', 'error', 'offline', 'invisible', 'dnd', 739 'xa', 'away', 'chat', 'online', 'requested', 'message'] 740 # contact can be null when a jid listed in the metacontact data 741 # is not in our roster 742 if not contact1: 743 if contact2: 744 return -1 # prefer the known contact 745 else: 746 show1 = 0 747 priority1 = 0 748 else: 749 show1 = show_list.index(contact1.show) 750 priority1 = contact1.priority 751 if not contact2: 752 if contact1: 753 return 1 # prefer the known contact 754 else: 755 show2 = 0 756 priority2 = 0 757 else: 758 show2 = show_list.index(contact2.show) 759 priority2 = contact2.priority 760 # If only one is offline, it's always second 761 if show1 > 2 and show2 < 3: 762 return 1 763 if show2 > 2 and show1 < 3: 764 return -1 765 if 'order' in data1 and 'order' in data2: 766 if data1['order'] > data2['order']: 767 return 1 768 if data1['order'] < data2['order']: 769 return -1 770 if 'order' in data1: 771 return 1 772 if 'order' in data2: 773 return -1 774 transport1 = common.gajim.get_transport_name_from_jid(jid1) 775 transport2 = common.gajim.get_transport_name_from_jid(jid2) 776 if transport2 and not transport1: 777 return 1 778 if transport1 and not transport2: 779 return -1 780 if show1 > show2: 781 return 1 782 if show2 > show1: 783 return -1 784 if priority1 > priority2: 785 return 1 786 if priority2 > priority1: 787 return -1 788 server1 = common.gajim.get_server_from_jid(jid1) 789 server2 = common.gajim.get_server_from_jid(jid2) 790 myserver1 = common.gajim.config.get_per('accounts', account1, 'hostname') 791 myserver2 = common.gajim.config.get_per('accounts', account2, 'hostname') 792 if server1 == myserver1: 793 if server2 != myserver2: 794 return 1 795 elif server2 == myserver2: 796 return -1 797 if jid1 > jid2: 798 return 1 799 if jid2 > jid1: 800 return -1 801 # If all is the same, compare accounts, they can't be the same 802 if account1 > account2: 803 return 1 804 if account2 > account1: 805 return -1 806 return 0
807
808 - def get_nearby_family_and_big_brother(self, family, account):
809 """ 810 Return the nearby family and its Big Brother 811 812 Nearby family is the part of the family that is grouped with the 813 metacontact. A metacontact may be over different accounts. If accounts 814 are not merged then the given family is split account wise. 815 816 (nearby_family, big_brother_jid, big_brother_account) 817 """ 818 if common.gajim.config.get('mergeaccounts'): 819 # group all together 820 nearby_family = family 821 else: 822 # we want one nearby_family per account 823 nearby_family = [data for data in family if account == data['account']] 824 825 big_brother_data = self._get_metacontacts_big_brother(nearby_family) 826 big_brother_jid = big_brother_data['jid'] 827 big_brother_account = big_brother_data['account'] 828 829 return (nearby_family, big_brother_jid, big_brother_account)
830
831 - def _get_metacontacts_big_brother(self, family):
832 """ 833 Which of the family will be the big brother under wich all others will be 834 ? 835 """ 836 family.sort(cmp=self._compare_metacontacts) 837 return family[-1]
838