core, frontends: refactoring to base Libervia on QuickFrontend (big mixed commit):

/!\ not finished, everything is still instable !
    - bridge: DBus bridge has been modified to allow blocking call to be called in the same way as asynchronous calls
    - bridge: calls with a callback and no errback are now possible, default errback log the error
    - constants: removed hack to manage presence without OrderedDict, as an OrderedDict like class has been implemented in Libervia
    - core: getLastResource has been removed and replaced by getMainResource (there is a global better management of resources)
    - various style improvments: use of constants when possible, fixed variable overlaps, import of module instead of direct class import
    - frontends: printInfo and printMessage methods in (Quick)Chat are more generic (use of extra instead of timestamp)
    - frontends: bridge creation and option parsing (command line arguments) are now specified by the frontend in QuickApp __init__
    - frontends: ProfileManager manage a more complete plug sequence (some stuff formerly manage in contact_list have moved to ProfileManager)
    - quick_frontend (quick_widgets): QuickWidgetsManager is now iterable (all widgets are then returned), or can return an iterator on a specific class (return all widgets of this class) with getWidgets
    - frontends: tools.jid can now be used in Pyjamas, with some care
    - frontends (XMLUI): profile is now managed
    - core (memory): big improvment on entities cache management (and specially resource management)
    - core (params/exceptions): added PermissionError
    - various fixes and improvments, check diff for more details
This commit is contained in:
Goffi 2015-01-24 01:00:29 +01:00
commit 8ce5c1862b
29 changed files with 1125 additions and 452 deletions

View file

