backend, frontends: message refactoring (huge commit):
/!\ several features are temporarily disabled, like notifications in frontends
next step in refactoring, with the following changes:
- jp: updated jp message to follow changes in backend/bridge
- jp: added --lang, --subject, --subject_lang, and --type options to jp message + fixed unicode handling for jid
- quick_frontend (QuickApp, QuickChat):
- follow backend changes
- refactored chat, message are now handled in OrderedDict and uid are kept so they can be updated
- Message and Occupant classes handle metadata, so frontend just have to display them
- Primitivus (Chat):
- follow backend/QuickFrontend changes
- info & standard messages are handled in the same MessageWidget class
- improved/simplified handling of messages, removed update() method
- user joined/left messages are merged when next to each other
- a separator is shown when message is received while widget is out of focus, so user can quickly see the new messages
- affiliation/role are shown (in a basic way for now) in occupants panel
- removed "/me" messages handling, as it will be done by a backend plugin
- message language is displayed when available (only one language per message for now)
- fixed :history and :search commands
- core (constants): new constants for messages type, XML namespace, entity type
- core: *Message methods renamed to follow new code sytle (e.g. sendMessageToBridge => messageSendToBridge)
- core (messages handling): fixed handling of language
- core (messages handling): mes_data['from'] and ['to'] are now jid.JID
- core (core.xmpp): reorganised message methods, added getNick() method to client.roster
- plugin text commands: fixed plugin and adapted to new messages behaviour. client is now used in arguments instead of profile
- plugins: added information for cancellation reason in CancelError calls
- plugin XEP-0045: various improvments, but this plugin still need work:
- trigger is used to avoid message already handled by the plugin to be handled a second time
- changed the way to handle history, the last message from DB is checked and we request only messages since this one, in seconds (thanks Poezio folks :))
- subject reception is waited before sending the roomJoined signal, this way we are sure that everything including history is ready
- cmd_* method now follow the new convention with client instead of profile
- roomUserJoined and roomUserLeft messages are removed, the events are now handled with info message with a "ROOM_USER_JOINED" info subtype
- probably other forgotten stuffs :p
This commit is contained in:
parent
feadc8c89e
commit
a7de473107
1 changed files with 987 additions and 598 deletions
|
|
@ -20,11 +20,17 @@
|
|||
from sat.core.i18n import _
|
||||
from sat.core.log import getLogger
|
||||
log = getLogger(__name__)
|
||||
from sat_frontends.tools import jid
|
||||
from sat.core import exceptions
|
||||
from sat_frontends.quick_frontend import quick_widgets
|
||||
from sat_frontends.quick_frontend.constants import Const as C
|
||||
from collections import OrderedDict
|
||||
from datetime import datetime
|
||||
from sat_frontends.tools import jid
|
||||
|
||||
ROOM_USER_JOINED = 'ROOM_USER_JOINED'
|
||||
ROOM_USER_LEFT = 'ROOM_USER_LEFT'
|
||||
ROOM_USER_MOVED = (ROOM_USER_JOINED, ROOM_USER_LEFT)
|
||||
|
||||
# from datetime import datetime
|
||||
|
||||
try:
|
||||
# FIXME: to be removed when an acceptable solution is here
|
||||
|
|
@ -32,26 +38,109 @@ try:
|
|||
except (TypeError, AttributeError): # Error raised is not the same depending on pyjsbuild options
|
||||
unicode = str
|
||||
|
||||
# FIXME: day_format need to be settable (i18n)
|
||||
|
||||
class Message(object):
|
||||
"""Message metadata"""
|
||||
|
||||
def __init__(self, parent, uid, timestamp, from_jid, to_jid, msg, subject, type_, extra, profile):
|
||||
self.parent = parent
|
||||
self.profile = profile
|
||||
self.uid = uid
|
||||
self.timestamp = timestamp
|
||||
self.from_jid = from_jid
|
||||
self.to_jid = to_jid
|
||||
self.message = msg
|
||||
self.subject = subject
|
||||
self.type = type_
|
||||
self.extra = extra
|
||||
self.nick = self.getNick(from_jid)
|
||||
# own_mess is True if message was sent by profile's jid
|
||||
self.own_mess = (from_jid.resource == self.parent.nick) if self.parent.type == C.CHAT_GROUP else (from_jid.bare == self.host.profiles[profile].whoami.bare)
|
||||
self.widgets = set() # widgets linked to this message
|
||||
|
||||
@property
|
||||
def host(self):
|
||||
return self.parent.host
|
||||
|
||||
@property
|
||||
def info_type(self):
|
||||
return self.extra.get('info_type')
|
||||
|
||||
def getNick(self, entity):
|
||||
"""Return nick of an entity when possible"""
|
||||
contact_list = self.host.contact_lists[self.profile]
|
||||
if self.type == C.MESS_TYPE_INFO and self.info_type in ROOM_USER_MOVED:
|
||||
try:
|
||||
return self.extra['user_nick']
|
||||
except KeyError:
|
||||
log.error(u"extra data is missing user nick for uid {}".format(self.uid))
|
||||
return ""
|
||||
if self.parent.type == C.CHAT_GROUP or entity in contact_list.getSpecialExtras(C.CONTACT_SPECIAL_GROUP):
|
||||
return entity.resource or ""
|
||||
if entity.bare in contact_list:
|
||||
return contact_list.getCache(entity, 'nick') or contact_list.getCache(entity, 'name') or entity.node or entity
|
||||
return entity.node or entity
|
||||
|
||||
|
||||
class Occupant(object):
|
||||
"""Occupant metadata"""
|
||||
|
||||
def __init__(self, parent, data, profile):
|
||||
self.parent = parent
|
||||
self.profile = profile
|
||||
self.nick = data['nick']
|
||||
self.entity = data.get('entity')
|
||||
if not self.entity:
|
||||
self.entity = jid.JID(u"{}/{}".format(parent.target.bare, self.nick)),
|
||||
self.affiliation = data['affiliation']
|
||||
self.role = data['role']
|
||||
self.widgets = set() # widgets linked to this occupant
|
||||
|
||||
@property
|
||||
def host(self):
|
||||
return self.parent.host
|
||||
|
||||
|
||||
class QuickChat(quick_widgets.QuickWidget):
|
||||
|
||||
visible_states = ['chat_state']
|
||||
|
||||
def __init__(self, host, target, type_=C.CHAT_ONE2ONE, profiles=None):
|
||||
def __init__(self, host, target, type_=C.CHAT_ONE2ONE, occupants=None, subject=None, profiles=None):
|
||||
"""
|
||||
@param type_: can be C.CHAT_ONE2ONE for single conversation or C.CHAT_GROUP for chat à la IRC
|
||||
"""
|
||||
self.lang = '' # default language to use for messages
|
||||
quick_widgets.QuickWidget.__init__(self, host, target, profiles=profiles)
|
||||
self._locked = False # True when we are waiting for history/search
|
||||
# messageNew signals are cached when locked
|
||||
self._cache = []
|
||||
assert type_ in (C.CHAT_ONE2ONE, C.CHAT_GROUP)
|
||||
if type_ == C.CHAT_GROUP and target.resource:
|
||||
raise ValueError("A group chat entity can't have a resource")
|
||||
self.current_target = target
|
||||
self.type = type_
|
||||
self.id = "" # FIXME: to be removed
|
||||
self.nick = None
|
||||
if type_ == C.CHAT_GROUP:
|
||||
if target.resource:
|
||||
raise exceptions.InternalError(u"a group chat entity can't have a resource")
|
||||
self.nick = None
|
||||
self.occupants = {}
|
||||
self.setOccupants(occupants)
|
||||
else:
|
||||
if occupants is not None:
|
||||
raise exceptions.InternalError(u"only group chat can have occupants")
|
||||
self.messages = OrderedDict() # key: uid, value: Message instance
|
||||
self.games = {} # key=game name (unicode), value=instance of quick_games.RoomGame
|
||||
self.subject = subject
|
||||
|
||||
def postInit(self):
|
||||
"""Method to be called by frontend after widget is initialised
|
||||
|
||||
handle the display of history and subject
|
||||
"""
|
||||
self.historyPrint(profile=self.profile)
|
||||
if self.subject is not None:
|
||||
self.setSubject(self.subject)
|
||||
|
||||
## Widget management ##
|
||||
|
||||
def __str__(self):
|
||||
return u"Chat Widget [target: {}, type: {}, profile: {}]".format(self.target, self.type, self.profile)
|
||||
|
|
@ -74,109 +163,6 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
if target.resource:
|
||||
self.current_target = target # FIXME: tmp, must use resource priority throught contactList instead
|
||||
|
||||
@property
|
||||
def target(self):
|
||||
if self.type == C.CHAT_GROUP:
|
||||
return self.current_target.bare
|
||||
return self.current_target
|
||||
|
||||
@property
|
||||
def occupants(self):
|
||||
"""Return the occupants of a group chat (nicknames).
|
||||
|
||||
@return: set(unicode)
|
||||
"""
|
||||
if self.type != C.CHAT_GROUP:
|
||||
return set()
|
||||
contact_list = self.host.contact_lists[self.profile]
|
||||
return contact_list.getCache(self.target, C.CONTACT_RESOURCES).keys()
|
||||
|
||||
def manageMessage(self, entity, mess_type):
|
||||
"""Tell if this chat widget manage an entity and message type couple
|
||||
|
||||
@param entity (jid.JID): (full) jid of the sending entity
|
||||
@param mess_type (str): message type as given by messageNew
|
||||
@return (bool): True if this Chat Widget manage this couple
|
||||
"""
|
||||
if self.type == C.CHAT_GROUP:
|
||||
if mess_type == C.MESS_TYPE_GROUPCHAT and self.target == entity.bare:
|
||||
return True
|
||||
else:
|
||||
if mess_type != C.MESS_TYPE_GROUPCHAT and entity in self.targets:
|
||||
return True
|
||||
return False
|
||||
|
||||
def addUser(self, nick):
|
||||
"""Add user if it is not in the group list"""
|
||||
self.printInfo("=> %s has joined the room" % nick)
|
||||
|
||||
def removeUser(self, nick):
|
||||
"""Remove a user from the group list"""
|
||||
self.printInfo("<= %s has left the room" % nick)
|
||||
|
||||
def setUserNick(self, nick):
|
||||
"""Set the nick of the user, usefull for e.g. change the color of the user"""
|
||||
self.nick = nick
|
||||
|
||||
def changeUserNick(self, old_nick, new_nick):
|
||||
"""Change nick of a user in group list"""
|
||||
self.printInfo("%s is now known as %s" % (old_nick, new_nick))
|
||||
|
||||
def setSubject(self, subject):
|
||||
"""Set title for a group chat"""
|
||||
log.debug(_("Setting subject to %s") % subject)
|
||||
if self.type != C.CHAT_GROUP:
|
||||
log.error (_("[INTERNAL] trying to set subject for a non group chat window"))
|
||||
raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here
|
||||
|
||||
def afterHistoryPrint(self):
|
||||
"""Refresh or scroll down the focus after the history is printed"""
|
||||
pass
|
||||
|
||||
def historyPrint(self, size=C.HISTORY_LIMIT_DEFAULT, search='', profile='@NONE@'):
|
||||
"""Print the current history
|
||||
|
||||
@param size (int): number of messages
|
||||
@param search (str): pattern to filter the history results
|
||||
@param profile (str): %(doc_profile)s
|
||||
"""
|
||||
log_msg = _(u"now we print the history")
|
||||
if size != C.HISTORY_LIMIT_DEFAULT:
|
||||
log_msg += _(u" (%d messages)" % size)
|
||||
log.debug(log_msg)
|
||||
|
||||
target = self.target.bare
|
||||
|
||||
def _historyGetCb(history):
|
||||
day_format = "%A, %d %b %Y" # to display the day change
|
||||
previous_day = datetime.now().strftime(day_format)
|
||||
for data in history:
|
||||
uid, timestamp, from_jid, to_jid, message, subject, type_, extra = data # FIXME: extra is unused !
|
||||
if ((self.type == C.CHAT_GROUP and type_ != C.MESS_TYPE_GROUPCHAT) or
|
||||
(self.type == C.CHAT_ONE2ONE and type_ == C.MESS_TYPE_GROUPCHAT)):
|
||||
continue
|
||||
message_day = datetime.fromtimestamp(timestamp).strftime(day_format)
|
||||
if previous_day != message_day:
|
||||
self.printDayChange(message_day)
|
||||
previous_day = message_day
|
||||
extra["timestamp"] = timestamp
|
||||
self.messageNew(uid, timestamp, jid.JID(from_jid), target, message, subject, type_, extra, profile)
|
||||
self.afterHistoryPrint()
|
||||
|
||||
def _historyGetEb(err):
|
||||
log.error(_("Can't get history"))
|
||||
|
||||
self.host.bridge.historyGet(unicode(self.host.profiles[profile].whoami.bare), unicode(target), size, True, search, profile, callback=_historyGetCb, errback=_historyGetEb)
|
||||
|
||||
def _get_nick(self, entity):
|
||||
"""Return nick of this entity when possible"""
|
||||
contact_list = self.host.contact_lists[self.profile]
|
||||
if self.type == C.CHAT_GROUP or entity in contact_list.getSpecialExtras(C.CONTACT_SPECIAL_GROUP):
|
||||
return entity.resource or ""
|
||||
if entity.bare in contact_list:
|
||||
return contact_list.getCache(entity, 'nick') or contact_list.getCache(entity, 'name') or entity.node or entity
|
||||
return entity.node or entity
|
||||
|
||||
def onPrivateCreated(self, widget):
|
||||
"""Method called when a new widget for private conversation (MUC) is created"""
|
||||
raise NotImplementedError
|
||||
|
|
@ -188,57 +174,171 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
"""
|
||||
return self.host.widgets.getOrCreateWidget(QuickChat, entity, type_=C.CHAT_ONE2ONE, force_hash=self.getPrivateHash(self.profile, entity), on_new_widget=self.onPrivateCreated, profile=self.profile) # we force hash to have a new widget, not this one again
|
||||
|
||||
def messageNew(self, uid, timestamp, from_jid, target, msg, subject, type_, extra, profile):
|
||||
@property
|
||||
def target(self):
|
||||
if self.type == C.CHAT_GROUP:
|
||||
return self.current_target.bare
|
||||
return self.current_target
|
||||
|
||||
## occupants ##
|
||||
|
||||
def setOccupants(self, occupants):
|
||||
"""set the whole list of occupants"""
|
||||
assert len(self.occupants) == 0
|
||||
for nick, data in occupants.iteritems():
|
||||
self.occupants[nick] = Occupant(
|
||||
self,
|
||||
data,
|
||||
self.profile
|
||||
)
|
||||
|
||||
def addUser(self, occupant_data):
|
||||
"""Add user if it is not in the group list"""
|
||||
occupant = Occupant(
|
||||
self,
|
||||
occupant_data,
|
||||
self.profile
|
||||
)
|
||||
self.occupants[occupant.nick] = occupant
|
||||
return occupant
|
||||
|
||||
def removeUser(self, occupant_data):
|
||||
"""Remove a user from the group list"""
|
||||
nick = occupant_data['nick']
|
||||
try:
|
||||
msg = msg.itervalues().next() # FIXME: tmp fix until message refactoring is finished (msg is now a dict)
|
||||
except StopIteration:
|
||||
log.warning(u"No message found (uid: {})".format(uid))
|
||||
msg = ''
|
||||
if self.type == C.CHAT_GROUP and target.resource and type_ != C.MESS_TYPE_GROUPCHAT:
|
||||
# we have a private message, we forward it to a private conversation widget
|
||||
chat_widget = self.getOrCreatePrivateWidget(target)
|
||||
chat_widget.messageNew(uid, timestamp, from_jid, target, msg, subject, type_, extra, profile)
|
||||
return
|
||||
|
||||
if type_ == C.MESS_TYPE_INFO:
|
||||
self.printInfo(msg, extra=extra)
|
||||
occupant = self.occupants.pop(nick)
|
||||
except KeyError:
|
||||
log.warning(u"Trying to remove an unknown occupant: {}".format(nick))
|
||||
else:
|
||||
nick = self._get_nick(from_jid)
|
||||
if msg.startswith('/me '):
|
||||
self.printInfo('* {} {}'.format(nick, msg[4:]), type_='me', extra=extra)
|
||||
else:
|
||||
# my_message is True if message comes from local user
|
||||
my_message = (from_jid.resource == self.nick) if self.type == C.CHAT_GROUP else (from_jid.bare == self.host.profiles[profile].whoami.bare)
|
||||
self.printMessage(nick, my_message, msg, timestamp, extra, profile)
|
||||
# FIXME: to be checked/removed after message refactoring
|
||||
# if timestamp:
|
||||
self.afterHistoryPrint()
|
||||
return occupant
|
||||
|
||||
def printMessage(self, nick, my_message, message, timestamp, extra=None, profile=C.PROF_KEY_NONE):
|
||||
"""Print message in chat window.
|
||||
def setUserNick(self, nick):
|
||||
"""Set the nick of the user, usefull for e.g. change the color of the user"""
|
||||
self.nick = nick
|
||||
|
||||
@param nick (unicode): author nick
|
||||
@param my_message (boolean): True if profile is the author
|
||||
@param message (unicode): message content
|
||||
@param extra (dict): extra data
|
||||
def changeUserNick(self, old_nick, new_nick):
|
||||
"""Change nick of a user in group list"""
|
||||
self.printInfo("%s is now known as %s" % (old_nick, new_nick))
|
||||
|
||||
## Messages ##
|
||||
|
||||
def manageMessage(self, entity, mess_type):
|
||||
"""Tell if this chat widget manage an entity and message type couple
|
||||
|
||||
@param entity (jid.JID): (full) jid of the sending entity
|
||||
@param mess_type (str): message type as given by messageNew
|
||||
@return (bool): True if this Chat Widget manage this couple
|
||||
"""
|
||||
# FIXME: check/remove this if necessary (message refactoring)
|
||||
# if not timestamp:
|
||||
# # XXX: do not send notifications for each line of the history being displayed
|
||||
# # FIXME: this must be changed in the future if the timestamp is passed with
|
||||
# # all messages and not only with the messages coming from the history.
|
||||
self.notify(nick, message)
|
||||
if self.type == C.CHAT_GROUP:
|
||||
if mess_type in (C.MESS_TYPE_GROUPCHAT, C.MESS_TYPE_INFO) and self.target == entity.bare:
|
||||
return True
|
||||
else:
|
||||
if mess_type != C.MESS_TYPE_GROUPCHAT and entity in self.targets:
|
||||
return True
|
||||
return False
|
||||
|
||||
def printInfo(self, msg, type_='normal', extra=None):
|
||||
"""Print general info.
|
||||
def updateHistory(self, size=C.HISTORY_LIMIT_DEFAULT, search='', profile='@NONE@'):
|
||||
"""Called when history need to be recreated
|
||||
|
||||
@param msg (unicode): message to print
|
||||
@param type_ (unicode):
|
||||
- 'normal': general info like "toto has joined the room"
|
||||
- 'me': "/me" information like "/me clenches his fist" ==> "toto clenches his fist"
|
||||
@param extra (dict): message data
|
||||
Remove all message from history then call historyPrint
|
||||
Must probably be overriden by frontend to clear widget
|
||||
@param size (int): number of messages
|
||||
@param search (str): pattern to filter the history results
|
||||
@param profile (str): %(doc_profile)s
|
||||
"""
|
||||
self.notify(msg=msg)
|
||||
self._locked = True
|
||||
self.messages.clear()
|
||||
self.historyPrint(size, search, profile)
|
||||
|
||||
def _onHistoryPrinted(self):
|
||||
"""Method called when history is printed (or failed)
|
||||
|
||||
unlock the widget, and can be used to refresh or scroll down
|
||||
the focus after the history is printed
|
||||
"""
|
||||
self._locked = False
|
||||
for data in self._cache:
|
||||
self.messageNew(*data)
|
||||
|
||||
def historyPrint(self, size=C.HISTORY_LIMIT_DEFAULT, search='', profile='@NONE@'):
|
||||
"""Print the current history
|
||||
|
||||
@param size (int): number of messages
|
||||
@param search (str): pattern to filter the history results
|
||||
@param profile (str): %(doc_profile)s
|
||||
"""
|
||||
if size == 0:
|
||||
log.debug(u"Empty history requested, skipping")
|
||||
self._onHistoryPrinted()
|
||||
return
|
||||
log_msg = _(u"now we print the history")
|
||||
if size != C.HISTORY_LIMIT_DEFAULT:
|
||||
log_msg += _(u" ({} messages)".format(size))
|
||||
log.debug(log_msg)
|
||||
|
||||
target = self.target.bare
|
||||
|
||||
def _historyGetCb(history):
|
||||
# day_format = "%A, %d %b %Y" # to display the day change
|
||||
# previous_day = datetime.now().strftime(day_format)
|
||||
# message_day = datetime.fromtimestamp(timestamp).strftime(self.day_format)
|
||||
# if previous_day != message_day:
|
||||
# self.printDayChange(message_day)
|
||||
# previous_day = message_day
|
||||
for data in history:
|
||||
uid, timestamp, from_jid, to_jid, message, subject, type_, extra = data
|
||||
from_jid = jid.JID(from_jid)
|
||||
to_jid = jid.JID(to_jid)
|
||||
# if ((self.type == C.CHAT_GROUP and type_ != C.MESS_TYPE_GROUPCHAT) or
|
||||
# (self.type == C.CHAT_ONE2ONE and type_ == C.MESS_TYPE_GROUPCHAT)):
|
||||
# continue
|
||||
self.messages[uid] = Message(self, uid, timestamp, from_jid, to_jid, message, subject, type_, extra, profile)
|
||||
self._onHistoryPrinted()
|
||||
|
||||
def _historyGetEb(err):
|
||||
log.error(_(u"Can't get history"))
|
||||
self._onHistoryPrinted()
|
||||
|
||||
self.host.bridge.historyGet(unicode(self.host.profiles[profile].whoami.bare), unicode(target), size, True, search, profile, callback=_historyGetCb, errback=_historyGetEb)
|
||||
|
||||
def messageNew(self, uid, timestamp, from_jid, to_jid, msg, subject, type_, extra, profile):
|
||||
log.debug(u"messageNew ==> {}".format((uid, timestamp, from_jid, to_jid, msg, subject, type_, extra, profile)))
|
||||
if self._locked:
|
||||
self._cache.append(uid, timestamp, from_jid, to_jid, msg, subject, type_, extra, profile)
|
||||
return
|
||||
if self.type == C.CHAT_GROUP:
|
||||
if to_jid.resource and type_ != C.MESS_TYPE_GROUPCHAT:
|
||||
# we have a private message, we forward it to a private conversation widget
|
||||
chat_widget = self.getOrCreatePrivateWidget(to_jid)
|
||||
chat_widget.messageNew(uid, timestamp, from_jid, to_jid, msg, subject, type_, extra, profile)
|
||||
return
|
||||
if type_ == C.MESS_TYPE_INFO:
|
||||
try:
|
||||
info_type = extra['info_type']
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
user_data = {k[5:]:v for k,v in extra.iteritems() if k.startswith('user_')}
|
||||
if info_type == ROOM_USER_JOINED:
|
||||
self.addUser(user_data)
|
||||
elif info_type == ROOM_USER_LEFT:
|
||||
self.removeUser(user_data)
|
||||
|
||||
message = Message(self, uid, timestamp, from_jid, to_jid, msg, subject, type_, extra, profile)
|
||||
self.messages[uid] = message
|
||||
|
||||
if 'received_timestamp' in extra:
|
||||
log.warning(u"Delayed message received after history, this should not happen")
|
||||
self.createMessage(message)
|
||||
|
||||
def createMessage(self, message, append=False):
|
||||
"""Must be implemented by frontend to create and show a new message widget
|
||||
|
||||
This is only called on messageNew, not on history.
|
||||
You need to override historyPrint to handle the later
|
||||
@param message(Message): message data
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def notify(self, contact="somebody", msg=""):
|
||||
"""Notify the user of a new message if the frontend doesn't have the focus.
|
||||
|
|
@ -246,6 +346,7 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
@param contact (unicode): contact who wrote to the users
|
||||
@param msg (unicode): the message that has been received
|
||||
"""
|
||||
# FIXME: not called anymore after refactoring
|
||||
raise NotImplemented
|
||||
|
||||
def printDayChange(self, day):
|
||||
|
|
@ -253,21 +354,16 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
|
||||
@param day(unicode): day to display (or not if this method is not overwritten)
|
||||
"""
|
||||
# FIXME: not called anymore after refactoring
|
||||
pass
|
||||
|
||||
def getEntityStates(self, entity):
|
||||
"""Retrieve states for an entity.
|
||||
## Room ##
|
||||
|
||||
@param entity (jid.JID): entity
|
||||
@return: OrderedDict{unicode: unicode}
|
||||
"""
|
||||
states = OrderedDict()
|
||||
clist = self.host.contact_lists[self.profile]
|
||||
for key in self.visible_states:
|
||||
value = clist.getCache(entity, key)
|
||||
if value:
|
||||
states[key] = value
|
||||
return states
|
||||
def setSubject(self, subject):
|
||||
"""Set title for a group chat"""
|
||||
self.subject = subject
|
||||
if self.type != C.CHAT_GROUP:
|
||||
raise exceptions.InternalError("trying to set subject for a non group chat window")
|
||||
|
||||
def addGamePanel(self, widget):
|
||||
"""Insert a game panel to this Chat dialog.
|
||||
|
|
|
|||
Loading…
Reference in a new issue