# HG changeset patch # User Goffi # Date 1349909315 -7200 # Node ID 886754295efe70eac8e480b55c6fe5faf2898e9f # Parent 64ff046dc201eaa67f727558bc9914b33e74026b quick frontend, primitivus, wix: MUC private messages management /!\ not fully finished, backend part is not done yet /!\ - as resources are discarded to manage chat windows lists, a pretty dirty hack is done to work around this: full jid is escaped using a prefix (it becomes invalid and resource is preserved). - new quick_utils module, with helper methods. escapePrivate and unescapePrivate implementations - MUC private messages are not managed in Wix yet diff -r 64ff046dc201 -r 886754295efe frontends/src/primitivus/chat.py --- a/frontends/src/primitivus/chat.py Fri Sep 28 00:48:52 2012 +0200 +++ b/frontends/src/primitivus/chat.py Thu Oct 11 00:48:35 2012 +0200 @@ -22,9 +22,9 @@ import urwid from urwid_satext import sat_widgets from urwid_satext.files_management import FileDialog -from sat_frontends.quick_frontend.quick_contact_list import QuickContactList from sat_frontends.quick_frontend.quick_chat import QuickChat from sat_frontends.primitivus.card_game import CardGame +from sat_frontends.quick_frontend.quick_utils import unescapePrivate import time from sat.tools.jid import JID @@ -145,7 +145,7 @@ self.__appendPresentPanel() def __getDecoration(self, widget): - return sat_widgets.LabelLine(widget, sat_widgets.SurroundedText(unicode(self.target))) + return sat_widgets.LabelLine(widget, sat_widgets.SurroundedText(unicode(unescapePrivate(self.target)))) def showDecoration(self, show=True): """Show/Hide the decoration around the chat window""" diff -r 64ff046dc201 -r 886754295efe frontends/src/primitivus/contact_list.py --- a/frontends/src/primitivus/contact_list.py Fri Sep 28 00:48:52 2012 +0200 +++ b/frontends/src/primitivus/contact_list.py Thu Oct 11 00:48:35 2012 +0200 @@ -22,6 +22,7 @@ import urwid from urwid_satext import sat_widgets from sat_frontends.quick_frontend.quick_contact_list import QuickContactList +from sat_frontends.quick_frontend.quick_utils import escapePrivate, unescapePrivate from sat.tools.jid import JID @@ -29,10 +30,10 @@ signals = ['click','change'] def __init__(self, host, on_click=None, on_change=None, user_data=None): + QuickContactList.__init__(self) self.host = host self.selected = None self.groups={} - self.special={} self.alert_jid=set() self.show_status = False self.show_disconnected = False @@ -45,7 +46,6 @@ urwid.connect_signal(self, 'click', on_click, user_data) if on_change: urwid.connect_signal(self, 'change', on_change, user_data) - QuickContactList.__init__(self) def update(self): """Update display, keep focus""" @@ -115,18 +115,23 @@ widgets = [] #list of built widgets for contact in contacts: - jid=JID(contact) - name = self.getCache(jid, 'name') - nick = self.getCache(jid, 'nick') - status = self.getCache(jid, 'status') - show = self.getCache(jid, 'show') - if show == None: - show = "unavailable" - if (not self.show_disconnected and show == "unavailable" - and not contact in self.alert_jid and contact != self.selected): - continue - show_icon, show_attr = const_SHOW_ICON.get(show,('','default')) - contact_disp = ('alert' if contact in self.alert_jid else show_attr, nick or name or jid.node or jid.short) + if contact.startswith(const_PRIVATE_PREFIX): + contact_disp = ('alert' if contact in self.alert_jid else "show_normal", unescapePrivate(contact)) + show_icon = '' + status = '' + else: + jid=JID(contact) + name = self.getCache(jid, 'name') + nick = self.getCache(jid, 'nick') + status = self.getCache(jid, 'status') + show = self.getCache(jid, 'show') + if show == None: + show = "unavailable" + if (not self.show_disconnected and show == "unavailable" + and not contact in self.alert_jid and contact != self.selected): + continue + show_icon, show_attr = const_SHOW_ICON.get(show,('','default')) + contact_disp = ('alert' if contact in self.alert_jid else show_attr, nick or name or jid.node or jid.short) display = [ show_icon + " " , contact_disp] if self.show_status: status_disp = ('status',"\n " + status) if status else "" @@ -147,7 +152,7 @@ def __buildSpecials(self, content): """Build the special entities""" - specials = self.special.keys() + specials = self.specials.keys() specials.sort() for special in specials: jid=JID(special) @@ -168,7 +173,7 @@ content = urwid.SimpleListWalker([]) self.__buildSpecials(content) - if self.special: + if self.specials: content.append(urwid.Divider('=')) group_keys = self.groups.keys() @@ -198,6 +203,7 @@ def clearContacts(self): """clear all the contact list""" + QuickContactList.clearContacts(self) self.groups={} self.selected = None self.unselectAll() @@ -205,7 +211,8 @@ def replace(self, jid, groups=None, attributes=None): """add a contact to the list if doesn't exist, else update it""" - if jid.short in self.special: + QuickContactList.replace(self, jid, groups, attributes) + if jid.short in self.specials: return if not groups: groups = [None] @@ -227,10 +234,10 @@ self.list_wid.changeValues(contacts) self._emit('change')""" - def remove(self, param_jid): + def remove(self, jid): """remove a contact from the list""" + QuickContactList.remove(self, jid) groups_to_remove = [] - jid = JID(param_jid) for group in self.groups: contacts = self.groups[group][1] if jid.short in contacts: @@ -239,10 +246,6 @@ groups_to_remove.append(group) for group in groups_to_remove: del self.groups[group] - try: - del self.special[jid.short] - except KeyError: - pass self.update() def add(self, jid, param_groups=[None]): @@ -254,7 +257,7 @@ @param jid: jid of the entity @param _type: special type (e.g.: "MUC") """ - self.special[special_jid.short] = special_type + QuickContactList.setSpecial(self, special_jid, special_type) if None in self.groups: folded,group_jids = self.groups[None] for group_jid in group_jids: @@ -265,5 +268,5 @@ def updatePresence(self, jid, show, priority, statuses): #XXX: for the moment, we ignore presence updates for special entities - if jid.short not in self.special: + if jid.short not in self.specials: QuickContactList.updatePresence(self, jid, show, priority, statuses) diff -r 64ff046dc201 -r 886754295efe frontends/src/primitivus/primitivus --- a/frontends/src/primitivus/primitivus Fri Sep 28 00:48:52 2012 +0200 +++ b/frontends/src/primitivus/primitivus Thu Oct 11 00:48:35 2012 +0200 @@ -24,8 +24,6 @@ from urwid_satext import sat_widgets from sat_frontends.quick_frontend.quick_app import QuickApp from sat_frontends.quick_frontend.quick_chat_list import QuickChatList -from sat_frontends.quick_frontend.quick_contact_list import QuickContactList -from sat_frontends.quick_frontend.quick_contact_management import QuickContactManagement from sat_frontends.primitivus.profile_manager import ProfileManager from sat_frontends.primitivus.contact_list import ContactList from sat_frontends.primitivus.chat import Chat @@ -47,9 +45,6 @@ class ChatList(QuickChatList): """This class manage the list of chat windows""" - def __init__(self, host): - QuickChatList.__init__(self, host) - def createChat(self, target): return Chat(target, self.host) @@ -300,7 +295,7 @@ if contact: chat = self.chat_wins[contact] try: - self.bridge.sendMessage(contact, + self.sendMessage(contact, editBar.get_edit_text(), mess_type = "groupchat" if chat.type == 'group' else "chat", profile_key=self.profile) @@ -308,18 +303,17 @@ self.notify(_("Error while sending message")) editBar.set_edit_text('') - def newMessage(self, from_jid, msg, type, to_jid, profile): - if not self.check_profile(profile): - return - sender = JID(from_jid) - if not sender in self.contact_list and sender.short != self.profiles[profile]['whoami'].short: + def newMessage(self, from_jid, to_jid, msg, _type, profile): + QuickApp.newMessage(self, from_jid, to_jid, msg, _type, profile) + + if not from_jid in self.contact_list and from_jid.short != self.profiles[profile]['whoami'].short: #XXX: needed to show entities which haven't sent any # presence information and which are not in roster #TODO: put these entities in a "not in roster" list - self.contact_list.replace(sender) - QuickApp.newMessage(self, from_jid, msg, type, to_jid, profile) - if JID(self.contact_list.selected).short != sender.short: - self.contact_list.putAlert(sender) + self.contact_list.replace(from_jid) + + if JID(self.contact_list.selected).short != from_jid.short: + self.contact_list.putAlert(from_jid) def _dialogOkCb(self, widget, data): self.removePopUp() diff -r 64ff046dc201 -r 886754295efe frontends/src/quick_frontend/constants.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/frontends/src/quick_frontend/constants.py Thu Oct 11 00:48:35 2012 +0200 @@ -0,0 +1,23 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +""" +Primitivus: a SAT frontend +Copyright (C) 2009, 2010, 2011, 2012 Jérôme Poisson (goffi@goffi.org) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" +import __builtin__ + +__builtin__.__dict__['const_PRIVATE_PREFIX'] = "@PRIVATE@" diff -r 64ff046dc201 -r 886754295efe frontends/src/quick_frontend/quick_app.py --- a/frontends/src/quick_frontend/quick_app.py Fri Sep 28 00:48:52 2012 +0200 +++ b/frontends/src/quick_frontend/quick_app.py Thu Oct 11 00:48:35 2012 +0200 @@ -22,8 +22,11 @@ from logging import debug, info, warning, error from sat.tools.jid import JID from sat_frontends.bridge.DBus import DBusBridgeFrontend,BridgeExceptionNoService +from sat_frontends.quick_frontend.quick_utils import escapePrivate, unescapePrivate from optparse import OptionParser +import sat_frontends.quick_frontend.constants + import gettext gettext.install('sat_frontend', "../i18n", unicode=True) @@ -46,7 +49,7 @@ self.bridge.register("disconnected", self.disconnected) self.bridge.register("connectionError", self.connectionError) self.bridge.register("newContact", self.newContact) - self.bridge.register("newMessage", self.newMessage) + self.bridge.register("newMessage", self._newMessage) self.bridge.register("newAlert", self.newAlert) self.bridge.register("presenceUpdate", self.presenceUpdate) self.bridge.register("subscribe", self.subscribe) @@ -230,17 +233,48 @@ return entity=JID(JabberId) _groups = list(groups) - self.contact_list._replace(entity, _groups, attributes) - - def newMessage(self, from_jid, msg, type, to_jid, profile): + self.contact_list.replace(entity, _groups, attributes) + + def _newMessage(self, from_jid_s, msg, _type, to_jid_s, profile): + """newMessage premanagement: a dirty hack to manage private messages + if a private MUC message is detected, from_jid or to_jid is prefixed and resource is escaped""" if not self.check_profile(profile): return - sender=JID(from_jid) - addr=JID(to_jid) - win = addr if sender.short == self.profiles[profile]['whoami'].short else sender + from_jid = JID(from_jid_s) + to_jid = JID(to_jid_s) + + from_me = from_jid.short == self.profiles[profile]['whoami'].short + win = to_jid if from_me else from_jid + + if _type != "groupchat" and self.contact_list.getSpecial(win) == "MUC": + #we have a private message in a MUC room + #XXX: normaly we use bare jid as key, here we need the full jid + # so we cheat by replacing the "/" before the resource by + # a "@", so the jid is invalid, + new_jid = escapePrivate(win) + if from_me: + to_jid = new_jid + else: + from_jid = new_jid + if new_jid not in self.contact_list: + self.contact_list.add(new_jid) + + self.newMessage(from_jid, to_jid, msg, _type, profile) + + def newMessage(self, from_jid, to_jid, msg, _type, profile): + from_me = from_jid.short == self.profiles[profile]['whoami'].short + win = to_jid if from_me else from_jid + self.current_action_ids = set() self.current_action_ids_cb = {} - self.chat_wins[win.short].printMessage(sender, msg, profile) + + self.chat_wins[win.short].printMessage(from_jid, msg, profile) + + def sendMessage(self, to_jid, message, subject='', mess_type="auto", profile_key="@DEFAULT@"): + if to_jid.startswith(const_PRIVATE_PREFIX): + to_jid = unescapePrivate(to_jid) + mess_type = "chat" + self.bridge.sendMessage(to_jid, message, subject, mess_type, profile_key) def newAlert(self, msg, title, alert_type, profile): if not self.check_profile(profile): @@ -292,13 +326,13 @@ self.chat_wins[room_jid].setPresents(list(set([user_nick]+room_nicks))) self.contact_list.setSpecial(JID(room_jid), "MUC") - def roomLeft(self, room_jid, profile): + def roomLeft(self, room_jid_s, profile): """Called when a MUC room is left""" if not self.check_profile(profile): return - debug (_("Room [%(room_jid)s] left by %(profile)s") % {'room_jid':room_jid, 'profile': profile}) - del self.chat_wins[room_jid] - self.contact_list.remove(room_jid) + debug (_("Room [%(room_jid)s] left by %(profile)s") % {'room_jid':room_jid_s, 'profile': profile}) + del self.chat_wins[room_jid_s] + self.contact_list.remove(JID(room_jid_s)) def roomUserJoined(self, room_jid, user_nick, user_data, profile): """Called when an user joined a MUC room""" @@ -504,12 +538,12 @@ if key == "nick": if jid in self.contact_list: self.contact_list.setCache(jid, 'nick', value) - self.contact_list._replace(jid) + self.contact_list.replace(jid) elif key == "avatar": if jid in self.contact_list: filename = self.bridge.getAvatarFile(value) self.contact_list.setCache(jid, 'avatar', filename) - self.contact_list._replace(jid) + self.contact_list.replace(jid) def askConfirmation(self, type, id, data): raise NotImplementedError diff -r 64ff046dc201 -r 886754295efe frontends/src/quick_frontend/quick_chat.py --- a/frontends/src/quick_frontend/quick_chat.py Fri Sep 28 00:48:52 2012 +0200 +++ b/frontends/src/quick_frontend/quick_chat.py Thu Oct 11 00:48:35 2012 +0200 @@ -21,7 +21,7 @@ from logging import debug, info, warning, error from sat.tools.jid import JID - +from sat_frontends.quick_frontend.quick_utils import unescapePrivate class QuickChat(): @@ -110,6 +110,8 @@ def _get_nick(self, jid): """Return nick of this jid when possible""" + if jid.startswith(const_PRIVATE_PREFIX): + return unescapePrivate(jid).resource return jid.resource if self.type == "group" else (self.host.contact_list.getCache(jid,'nick') or self.host.contact_list.getCache(jid,'name') or jid.node) def printMessage(self, from_jid, msg, profile, timestamp): diff -r 64ff046dc201 -r 886754295efe frontends/src/quick_frontend/quick_contact_list.py --- a/frontends/src/quick_frontend/quick_contact_list.py Fri Sep 28 00:48:52 2012 +0200 +++ b/frontends/src/quick_frontend/quick_contact_list.py Thu Oct 11 00:48:35 2012 +0200 @@ -28,6 +28,7 @@ def __init__(self): debug(_("Contact List init")) self._cache = {} + self.specials={} def update_jid(self, jid): """Update the jid in the list when something changed""" @@ -51,31 +52,34 @@ def clearContacts(self, jid): """Clear all the contact list""" - raise NotImplementedError + self.specials.clear() - def _replace(self, jid, groups=None, attributes=None): + def replace(self, jid, groups=None, attributes=None): + """add a contact to the list if doesn't exist, else update it""" if attributes and 'name' in attributes: self.setCache(jid, 'name', attributes['name']) - self.replace(jid, groups, attributes) - - def replace(self, jid, groups, attributes): - """add a contact to the list if doesn't exist, else update it""" - raise NotImplementedError def remove(self, jid): """remove a contact from the list""" - raise NotImplementedError + try: + del self.specials[jid.short] + except KeyError: + pass def add(self, jid, param_groups=None): """add a contact to the list""" raise NotImplementedError + def getSpecial(self, jid): + """Return special type of jid, or None if it's not special""" + return self.specials.get(jid.short) + def setSpecial(self, jid, _type): """Set entity as a special @param jid: jid of the entity @param _type: special type (e.g.: "MUC") """ - raise NotImplementedError + self.specials[jid.short] = _type def updatePresence(self, jid, show, priority, statuses): """Update entity's presence status diff -r 64ff046dc201 -r 886754295efe frontends/src/quick_frontend/quick_utils.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/frontends/src/quick_frontend/quick_utils.py Thu Oct 11 00:48:35 2012 +0200 @@ -0,0 +1,33 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +""" +Primitivus: a SAT frontend +Copyright (C) 2009, 2010, 2011, 2012 Jérôme Poisson (goffi@goffi.org) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + +from sat.tools.jid import JID + +def escapePrivate(ori_jid): + """Escape a private jid""" + return JID(const_PRIVATE_PREFIX + ori_jid.short + '@' + ori_jid.resource) + +def unescapePrivate(escaped_jid): + if not escaped_jid.startswith(const_PRIVATE_PREFIX): + return escaped_jid + escaped_split = tuple(escaped_jid[len(const_PRIVATE_PREFIX):].split('@')) + assert(len(escaped_split) == 3) + return JID("%s@%s/%s" % escaped_split) diff -r 64ff046dc201 -r 886754295efe frontends/src/wix/chat.py --- a/frontends/src/wix/chat.py Fri Sep 28 00:48:52 2012 +0200 +++ b/frontends/src/wix/chat.py Thu Oct 11 00:48:35 2012 +0200 @@ -201,7 +201,7 @@ def onEnterPressed(self, event): """Behaviour when enter pressed in send line.""" - self.host.bridge.sendMessage(self.target.short if self.type=='group' else self.target, + self.host.sendMessage(self.target.short if self.type=='group' else self.target, event.GetString(), mess_type = "groupchat" if self.type=='group' else "chat", profile_key=self.host.profile) diff -r 64ff046dc201 -r 886754295efe frontends/src/wix/contact_list.py --- a/frontends/src/wix/contact_list.py Fri Sep 28 00:48:52 2012 +0200 +++ b/frontends/src/wix/contact_list.py Thu Oct 11 00:48:35 2012 +0200 @@ -169,8 +169,7 @@ self.Insert(_present, gp_idx+1, contact) def setSpecial(self, special_jid, special_type): - pass - + QuickContactList.setSpecial(self, special_jid, special_type) def remove(self, contact): """remove a contact from the list""" diff -r 64ff046dc201 -r 886754295efe frontends/src/wix/main_window.py --- a/frontends/src/wix/main_window.py Fri Sep 28 00:48:52 2012 +0200 +++ b/frontends/src/wix/main_window.py Thu Oct 11 00:48:35 2012 +0200 @@ -22,7 +22,6 @@ from sat_frontends.quick_frontend.quick_chat_list import QuickChatList from sat_frontends.quick_frontend.quick_app import QuickApp -from sat_frontends.quick_frontend.quick_contact_management import QuickContactManagement import wx from sat_frontends.wix.contact_list import ContactList from sat_frontends.wix.chat import Chat @@ -31,9 +30,7 @@ from sat_frontends.wix.gateways import GatewaysManager from sat_frontends.wix.profile import Profile from sat_frontends.wix.profile_manager import ProfileManager -import gobject import os.path -import pdb from sat.tools.jid import JID from logging import debug, info, warning, error import sat_frontends.wix.constants @@ -52,9 +49,6 @@ class ChatList(QuickChatList): """This class manage the list of chat windows""" - def __init__(self, host): - QuickChatList.__init__(self, host) - def createChat(self, target): return Chat(target, self.host) @@ -186,8 +180,8 @@ wx.EVT_MENU(self, idFIND_GATEWAYS, self.onFindGateways) - def newMessage(self, from_jid, msg, type, to_jid, profile): - QuickApp.newMessage(self, from_jid, msg, type, to_jid, profile) + def newMessage(self, from_jid, to_jid, msg, _type, profile): + QuickApp.newMessage(self, from_jid, to_jid, msg, _type, profile) def showAlert(self, message): # TODO: place this in a separate class