@ -102,10 +102,10 @@ class DBusBridgeFrontend(BridgeFrontend):
async = False
if kwargs:
if 'callback' in kwargs and 'errback' in kwargs:
if 'callback' in kwargs:
async = True
_callback = kwargs.pop('callback')
_errback = kwargs.pop('errback')
_errback = kwargs.pop('errback', lambda failure: log.error(unicode(failure)))
elif len(args) >= 2 and callable(args[-1]) and callable(args[-2]):
async = True
args = list(args)
@ -122,141 +122,497 @@ class DBusBridgeFrontend(BridgeFrontend):
return method(*args, **kwargs)
return getPluginMethod
def addContact(self, entity_jid, profile_key="@DEFAULT@"):
return self.db_core_iface.addContact(entity_jid, profile_key)
def addContact(self, entity_jid, profile_key="@DEFAULT@", callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return self.db_core_iface.addContact(entity_jid, profile_key, **kwargs)
def asyncConnect(self, profile_key="@DEFAULT@", password='', callback=None, errback=None):
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
return self.db_core_iface.asyncConnect(profile_key, password, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
def asyncCreateProfile(self, profile, password='', callback=None, errback=None):
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
return self.db_core_iface.asyncCreateProfile(profile, password, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
def asyncDeleteProfile(self, profile, callback=None, errback=None):
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
return self.db_core_iface.asyncDeleteProfile(profile, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
def asyncGetParamA(self, name, category, attribute="value", security_limit=-1, profile_key="@DEFAULT@", callback=None, errback=None):
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
return unicode(self.db_core_iface.asyncGetParamA(name, category, attribute, security_limit, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler))
def confirmationAnswer(self, id, accepted, data, profile):
return self.db_core_iface.confirmationAnswer(id, accepted, data, profile)
def confirmationAnswer(self, id, accepted, data, profile, callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return self.db_core_iface.confirmationAnswer(id, accepted, data, profile, **kwargs)
def delContact(self, entity_jid, profile_key="@DEFAULT@", callback=None, errback=None):
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
return self.db_core_iface.delContact(entity_jid, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
def discoInfos(self, entity_jid, profile_key, callback=None, errback=None):
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
return self.db_core_iface.discoInfos(entity_jid, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
def discoItems(self, entity_jid, profile_key, callback=None, errback=None):
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
return self.db_core_iface.discoItems(entity_jid, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
def disconnect(self, profile_key="@DEFAULT@"):
return self.db_core_iface.disconnect(profile_key)
def disconnect(self, profile_key="@DEFAULT@", callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return self.db_core_iface.disconnect(profile_key, **kwargs)
def getConfig(self, section, name):
return unicode(self.db_core_iface.getConfig(section, name))
def getConfig(self, section, name, callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return unicode(self.db_core_iface.getConfig(section, name, **kwargs))
def getContacts(self, profile_key="@DEFAULT@", callback=None, errback=None):
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
return self.db_core_iface.getContacts(profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
def getContactsFromGroup(self, group, profile_key="@DEFAULT@"):
return self.db_core_iface.getContactsFromGroup(group, profile_key)
def getContactsFromGroup(self, group, profile_key="@DEFAULT@", callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return self.db_core_iface.getContactsFromGroup(group, profile_key, **kwargs)
def getEntityData(self, jid, keys, profile):
return self.db_core_iface.getEntityData(jid, keys, profile)
def getEntityData(self, jid, keys, profile, callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return self.db_core_iface.getEntityData(jid, keys, profile, **kwargs)
def getHistory(self, from_jid, to_jid, limit, between=True, search='', profile="@NONE@", callback=None, errback=None):
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
return self.db_core_iface.getHistory(from_jid, to_jid, limit, between, search, profile, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
def getLastResource(self, contact_jid, profile_key="@DEFAULT@"):
return unicode(self.db_core_iface.getLastResource(contact_jid, profile_key))
def getMainResource(self, contact_jid, profile_key="@DEFAULT@", callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return unicode(self.db_core_iface.getMainResource(contact_jid, profile_key, **kwargs))
def getMenuHelp(self, menu_id, language):
return unicode(self.db_core_iface.getMenuHelp(menu_id, language))
def getMenuHelp(self, menu_id, language, callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return unicode(self.db_core_iface.getMenuHelp(menu_id, language, **kwargs))
def getMenus(self, language, security_limit):
return self.db_core_iface.getMenus(language, security_limit)
def getMenus(self, language, security_limit, callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return self.db_core_iface.getMenus(language, security_limit, **kwargs)
def getParamA(self, name, category, attribute="value", profile_key="@DEFAULT@"):
return unicode(self.db_core_iface.getParamA(name, category, attribute, profile_key))
def getParamA(self, name, category, attribute="value", profile_key="@DEFAULT@", callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return unicode(self.db_core_iface.getParamA(name, category, attribute, profile_key, **kwargs))
def getParams(self, security_limit=-1, app='', profile_key="@DEFAULT@", callback=None, errback=None):
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
return unicode(self.db_core_iface.getParams(security_limit, app, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler))
def getParamsCategories(self, ):
return self.db_core_iface.getParamsCategories()
def getParamsCategories(self, callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return self.db_core_iface.getParamsCategories(**kwargs)
def getParamsForCategory(self, category, security_limit=-1, app='', profile_key="@DEFAULT@", callback=None, errback=None):
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
return unicode(self.db_core_iface.getParamsForCategory(category, security_limit, app, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler))
def getParamsUI(self, security_limit=-1, app='', profile_key="@DEFAULT@", callback=None, errback=None):
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
return unicode(self.db_core_iface.getParamsUI(security_limit, app, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler))
def getPresenceStatuses(self, profile_key="@DEFAULT@"):
return self.db_core_iface.getPresenceStatuses(profile_key)
def getPresenceStatuses(self, profile_key="@DEFAULT@", callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return self.db_core_iface.getPresenceStatuses(profile_key, **kwargs)
def getProfileName(self, profile_key="@DEFAULT@"):
return unicode(self.db_core_iface.getProfileName(profile_key))
def getProfileName(self, profile_key="@DEFAULT@", callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return unicode(self.db_core_iface.getProfileName(profile_key, **kwargs))
def getProfilesList(self, ):
return self.db_core_iface.getProfilesList()
def getProfilesList(self, callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return self.db_core_iface.getProfilesList(**kwargs)
def getProgress(self, id, profile):
return self.db_core_iface.getProgress(id, profile)
def getProgress(self, id, profile, callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return self.db_core_iface.getProgress(id, profile, **kwargs)
def getReady(self, callback=None, errback=None):
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
return self.db_core_iface.getReady(timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
def getVersion(self, ):
return unicode(self.db_core_iface.getVersion())
def getVersion(self, callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return unicode(self.db_core_iface.getVersion(**kwargs))
def getWaitingConf(self, profile_key):
return self.db_core_iface.getWaitingConf(profile_key)
def getWaitingConf(self, profile_key, callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return self.db_core_iface.getWaitingConf(profile_key, **kwargs)
def getWaitingSub(self, profile_key="@DEFAULT@"):
return self.db_core_iface.getWaitingSub(profile_key)
def getWaitingSub(self, profile_key="@DEFAULT@", callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return self.db_core_iface.getWaitingSub(profile_key, **kwargs)
def isConnected(self, profile_key="@DEFAULT@"):
return self.db_core_iface.isConnected(profile_key)
def isConnected(self, profile_key="@DEFAULT@", callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return self.db_core_iface.isConnected(profile_key, **kwargs)
def launchAction(self, callback_id, data, profile_key="@DEFAULT@", callback=None, errback=None):
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
return self.db_core_iface.launchAction(callback_id, data, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
def loadParamsTemplate(self, filename):
return self.db_core_iface.loadParamsTemplate(filename)
def loadParamsTemplate(self, filename, callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return self.db_core_iface.loadParamsTemplate(filename, **kwargs)
def paramsRegisterApp(self, xml, security_limit=-1, app=''):
return self.db_core_iface.paramsRegisterApp(xml, security_limit, app)
def paramsRegisterApp(self, xml, security_limit=-1, app='', callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return self.db_core_iface.paramsRegisterApp(xml, security_limit, app, **kwargs)
def saveParamsTemplate(self, filename):
return self.db_core_iface.saveParamsTemplate(filename)
def saveParamsTemplate(self, filename, callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return self.db_core_iface.saveParamsTemplate(filename, **kwargs)
def sendMessage(self, to_jid, message, subject='', mess_type="auto", extra={}, profile_key="@NONE@", callback=None, errback=None):
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
return self.db_core_iface.sendMessage(to_jid, message, subject, mess_type, extra, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
def setParam(self, name, value, category, security_limit=-1, profile_key="@DEFAULT@"):
return self.db_core_iface.setParam(name, value, category, security_limit, profile_key)
def setParam(self, name, value, category, security_limit=-1, profile_key="@DEFAULT@", callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return self.db_core_iface.setParam(name, value, category, security_limit, profile_key, **kwargs)
def setPresence(self, to_jid='', show='', statuses={}, profile_key="@DEFAULT@"):
return self.db_core_iface.setPresence(to_jid, show, statuses, profile_key)
def setPresence(self, to_jid='', show='', statuses={}, profile_key="@DEFAULT@", callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return self.db_core_iface.setPresence(to_jid, show, statuses, profile_key, **kwargs)
def subscription(self, sub_type, entity, profile_key="@DEFAULT@"):
return self.db_core_iface.subscription(sub_type, entity, profile_key)
def subscription(self, sub_type, entity, profile_key="@DEFAULT@", callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return self.db_core_iface.subscription(sub_type, entity, profile_key, **kwargs)
def updateContact(self, entity_jid, name, groups, profile_key="@DEFAULT@", callback=None, errback=None):
error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
return self.db_core_iface.updateContact(entity_jid, name, groups, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)

View file

@ -20,14 +20,7 @@
from sat.core import constants
from sat.core.i18n import _, D_
try:
from collections import OrderedDict # only available from python 2.7
except ImportError:
try:
from ordereddict import OrderedDict
except ImportError:
pass # libervia can not import external libraries
from collections import OrderedDict # only available from python 2.7
def getPresence():
@ -35,25 +28,15 @@ def getPresence():
in a method we get a JS runtime SyntaxError: "missing ) in parenthetical".
# TODO: merge this definition with those in primitivus.constants and wix.constants
"""
try:
presence = OrderedDict([("", _("Online")),
("chat", _("Free for chat")),
("away", _("Away from keyboard")),
("dnd", _("Do not disturb")),
("xa", _("Extended away"))])
except TypeError:
presence = {"": _("Online"),
"chat": _("Free for chat"),
"away": _("Away from keyboard"),
"dnd": _("Do not disturb"),
"xa": _("Extended away")
}
return presence
class Const(constants.Const):
PRESENCE = getPresence()
PRESENCE = OrderedDict([("", _("Online")),
("chat", _("Free for chat")),
("away", _("Away from keyboard")),
("dnd", _("Do not disturb")),
("xa", _("Extended away"))])
# from plugin_misc_text_syntaxes
SYNTAX_XHTML = "XHTML"
@ -78,3 +61,7 @@ class Const(constants.Const):
# Roster
GROUP_NOT_IN_ROSTER = D_('Not in roster')
#Chats
CHAT_ONE2ONE = 'one2one'
CHAT_GROUP = 'group'

View file

@ -245,11 +245,11 @@ class Jp(object):
callback()
def get_full_jid(self, param_jid):
"""Return the full jid if possible (add last resource when find a bare jid)"""
"""Return the full jid if possible (add main resource when find a bare jid)"""
_jid = JID(param_jid)
if not _jid.resource:
#if the resource is not given, we try to add the last known resource
last_resource = self.bridge.getLastResource(param_jid, self.profile)
last_resource = self.bridge.getMainResource(param_jid, self.profile)
if last_resource:
return "%s/%s" % (_jid.bare, last_resource)
return param_jid

View file

@ -30,7 +30,7 @@ from sat_frontends.primitivus.constants import Const as C
from sat_frontends.primitivus.keys import action_key_map as a_key
from sat_frontends.primitivus.widget import PrimitivusWidget
import time
from sat_frontends.tools.jid import JID
from sat_frontends.tools import jid
class ChatText(urwid.FlowWidget):
@ -83,7 +83,7 @@ class ChatText(urwid.FlowWidget):
class Chat(PrimitivusWidget, QuickChat):
def __init__(self, host, target, type_='one2one', profiles=None):
def __init__(self, host, target, type_=C.CHAT_ONE2ONE, profiles=None):
QuickChat.__init__(self, host, target, type_, profiles=profiles)
self.content = urwid.SimpleListWalker([])
self.text_list = urwid.ListBox(self.content)
@ -93,10 +93,10 @@ class Chat(PrimitivusWidget, QuickChat):
self.pile = urwid.Pile([self.chat_colums])
PrimitivusWidget.__init__(self, self.pile, self.target)
# we must adapt the behavious with the type
if type_ == 'one2one':
# we must adapt the behaviour with the type
if type_ == C.CHAT_ONE2ONE:
self.historyPrint(profile=self.profile)
elif type_ == 'group':
elif type_ == C.CHAT_GROUP:
if len(self.chat_colums.contents) == 1:
present_widget = self._buildPresentList()
self.present_panel = sat_widgets.VerticalSeparator(present_widget)
@ -110,7 +110,7 @@ class Chat(PrimitivusWidget, QuickChat):
def keypress(self, size, key):
if key == a_key['OCCUPANTS_HIDE']: #user wants to (un)hide the presents panel
if self.type == 'group':
if self.type == C.CHAT_GROUP:
widgets = [widget for (widget, options) in self.chat_colums.contents]
if self.present_panel in widgets:
self._removePresentPanel()
@ -140,11 +140,11 @@ class Chat(PrimitivusWidget, QuickChat):
def getMenu(self):
"""Return Menu bar"""
menu = sat_widgets.Menu(self.host.loop)
if self.type == 'group':
if self.type == C.CHAT_GROUP:
self.host.addMenus(menu, C.MENU_ROOM, {'room_jid': self.target.bare})
game = _("Game")
menu.addMenu(game, "Tarot", self.onTarotRequest)
elif self.type == 'one2one':
elif self.type == C.CHAT_ONE2ONE:
self.host.addMenus(menu, C.MENU_SINGLE, {'jid': self.target})
menu.addMenu(_("Action"), _("Send file"), self.onSendFileRequest)
return menu
@ -170,13 +170,13 @@ class Chat(PrimitivusWidget, QuickChat):
self.title_dynamic = '({})'.format(state)
def _presentClicked(self, list_wid, clicked_wid):
assert self.type == 'group'
assert self.type == C.CHAT_GROUP
nick = clicked_wid.getValue().value
if nick == self.getUserNick():
#We ignore clicks on our own nick
return
contact_list = self.host.contact_lists[self.profile]
full_jid = JID("%s/%s" % (self.target.bare, nick))
full_jid = jid.JID("%s/%s" % (self.target.bare, nick))
#we have a click on a nick, we need to create the widget if it doesn't exists
self.getOrCreatePrivateWidget(full_jid)
@ -258,10 +258,16 @@ class Chat(PrimitivusWidget, QuickChat):
def onPrivateCreated(self, widget):
self.host.contact_lists[widget.profile].specialResourceVisible(widget.target)
def printMessage(self, from_jid, msg, profile, timestamp=None):
assert isinstance(from_jid, JID)
def printMessage(self, from_jid, msg, extra=None, profile=C.PROF_KEY_NONE):
assert isinstance(from_jid, jid.JID)
if extra is None:
extra = {}
try:
jid, nick, mymess = QuickChat.printMessage(self, from_jid, msg, profile, timestamp)
timestamp = float(extra['timestamp'])
except KeyError:
timestamp=None
try:
nick, mymess = QuickChat.printMessage(self, from_jid, msg, extra, profile)
except TypeError:
# None is returned, the message is managed
return
@ -291,7 +297,7 @@ class Chat(PrimitivusWidget, QuickChat):
# all messages and not only with the messages coming from the history.
self._notify(from_jid, msg)
def printInfo(self, msg, type_='normal', timestamp=None):
def printInfo(self, msg, type_='normal', extra=None):
"""Print general info
@param msg: message to print
@type_: one of:
@ -299,6 +305,12 @@ class Chat(PrimitivusWidget, QuickChat):
me: "/me" information like "/me clenches his fist" ==> "toto clenches his fist"
@param timestamp (float): number of seconds since epoch
"""
if extra is None:
extra = {}
try:
timestamp = float(extra['timestamp'])
except KeyError:
timestamp=None
_widget = ChatText(self, timestamp, None, False, msg, is_info=True)
self.content.append(_widget)
self._notify(msg=msg)
@ -316,7 +328,7 @@ class Chat(PrimitivusWidget, QuickChat):
self.text_list.focus_position = len(self.content) - 1
self.host.redraw()
if not self.host.x_notify.hasFocus():
if self.type == "one2one":
if self.type == C.CHAT_ONE2ONE:
self.host.x_notify.sendNotification(_("Primitivus: %s is talking to you") % from_jid)
elif self.getUserNick().lower() in msg.lower():
self.host.x_notify.sendNotification(_("Primitivus: %(user)s mentioned you in room '%(room)s'") % {'user': from_jid, 'room': self.target})
@ -356,9 +368,9 @@ class Chat(PrimitivusWidget, QuickChat):
self.host.showDialog(_(u"File has a unicode error in its name, it's not yet managed by SàT"), title=_("Can't send file"), type_="error")
return
#FIXME: check last_resource: what if self.target.resource exists ?
last_resource = self.host.bridge.getLastResource(unicode(self.target.bare), self.profile)
last_resource = self.host.bridge.getMainResource(unicode(self.target.bare), self.profile)
if last_resource:
full_jid = JID("%s/%s" % (self.target.bare, last_resource))
full_jid = jid.JID("%s/%s" % (self.target.bare, last_resource))
else:
full_jid = self.target
progress_id = self.host.bridge.sendFile(full_jid, filepath, {}, self.profile)

View file

@ -148,6 +148,10 @@ class ContactList(PrimitivusWidget, QuickContactList):
self.update()
self._emit('click', entity)
def updatePresence(self, entity, show, priority, statuses):
super(ContactList, self).updatePresence(entity, show, priority, statuses)
self.update()
# Methods to build the widget
def _buildEntityWidget(self, entity, keys=None, use_bare_jid=False, with_alert=True, with_show_attr=True, markup_prepend=None, markup_append = None):

View file

@ -27,8 +27,9 @@ log = logging.getLogger(__name__)
import urwid
from urwid_satext import sat_widgets
from urwid_satext.files_management import FileDialog
from sat_frontends.bridge.DBus import DBusBridgeFrontend
from sat_frontends.quick_frontend.quick_app import QuickApp
from sat_frontends.quick_frontend.quick_utils import getNewPath
from sat_frontends.quick_frontend import quick_utils
from sat_frontends.quick_frontend import quick_chat
from sat_frontends.primitivus.profile_manager import ProfileManager
from sat_frontends.primitivus.contact_list import ContactList
@ -277,14 +278,14 @@ class PrimitivusTopWidget(sat_widgets.FocusPile):
class PrimitivusApp(QuickApp, InputHistory):
def __init__(self):
QuickApp.__init__(self)
QuickApp.__init__(self, create_bridge=DBusBridgeFrontend, check_options=quick_utils.check_options)
## main loop setup ##
self.main_widget = ProfileManager(self)
self.loop = urwid.MainLoop(self.main_widget, C.PALETTE, event_loop=urwid.GLibEventLoop(), input_filter=self.inputFilter, unhandled_input=self.keyHandler)
##misc setup##
self._visible_widgets = set()
self.notif_bar = sat_widgets.NotificationBar()
urwid.connect_signal(self.notif_bar, 'change', self.onNotification)
@ -297,6 +298,10 @@ class PrimitivusApp(QuickApp, InputHistory):
# we already manage exit with a_key['APP_QUIT'], so we don't want C-c
signal.signal(signal.SIGINT, signal.SIG_IGN)
@property
def visible_widgets(self):
return self._visible_widgets
@property
def mode(self):
return self.editBar.mode
@ -496,14 +501,14 @@ class PrimitivusApp(QuickApp, InputHistory):
self.redraw()
def newWidget(self, widget):
self.selectWidget(widget)
def selectWidget(self, widget):
"""Display a widget if possible,
else add it in the notification bar queue
@param widget: BoxWidget
"""
self.selectWidget(widget)
def selectWidget(self, widget):
assert len(self.center_part.widget_list)<=2
wid_idx = len(self.center_part.widget_list)-1
self.center_part.widget_list[wid_idx] = widget
@ -512,7 +517,7 @@ class PrimitivusApp(QuickApp, InputHistory):
except KeyError:
log.debug("No menu to delete")
self.selected_widget = widget
self.visible_widgets = set([widget]) # XXX: we can only have one widget visible at the time for now
self._visible_widgets = set([widget]) # XXX: we can only have one widget visible at the time for now
for contact_list in self.contact_lists.itervalues():
contact_list.unselectAll()
@ -642,7 +647,7 @@ class PrimitivusApp(QuickApp, InputHistory):
def dir_selected_cb(path):
dest_path = join(path, data['filename'])
answer_data["dest_path"] = getNewPath(dest_path)
answer_data["dest_path"] = quick_utils.getNewPath(dest_path)
self.addProgress(confirmation_id, dest_path)
accept_cb(None)

View file

@ -22,10 +22,6 @@ from sat_frontends import constants
class Const(constants.Const):
#Chats
CHAT_ONE2ONE = 'one2one'
CHAT_GROUP = 'group'
#Contact list
CONTACT_GROUPS = 'groups'
CONTACT_RESOURCES = 'resources'

View file

@ -22,11 +22,9 @@ import sys
from sat.core.log import getLogger
log = getLogger(__name__)
from sat.core import exceptions
from sat_frontends.bridge.DBus import DBusBridgeFrontend
from sat_frontends.tools import jid
from sat_frontends.quick_frontend.quick_widgets import QuickWidgetsManager
from sat_frontends.quick_frontend import quick_chat
from optparse import OptionParser
from sat_frontends.quick_frontend.constants import Const as C
@ -78,20 +76,54 @@ class ProfileManager(object):
contact_list.fill()
#The waiting subscription requests
waitingSub = self.bridge.getWaitingSub(self.profile)
for sub in waitingSub:
self.host.subscribeHandler(waitingSub[sub], sub, self.profile)
self.bridge.getWaitingSub(self.profile, callback=self._plug_profile_gotWaitingSub)
#Now we open the MUC window where we already are:
for room_args in self.bridge.getRoomsJoined(self.profile):
self.host.roomJoinedHandler(*room_args, profile=self.profile)
def _plug_profile_gotWaitingSub(self, waiting_sub):
for sub in waiting_sub:
self.host.subscribeHandler(waiting_sub[sub], sub, self.profile)
for subject_args in self.bridge.getRoomsSubjects(self.profile):
self.host.roomNewSubjectHandler(*subject_args, profile=self.profile)
self.bridge.getRoomsJoined(self.profile, callback=self._plug_profile_gotRoomsJoined)
#Finaly, we get the waiting confirmation requests
for confirm_id, confirm_type, data in self.bridge.getWaitingConf(self.profile):
self.host.askConfirmationHandler(confirm_id, confirm_type, data, self.profile)
def _plug_profile_gotRoomsJoined(self, rooms_args):
#Now we open the MUC window where we already are:
for room_args in rooms_args:
self.host.roomJoinedHandler(*room_args, profile=self.profile)
self.bridge.getRoomsSubjects(self.profile, callback=self._plug_profile_gotRoomsSubjects)
def _plug_profile_gotRoomsSubjects(self, subjects_args):
for subject_args in subjects_args:
self.host.roomNewSubjectHandler(*subject_args, profile=self.profile)
#Presence must be requested after rooms are filled
self.host.bridge.getPresenceStatuses(self.profile, callback=self._plug_profile_gotPresences)
def _plug_profile_gotPresences(self, presences):
def gotEntityData(data, contact):
for key in ('avatar', 'nick'):
if key in data:
self.host.entityDataUpdatedHandler(contact, key, data[key], self.profile)
for contact in presences:
for res in presences[contact]:
jabber_id = ('%s/%s' % (jid.JID(contact).bare, res)) if res else contact
show = presences[contact][res][0]
priority = presences[contact][res][1]
statuses = presences[contact][res][2]
self.host.presenceUpdateHandler(jabber_id, show, priority, statuses, self.profile)
self.host.bridge.getEntityData(contact, ['avatar', 'nick'], self.profile, callback=lambda data, contact=contact: gotEntityData(data, contact), errback=lambda failure, contact=contact: log.debug("No cache data for {}".format(contact)))
#Finaly, we get the waiting confirmation requests
self.bridge.getWaitingConf(self.profile, callback=self._plug_profile_gotWaitingConf)
def _plug_profile_gotWaitingConf(self, waiting_confs):
for confirm_id, confirm_type, data in waiting_confs:
self.host.askConfirmationHandler(confirm_id, confirm_type, data, self.profile)
# At this point, profile should be fully plugged
# and we launch frontend specific method
self.host.profilePlugged(self.profile)
def _getParamError(self, ignore):
log.error(_("Can't get profile parameter"))
@ -132,20 +164,27 @@ class ProfilesManager(object):
class QuickApp(object):
"""This class contain the main methods needed for the frontend"""
def __init__(self):
def __init__(self, create_bridge, check_options=None):
"""Create a frontend application
@param create_bridge: method to use to create the Bridge
@param check_options: method to call to check options (usually command line arguments)
"""
ProfileManager.host = self
self.profiles = ProfilesManager()
self.contact_lists = {}
self.widgets = QuickWidgetsManager(self)
self.check_options()
if check_options is not None:
self.options = check_options()
else:
self.options = None
# widgets
self.visible_widgets = set() # widgets currently visible (must be filled by frontend)
self.selected_widget = None # widget currently selected (must be filled by frontend)
## bridge ##
try:
self.bridge = DBusBridgeFrontend()
self.bridge = create_bridge()
except exceptions.BridgeExceptionNoService:
print(_(u"Can't connect to SàT backend, are you sure it's launched ?"))
sys.exit(1)
@ -202,6 +241,11 @@ class QuickApp(object):
except (TypeError, AttributeError):
return self.profiles.chooseOneProfile()
@property
def visible_widgets(self):
"""widgets currently visible (must be implemented by frontend)"""
raise NotImplementedError
def registerSignal(self, functionName, handler=None, iface="core", with_profile=True):
"""Register a handler for a signal
@ -211,7 +255,7 @@ class QuickApp(object):
@param with_profile (boolean): True if the signal concerns a specific profile, in that case the profile name has to be passed by the caller
"""
if handler is None:
handler = getattr(self, "%s%s" % (functionName, 'Handler'))
handler = getattr(self, "{}{}".format(functionName, 'Handler'))
if not with_profile:
self.bridge.register(functionName, handler, iface)
return
@ -236,24 +280,15 @@ class QuickApp(object):
@param profile_manager: instance of a subclass of Quick_frontend.QuickProfileManager
"""
if self.options.profile:
if self.options and self.options.profile:
profile_manager.autoconnect([self.options.profile])
def check_options(self):
"""Check command line options"""
usage = _("""
%prog [options]
def profilePlugged(self, profile):
"""Method called when the profile is fully plugged, to launch frontend specific workflow
%prog --help for options list
""")
parser = OptionParser(usage=usage) # TODO: use argparse
parser.add_option("-p", "--profile", help=_("Select the profile to use"))
(self.options, args) = parser.parse_args()
if self.options.profile:
self.options.profile = self.options.profile.decode('utf-8')
return args
@param profile(unicode): %(doc_profile)s
"""
pass
def asyncConnect(self, profile, callback=None, errback=None):
if not callback:
@ -281,7 +316,7 @@ class QuickApp(object):
will be called when profiles are choosen and are to be plugged soon
"""
raise NotImplementedError
pass
def unplug_profile(self, profile):
"""Tell the application to not follow anymore the profile"""
@ -292,6 +327,14 @@ class QuickApp(object):
def clear_profile(self):
self.profiles.clear()
def addContactList(self, profile):
"""Method to subclass to add a contact list widget
will be called on each profile session build
@return: a ContactList widget
"""
return NotImplementedError
def newWidget(self, widget):
raise NotImplementedError
@ -331,7 +374,7 @@ class QuickApp(object):
def sendMessage(self, to_jid, message, subject='', mess_type="auto", extra={}, callback=None, errback=None, profile_key=C.PROF_KEY_NONE):
if callback is None:
callback = lambda: None
callback = lambda dummy=None: None # FIXME: optional argument is here because pyjamas doesn't support callback without arg with json proxy
if errback is None:
errback = lambda failure: self.showDialog(failure.fullname, failure.message, "error")
self.bridge.sendMessage(to_jid, message, subject, mess_type, extra, profile_key, callback=callback, errback=errback)
@ -500,10 +543,9 @@ class QuickApp(object):
@profile: current profile
"""
from_jid = jid.JID(from_jid_s) if from_jid_s != C.ENTITY_ALL else C.ENTITY_ALL
for widget in self.visible_widgets:
if isinstance(widget, quick_chat.QuickChat):
if from_jid == C.ENTITY_ALL or from_jid.bare == widget.target.bare:
widget.updateChatState(from_jid, state)
for widget in self.widgets.getWidgets(quick_chat.QuickChat):
if from_jid == C.ENTITY_ALL or from_jid.bare == widget.target.bare:
widget.updateChatState(from_jid, state)
def _subscribe_cb(self, answer, data):
entity, profile = data
@ -554,8 +596,9 @@ class QuickApp(object):
self.contact_lists[profile].setCache(entity, 'nick', value)
elif key == "avatar":
if entity in self.contact_lists[profile]:
filename = self.bridge.getAvatarFile(value)
self.contact_lists[profile].setCache(entity, 'avatar', filename)
def gotFilename(filename):
self.contact_lists[profile].setCache(entity, 'avatar', filename)
self.bridge.getAvatarFile(value, callback=gotFilename)
def askConfirmationHandler(self, confirm_id, confirm_type, data, profile):
raise NotImplementedError

View file

@ -20,11 +20,18 @@
from sat.core.i18n import _
from sat.core.log import getLogger
log = getLogger(__name__)
from sat_frontends.tools.jid import JID
from sat_frontends.tools import jid
from sat_frontends.quick_frontend import quick_widgets
from sat_frontends.quick_frontend.constants import Const as C
try:
# FIXME: to be removed when an acceptable solution is here
unicode('') # XXX: unicode doesn't exist in pyjamas
except (TypeError, AttributeError): # Error raised is not the same depending on pyjsbuild options
unicode = lambda x: str(x)
class QuickChat(quick_widgets.QuickWidget):
def __init__(self, host, target, type_=C.CHAT_ONE2ONE, profiles=None):
@ -153,11 +160,11 @@ class QuickChat(quick_widgets.QuickWidget):
def onHistory(history):
for line in history:
timestamp, from_jid, to_jid, message, _type, extra = line
if ((self.type == C.CHAT_GROUP and _type != C.MESS_TYPE_GROUPCHAT) or
(self.type == C.CHAT_ONE2ONE and _type == C.MESS_TYPE_GROUPCHAT)):
timestamp, from_jid, to_jid, message, type_, extra = line # 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
self.printMessage(JID(from_jid), message, profile, timestamp)
self.printMessage(jid.JID(from_jid), message, {'timestamp':timestamp}, profile)
self.afterHistoryPrint()
def onHistoryError(err):
@ -165,7 +172,7 @@ class QuickChat(quick_widgets.QuickWidget):
target = self.target.bare
return self.host.bridge.getHistory(self.host.profiles[profile].whoami.bare, target, size, search=search, profile=profile, callback=onHistory, errback=onHistoryError)
self.host.bridge.getHistory(unicode(self.host.profiles[profile].whoami.bare), unicode(target), size, True, search, profile, callback=onHistory, errback=onHistoryError)
def _get_nick(self, entity):
"""Return nick of this entity when possible"""
@ -193,29 +200,27 @@ class QuickChat(quick_widgets.QuickWidget):
chat_widget = self.getOrCreatePrivateWidget(target)
chat_widget.newMessage(from_jid, target, msg, type_, extra, profile)
else:
timestamp = extra.get('archive')
if type_ == C.MESS_TYPE_INFO:
self.printInfo(msg, timestamp=float(timestamp) if timestamp else None)
self.printInfo(msg, extra=extra)
else:
self.printMessage(from_jid, msg, profile, float(timestamp) if timestamp else None)
self.printMessage(from_jid, msg, extra, profile)
def printMessage(self, from_jid, msg, profile, timestamp=None):
def printMessage(self, from_jid, msg, extra=None, profile=C.PROF_KEY_NONE):
"""Print message in chat window. Must be implemented by child class"""
jid = JID(from_jid)
nick = self._get_nick(jid)
mymess = (jid.resource == self.nick) if self.type == C.CHAT_GROUP else (jid.bare == self.host.profiles[profile].whoami.bare) #mymess = True if message comes from local user
nick = self._get_nick(from_jid)
mymess = (from_jid.resource == self.nick) if self.type == C.CHAT_GROUP else (from_jid.bare == self.host.profiles[profile].whoami.bare) #mymess = True if message comes from local user
if msg.startswith('/me '):
self.printInfo('* %s %s' % (nick, msg[4:]), type_='me', timestamp=timestamp)
self.printInfo('* %s %s' % (nick, msg[4:]), type_='me', extra=extra)
return
return jid, nick, mymess
return nick, mymess
def printInfo(self, msg, type_='normal', timestamp=None):
def printInfo(self, msg, type_='normal', extra=None):
"""Print general info
@param msg: message to print
@type_: one of:
normal: general info like "toto has joined the room"
me: "/me" information like "/me clenches his fist" ==> "toto clenches his fist"
@param timestamp (float): number of seconds since epoch
@param extra (dict): message data
"""
raise NotImplementedError

View file

@ -22,7 +22,18 @@ from sat.core.log import getLogger
log = getLogger(__name__)
from sat_frontends.quick_frontend.quick_widgets import QuickWidget
from sat_frontends.quick_frontend.constants import Const as C
from sat_frontends.tools import jid
try:
# FIXME: to be removed when an acceptable solution is here
unicode('') # XXX: unicode doesn't exist in pyjamas
except (TypeError, AttributeError): # Error raised is not the same depending on pyjsbuild options
# XXX: pyjamas' max doesn't support key argument, so we implement it ourself
pyjamas_max = max
def max(iterable, key):
iter_cpy = list(iterable)
iter_cpy.sort(key=key)
return pyjamas_max(iter_cpy)
class QuickContactList(QuickWidget):
@ -78,18 +89,6 @@ class QuickContactList(QuickWidget):
for contact in contacts:
self.host.newContactHandler(*contact, profile=self.profile)
presences = self.host.bridge.getPresenceStatuses(self.profile)
for contact in presences:
for res in presences[contact]:
jabber_id = ('%s/%s' % (jid.JID(contact).bare, res)) if res else contact
show = presences[contact][res][0]
priority = presences[contact][res][1]
statuses = presences[contact][res][2]
self.host.presenceUpdateHandler(jabber_id, show, priority, statuses, self.profile)
data = self.host.bridge.getEntityData(contact, ['avatar', 'nick'], self.profile)
for key in ('avatar', 'nick'):
if key in data:
self.host.entityDataUpdatedHandler(contact, key, data[key], self.profile)
self.host.bridge.getContacts(self.profile, callback=gotContacts)
def update(self):
@ -105,7 +104,12 @@ class QuickContactList(QuickWidget):
- if resource is given, it is used
@param name(unicode): name the data to get, or None to get everything
"""
cache = self._cache[entity.bare]
try:
cache = self._cache[entity.bare]
except KeyError:
self.setContact(entity)
cache = self._cache[entity.bare]
if name is None:
return cache
try:
@ -280,10 +284,6 @@ class QuickContactList(QuickWidget):
set_.difference_update(to_remove)
self.update()
def add(self, jid, param_groups=None):
"""add a contact to the list"""
raise NotImplementedError
def updatePresence(self, entity, show, priority, statuses):
"""Update entity's presence status
@ -311,7 +311,6 @@ class QuickContactList(QuickWidget):
priority_resource = max(resources_data, key=lambda res: resources_data[res][C.PRESENCE_PRIORITY])
cache[C.CONTACT_MAIN_RESOURCE] = priority_resource
self.update()
def unselectAll(self):
"""Unselect all contacts"""

View file

@ -17,7 +17,9 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from sat.core.i18n import _
from os.path import exists, splitext
from optparse import OptionParser
def getNewPath(path):
""" Check if path exists, and find a non existant path if needed """
@ -31,3 +33,19 @@ def getNewPath(path):
return new_path
idx+=1
def check_options():
"""Check command line options"""
usage = _("""
%prog [options]
%prog --help for options list
""")
parser = OptionParser(usage=usage) # TODO: use argparse
parser.add_option("-p", "--profile", help=_("Select the profile to use"))
(options, args) = parser.parse_args()
if options.profile:
options.profile = options.profile.decode('utf-8')
return options

View file

@ -25,6 +25,13 @@ from sat.core import exceptions
classes_map = {}
try:
# FIXME: to be removed when an acceptable solution is here
unicode('') # XXX: unicode doesn't exist in pyjamas
except (TypeError, AttributeError): # Error raised is not the same depending on pyjsbuild options
unicode = str
def register(base_cls, child_cls=None):
"""Register a child class to use by default when a base class is needed
@ -43,6 +50,40 @@ class QuickWidgetsManager(object):
self.host = host
self._widgets = {}
def __iter__(self):
"""Iterate throught all widgets"""
for widget_map in self._widgets.itervalues():
for widget in widget_map.itervalues():
yield widget
def getRealClass(self, class_):
"""Return class registered for given class_
@param class_: subclass of QuickWidget
@return: class actually used to create widget
"""
try:
cls = classes_map[class_]
except KeyError:
cls = class_
if cls is None:
raise exceptions.InternalError("There is not class registered for {}".format(class_))
return cls
def getWidgets(self, class_):
"""Get all subclassed widgets
@param class_: subclass of QuickWidget, same parameter as used in [getOrCreateWidget]
@return: iterator on widgets
"""
class_ = self.getRealClass(class_)
try:
widgets_map = self._widgets[class_]
except KeyError:
return iter([])
else:
return widgets_map.itervalues()
def getOrCreateWidget(self, class_, target, *args, **kwargs):
"""Get an existing widget or create a new one when necessary
@ -60,13 +101,7 @@ class QuickWidgetsManager(object):
if 'force_hash' is present, the hash given in value will be used instead of the one returned by class_.getWidgetHash
@return: a class_ instance, either new or already existing
"""
# class management
try:
cls = classes_map[class_]
except KeyError:
cls = class_
if cls is None:
raise exceptions.InternalError("There is not class registered for {}".format(class_))
cls = self.getRealClass(class_)
# arguments management
_args = [self.host, target] + list(args) or [] # FIXME: check if it's really necessary to use optional args
@ -86,12 +121,12 @@ class QuickWidgetsManager(object):
hash_ = cls.getWidgetHash(target, _kwargs['profiles'])
# widget creation or retrieval
widgets_list = self._widgets.setdefault(cls, {}) # we sorts widgets by classes
widgets_map = self._widgets.setdefault(cls, {}) # we sorts widgets by classes
if not cls.SINGLE:
widget = None # if the class is not SINGLE, we always create a new widget
else:
try:
widget = widgets_list[hash_]
widget = widgets_map[hash_]
widget.addTarget(target)
except KeyError:
widget = None
@ -106,7 +141,7 @@ class QuickWidgetsManager(object):
log.debug(u"Creating new widget for target {} {}".format(target, cls))
widget = cls(*_args, **_kwargs)
widgets_list[hash_] = widget
widgets_map[hash_] = widget
if on_new_widget == 'NEW_WIDGET':
self.host.newWidget(widget)

View file

@ -18,20 +18,84 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
class JID(unicode):
# hack to use this module with pyjamas
try:
unicode('') # XXX: unicode doesn't exist in pyjamas
# normal version
class BaseJID(unicode):
def __new__(cls, jid_str):
self = unicode.__new__(cls, cls._normalize(jid_str))
return self
def __init__(self, jid_str):
pass
def _parse(self):
"""Find node domain and resource"""
node_end = self.find('@')
if node_end < 0:
node_end = 0
domain_end = self.find('/')
if domain_end == 0:
raise ValueError("a jid can't start with '/'")
if domain_end == -1:
domain_end = len(self)
self.node = self[:node_end] or None
self.domain = self[(node_end + 1) if node_end else 0:domain_end]
self.resource = self[domain_end + 1:] or None
except (TypeError, AttributeError): # Error raised is not the same depending on pyjsbuild options
# pyjamas version
class BaseJID(object):
def __init__(self, jid_str):
self.__internal_str = JID._normalize(jid_str)
def __str__(self):
return self.__internal_str
def __getattr__(self, name):
return getattr(self.__internal_str, name)
def __eq__(self, other):
return (self.node == other.node
and self.domain == other.domain
and self.resource == other.resource)
def __hash__(self):
return hash(self.__internal_str)
def find(self, *args):
return self.__internal_str.find(*args)
def _parse(self):
"""Find node domain and resource"""
node_end = self.__internal_str.find('@')
if node_end < 0:
node_end = 0
domain_end = self.__internal_str.find('/')
if domain_end == 0:
raise ValueError("a jid can't start with '/'")
if domain_end == -1:
domain_end = len(self.__internal_str)
self.node = self.__internal_str[:node_end] or None
self.domain = self.__internal_str[(node_end + 1) if node_end else 0:domain_end]
self.resource = self.__internal_str[domain_end + 1:] or None
class JID(BaseJID):
"""This class help manage JID (Node@Domaine/Resource)"""
def __new__(cls, jid):
self = unicode.__new__(cls, cls._normalize(jid))
def __init__(self, jid_str):
super(JID, self).__init__(jid_str)
self._parse()
return self
@classmethod
def _normalize(cls, jid):
@staticmethod
def _normalize(jid_str):
"""Naive normalization before instantiating and parsing the JID"""
if not jid:
return jid
tokens = jid.split('/')
if not jid_str:
return jid_str
tokens = jid_str.split('/')
tokens[0] = tokens[0].lower() # force node and domain to lower-case
return '/'.join(tokens)
@ -41,20 +105,6 @@ class JID(unicode):
return JID(self.domain)
return JID(u"{}@{}".format(self.node, self.domain))
def _parse(self):
"""Find node domain and resource"""
node_end = self.find('@')
if node_end < 0:
node_end = 0
domain_end = self.find('/')
if domain_end == 0:
raise ValueError("a jid can't start with '/'")
if domain_end == -1:
domain_end = len(self)
self.node = self[:node_end] or None
self.domain = self[(node_end + 1) if node_end else 0:domain_end]
self.resource = self[domain_end + 1:] or None
def is_valid(self):
"""
@return: True if the JID is XMPP compliant

View file

@ -210,7 +210,7 @@ class XMLUIBase(object):
@param title: force the title, or use XMLUI one if None
@param flags: list of string which can be:
- NO_CANCEL: the UI can't be cancelled
@param callback: if present, will be use with launchAction
@param callback: if present, will be used with launchAction
"""
self.host = host
top=parsed_dom.documentElement
@ -289,7 +289,7 @@ class XMLUIPanel(XMLUIBase):
self._main_cont = value
def _parseChilds(self, _xmlui_parent, current_node, wanted = ('container',), data = None):
"""Recursively parse childNodes of an elemen
"""Recursively parse childNodes of an element
@param _xmlui_parent: widget container with '_xmluiAppend' method
@param current_node: element from which childs will be parsed
@ -695,7 +695,7 @@ def registerClass(type_, class_):
def create(host, xml_data, title=None, flags=None, dom_parse=None, dom_free=None, callback=None, profile=C.PROF_KEY_NONE):
"""
@param dom_parse: methode equivalent to minidom.parseString (but which must manage unicode), or None to use default one
@param dom_parse: methode equivalent to minidom.parseString (but which must manage unicode), or None to use default one
@param dom_free: method used to free the parsed DOM
"""
if dom_parse is None:

View file

@ -285,8 +285,8 @@ class DbusObject(dbus.service.Object):
@dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
in_signature='ss', out_signature='s',
async_callbacks=None)
def getLastResource(self, contact_jid, profile_key="@DEFAULT@"):
return self._callback("getLastResource", unicode(contact_jid), unicode(profile_key))
def getMainResource(self, contact_jid, profile_key="@DEFAULT@"):
return self._callback("getMainResource", unicode(contact_jid), unicode(profile_key))
@dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
in_signature='ss', out_signature='s',

View file

@ -446,17 +446,35 @@ class DbusConstructor(Constructor):
'args': self.getArguments(function['sig_in'], name=arg_doc, default=default)}
if function["type"] == "method":
# XXX: we can manage blocking call in the same way as async one: if callback is None the call will be blocking
completion['debug'] = "" if not self.options.debug else 'log.debug ("%s")\n%s' % (section, 8 * ' ')
completion['args_result'] = self.getArguments(function['sig_in'], name=arg_doc)
completion['async_args'] = 'callback=None, errback=None' if async else ''
completion['async_comma'] = ', ' if async and function['sig_in'] else ''
completion['error_handler'] = ("error_handler = None if callback is None else lambda err:errback(dbus_to_bridge_exception(err))\n" + 8*" ") if async else ''
completion['async_args_result'] = 'timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler' if async else ''
completion['async_args'] = 'callback=None, errback=None'
completion['async_comma'] = ', ' if function['sig_in'] else ''
completion['error_handler'] = """if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
"""
if async:
completion['blocking_call'] = ''
completion['async_args_result'] = 'timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler'
else:
# XXX: To have a blocking call, we must have not reply_handler, so we test if callback exists, and add reply_handler only in this case
completion['blocking_call'] = """kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
"""
completion['async_args_result'] = '**kwargs'
result = "self.db_%(category)s_iface.%(name)s(%(args_result)s%(async_comma)s%(async_args_result)s)" % completion
completion['result'] = ("unicode(%s)" if self.options.unicode and function['sig_out'] == 's' else "%s") % result
methods_part.append("""\
def %(name)s(self, %(args)s%(async_comma)s%(async_args)s):
%(error_handler)s%(debug)sreturn %(result)s
%(error_handler)s%(blocking_call)s%(debug)sreturn %(result)s
""" % completion)
#at this point, methods_part should be filled,

View file

@ -279,7 +279,7 @@ doc_param_0=group: name of the group to check
doc_param_1=%(doc_profile_key)s
doc_return=array of jids
[getLastResource]
[getMainResource]
type=method
category=core
sig_in=ss
@ -288,7 +288,7 @@ param_1_default="@DEFAULT@"
doc=Return the last resource connected for a contact
doc_param_0=contact_jid: jid of the contact
doc_param_1=%(doc_profile_key)s
doc_return=the last resource connected of the contact, or ""
doc_return=the resource connected of the contact with highest priority, or ""
[getPresenceStatuses]
type=method

View file

@ -102,10 +102,10 @@ class DBusBridgeFrontend(BridgeFrontend):
async = False
if kwargs:
if 'callback' in kwargs and 'errback' in kwargs:
if 'callback' in kwargs:
async = True
_callback = kwargs.pop('callback')
_errback = kwargs.pop('errback')
_errback = kwargs.pop('errback', lambda failure: log.error(unicode(failure)))
elif len(args) >= 2 and callable(args[-1]) and callable(args[-2]):
async = True
args = list(args)
@ -122,4 +122,5 @@ class DBusBridgeFrontend(BridgeFrontend):
return method(*args, **kwargs)
return getPluginMethod
##METHODS_PART##

View file

@ -76,7 +76,8 @@ class Const(object):
PROF_KEY_NONE = '@NONE@'
PROF_KEY_DEFAULT = '@DEFAULT@'
ENTITY_ALL = '@ALL@'
ENTITY_LAST_RESOURCE = 'LAST_RESOURCE'
ENTITY_ALL_RESOURCES = '@ALL_RESOURCES@'
ENTITY_MAIN_RESOURCE = '@MAIN_RESOURCE@'
ENTITY_CAP_HASH = 'CAP_HASH'

View file

@ -94,5 +94,9 @@ class PasswordError(Exception):
pass
class PermissionError(Exception):
pass
class SkipHistory(Exception): # used in MessageReceivedTrigger to avoid history writting
pass

Some files were not shown because too many files have changed in this diff Show more