core (menus): improvments:
- use the new convention for bridge names (getMenus ==> menusGet, etc.)
- menu now use canonical path, which is the untranslated path with each element stripped and lowercase, it must be unique by menu type
- added menuLaunch method to manually launch a menu like an action, canonical path is used instead of id
- added SECURITY_LIMIT_MAX constant
This commit is contained in:
parent
d5fbafd979
commit
d8b9c85cc0
7 changed files with 164 additions and 65 deletions
|
|
@ -338,34 +338,6 @@ class Bridge(BridgeFrontend):
|
|||
kwargs['error_handler'] = error_handler
|
||||
return unicode(self.db_core_iface.getMainResource(contact_jid, profile_key, **kwargs))
|
||||
|
||||
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, 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@", callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
|
|
@ -528,6 +500,43 @@ class Bridge(BridgeFrontend):
|
|||
kwargs['error_handler'] = error_handler
|
||||
return self.db_core_iface.loadParamsTemplate(filename, **kwargs)
|
||||
|
||||
def menuHelpGet(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.menuHelpGet(menu_id, language, **kwargs))
|
||||
|
||||
def menuLaunch(self, menu_type, path, data, security_limit, 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))
|
||||
return self.db_core_iface.menuLaunch(menu_type, path, data, security_limit, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
|
||||
|
||||
def menusGet(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.menusGet(language, security_limit, **kwargs)
|
||||
|
||||
def messageSend(self, to_jid, message, subject={}, mess_type="auto", extra={}, profile_key="@NONE@", callback=None, errback=None):
|
||||
if callback is None:
|
||||
error_handler = None
|
||||
|
|
|
|||
|
|
@ -514,7 +514,7 @@ class PrimitivusApp(QuickApp, InputHistory):
|
|||
"""
|
||||
def add_menu_cb(callback_id):
|
||||
self.launchAction(callback_id, menu_data, profile=self.current_profile)
|
||||
for id_, type_, path, path_i18n, extra in self.bridge.getMenus("", C.NO_SECURITY_LIMIT ): # TODO: manage extra
|
||||
for id_, type_, path, path_i18n, extra in self.bridge.menusGet("", C.NO_SECURITY_LIMIT ): # TODO: manage extra
|
||||
if type_ != type_filter:
|
||||
continue
|
||||
if len(path) != 2:
|
||||
|
|
|
|||
|
|
@ -857,8 +857,15 @@ class QuickApp(object):
|
|||
if action_data:
|
||||
raise exceptions.DataError(u"Not all keys in action_data are managed ({keys})".format(keys=', '.join(action_data.keys())))
|
||||
|
||||
|
||||
def _actionCb(self, data, callback, callback_id, profile):
|
||||
if callback is None:
|
||||
self.actionManager(data, profile=profile)
|
||||
else:
|
||||
callback(data=data, cb_id=callback_id, profile=profile)
|
||||
|
||||
def launchAction(self, callback_id, data=None, callback=None, profile=C.PROF_KEY_NONE):
|
||||
""" Launch a dynamic action
|
||||
"""Launch a dynamic action
|
||||
|
||||
@param callback_id: id of the action to launch
|
||||
@param data: data needed only for certain actions
|
||||
|
|
@ -873,16 +880,29 @@ class QuickApp(object):
|
|||
"""
|
||||
if data is None:
|
||||
data = dict()
|
||||
|
||||
|
||||
def action_cb(data):
|
||||
if callback is None:
|
||||
self.actionManager(data, profile=profile)
|
||||
else:
|
||||
callback(data=data, cb_id=callback_id, profile=profile)
|
||||
|
||||
action_cb = lambda data: self._actionCb(data, callback, callback_id, profile)
|
||||
self.bridge.launchAction(callback_id, data, profile, callback=action_cb, errback=self.dialogFailure)
|
||||
|
||||
def launchMenu(self, menu_type, path, data=None, callback=None, security_limit=C.SECURITY_LIMIT_MAX, profile=C.PROF_KEY_NONE):
|
||||
"""Launch a menu manually
|
||||
|
||||
@param menu_type(unicode): type of the menu to launch
|
||||
@param path(iterable[unicode]): path to the menu
|
||||
@param data: data needed only for certain actions
|
||||
@param callback(callable, None): will be called with the resut
|
||||
if None, self.actionManager will be called
|
||||
else the callable will be called with the following kw parameters:
|
||||
- data: action_data
|
||||
- cb_id: (menu_type, path) tuple
|
||||
- profile: %(doc_profile)s
|
||||
@param profile: %(doc_profile)s
|
||||
|
||||
"""
|
||||
if data is None:
|
||||
data = dict()
|
||||
action_cb = lambda data: self._actionCb(data, callback, (menu_type, path), profile)
|
||||
self.bridge.menuLaunch(menu_type, path, data, security_limit, profile, callback=action_cb, errback=self.dialogFailure)
|
||||
|
||||
def _avatarGetCb(self, avatar_path, entity, contact_list, profile):
|
||||
path = avatar_path or self.getDefaultAvatar(entity)
|
||||
contact_list.setCache(entity, "avatar", path)
|
||||
|
|
|
|||
|
|
@ -640,7 +640,7 @@ doc_return= a dict which map profile to progress_dict
|
|||
progress_dict map progress_id to progress_data
|
||||
progress_data is the same dict as returned by [progressGet]
|
||||
|
||||
[getMenus]
|
||||
[menusGet]
|
||||
type=method
|
||||
category=core
|
||||
sig_in=si
|
||||
|
|
@ -656,7 +656,22 @@ doc_return=list of tuple with the following value:
|
|||
- menu_path_i18n: translated path of the menu
|
||||
- extra: extra data, like icon name
|
||||
|
||||
[getMenuHelp]
|
||||
[menuLaunch]
|
||||
async=
|
||||
type=method
|
||||
category=core
|
||||
sig_in=sasa{ss}is
|
||||
sig_out=a{ss}
|
||||
doc=Launch a registred menu
|
||||
doc_param_0=menu_type: type of the menu (C.MENU_*)
|
||||
doc_param_1=path: canonical (untranslated) path of the menu
|
||||
doc_param_2=data: optional data
|
||||
doc_param_3=%(doc_security_limit)s
|
||||
doc_param_4=%(doc_profile_key)s
|
||||
doc_return=dict where key can be:
|
||||
- xmlui: a XMLUI need to be displayed
|
||||
|
||||
[menuHelpGet]
|
||||
type=method
|
||||
category=core
|
||||
sig_in=ss
|
||||
|
|
|
|||
|
|
@ -299,18 +299,6 @@ class DbusObject(dbus.service.Object):
|
|||
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',
|
||||
async_callbacks=None)
|
||||
def getMenuHelp(self, menu_id, language):
|
||||
return self._callback("getMenuHelp", unicode(menu_id), unicode(language))
|
||||
|
||||
@dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
|
||||
in_signature='si', out_signature='a(ssasasa{ss})',
|
||||
async_callbacks=None)
|
||||
def getMenus(self, language, security_limit):
|
||||
return self._callback("getMenus", unicode(language), security_limit)
|
||||
|
||||
@dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
|
||||
in_signature='ssss', out_signature='s',
|
||||
async_callbacks=None)
|
||||
|
|
@ -389,6 +377,24 @@ class DbusObject(dbus.service.Object):
|
|||
def loadParamsTemplate(self, filename):
|
||||
return self._callback("loadParamsTemplate", unicode(filename))
|
||||
|
||||
@dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
|
||||
in_signature='ss', out_signature='s',
|
||||
async_callbacks=None)
|
||||
def menuHelpGet(self, menu_id, language):
|
||||
return self._callback("menuHelpGet", unicode(menu_id), unicode(language))
|
||||
|
||||
@dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
|
||||
in_signature='sasa{ss}is', out_signature='a{ss}',
|
||||
async_callbacks=('callback', 'errback'))
|
||||
def menuLaunch(self, menu_type, path, data, security_limit, profile_key, callback=None, errback=None):
|
||||
return self._callback("menuLaunch", unicode(menu_type), path, data, security_limit, unicode(profile_key), callback=callback, errback=errback)
|
||||
|
||||
@dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
|
||||
in_signature='si', out_signature='a(ssasasa{ss})',
|
||||
async_callbacks=None)
|
||||
def menusGet(self, language, security_limit):
|
||||
return self._callback("menusGet", unicode(language), security_limit)
|
||||
|
||||
@dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
|
||||
in_signature='sa{ss}a{ss}sa{ss}s', out_signature='',
|
||||
async_callbacks=('callback', 'errback'))
|
||||
|
|
|
|||
|
|
@ -49,7 +49,8 @@ class Const(object):
|
|||
|
||||
|
||||
## Parameters ##
|
||||
NO_SECURITY_LIMIT = -1
|
||||
NO_SECURITY_LIMIT = -1 # FIXME: to rename
|
||||
SECURITY_LIMIT_MAX = 0
|
||||
INDIVIDUAL = "individual"
|
||||
GENERAL = "general"
|
||||
# General parameters
|
||||
|
|
@ -64,6 +65,7 @@ class Const(object):
|
|||
MEMORY_CRYPTO_NAMESPACE = 'crypto' # for the private persistent binary dict
|
||||
MEMORY_CRYPTO_KEY = 'personal_key'
|
||||
# Parameters for static blog pages
|
||||
# FIXME: blog constants should not be in core constants
|
||||
STATIC_BLOG_KEY = "Blog page"
|
||||
STATIC_BLOG_PARAM_TITLE = "Title"
|
||||
STATIC_BLOG_PARAM_BANNER = "Banner"
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ class SAT(service.Service):
|
|||
def __init__(self):
|
||||
self._cb_map = {} # map from callback_id to callbacks
|
||||
self._menus = OrderedDict() # dynamic menus. key: callback_id, value: menu data (dictionnary)
|
||||
self._menus_paths = {} # path to id. key: (menu_type, lower case tuple of path), value: menu id
|
||||
self.initialised = defer.Deferred()
|
||||
self.profiles = {}
|
||||
self.plugins = {}
|
||||
|
|
@ -112,8 +113,9 @@ class SAT(service.Service):
|
|||
self.bridge.register_method("actionsGet", self.actionsGet)
|
||||
self.bridge.register_method("progressGet", self._progressGet)
|
||||
self.bridge.register_method("progressGetAll", self._progressGetAll)
|
||||
self.bridge.register_method("getMenus", self.getMenus)
|
||||
self.bridge.register_method("getMenuHelp", self.getMenuHelp)
|
||||
self.bridge.register_method("menusGet", self.getMenus)
|
||||
self.bridge.register_method("menuHelpGet", self.getMenuHelp)
|
||||
self.bridge.register_method("menuLaunch", self._launchMenu)
|
||||
self.bridge.register_method("discoInfos", self.memory.disco._discoInfos)
|
||||
self.bridge.register_method("discoItems", self.memory.disco._discoItems)
|
||||
self.bridge.register_method("saveParamsTemplate", self.memory.save_xml)
|
||||
|
|
@ -917,10 +919,11 @@ class SAT(service.Service):
|
|||
def registerCallback(self, callback, *args, **kwargs):
|
||||
"""Register a callback.
|
||||
|
||||
Use with_data=True in kwargs if the callback use the optional data dict
|
||||
use force_id=id to avoid generated id. Can lead to name conflict, avoid if possible
|
||||
use one_shot=True to delete callback once it have been called
|
||||
@param callback: any callable
|
||||
@param callback(callable): method to call
|
||||
@param kwargs: can contain:
|
||||
with_data(bool): True if the callback use the optional data dict
|
||||
force_id(unicode): id to avoid generated id. Can lead to name conflict, avoid if possible
|
||||
one_shot(bool): True to delete callback once it have been called
|
||||
@return: id of the registered callback
|
||||
"""
|
||||
callback_id = kwargs.pop('force_id', None)
|
||||
|
|
@ -949,6 +952,7 @@ class SAT(service.Service):
|
|||
|
||||
def launchCallback(self, callback_id, data=None, profile_key=C.PROF_KEY_NONE):
|
||||
"""Launch a specific callback
|
||||
|
||||
@param callback_id: id of the action (callback) to launch
|
||||
@param data: optional data
|
||||
@profile_key: %(doc_profile_key)s
|
||||
|
|
@ -958,13 +962,14 @@ class SAT(service.Service):
|
|||
- C.BOOL_TRUE
|
||||
- C.BOOL_FALSE
|
||||
"""
|
||||
# FIXME: security limit need to be checked here
|
||||
try:
|
||||
client = self.getClient(profile_key)
|
||||
except exceptions.NotFound:
|
||||
# client is not available yet
|
||||
profile = self.memory.getProfileName(profile_key)
|
||||
if not profile:
|
||||
raise exceptions.ProfileUnknownError(_('trying to launch action with a non-existant profile'))
|
||||
raise exceptions.ProfileUnknownError(_(u'trying to launch action with a non-existant profile'))
|
||||
else:
|
||||
profile = client.profile
|
||||
# we check if the action is kept, and remove it
|
||||
|
|
@ -996,17 +1001,27 @@ class SAT(service.Service):
|
|||
|
||||
#Menus management
|
||||
|
||||
def _getMenuCanonicalPath(self, path):
|
||||
"""give canonical form of path
|
||||
|
||||
canonical form is a tuple of the path were every element is stripped and lowercase
|
||||
@param path(iterable[unicode]): untranslated path to menu
|
||||
@return (tuple[unicode]): canonical form of path
|
||||
"""
|
||||
return tuple((p.lower().strip() for p in path))
|
||||
|
||||
def importMenu(self, path, callback, security_limit=C.NO_SECURITY_LIMIT, help_string="", type_=C.MENU_GLOBAL):
|
||||
"""register a new menu for frontends
|
||||
|
||||
@param path: path to go to the menu (category/subcategory/.../item), must be an iterable (e.g.: ("File", "Open"))
|
||||
@param path(iterable[unicode]): path to go to the menu (category/subcategory/.../item) (e.g.: ("File", "Open"))
|
||||
/!\ use D_() instead of _() for translations (e.g. (D_("File"), D_("Open")))
|
||||
@param callback: method to be called when menuitem is selected, callable or a callback id (string) as returned by [registerCallback]
|
||||
@param security_limit: %(doc_security_limit)s
|
||||
untranslated/lower case path can be used to identity a menu, for this reason it must be unique independently of case.
|
||||
@param callback(callable): method to be called when menuitem is selected, callable or a callback id (string) as returned by [registerCallback]
|
||||
@param security_limit(int): %(doc_security_limit)s
|
||||
/!\ security_limit MUST be added to data in launchCallback if used #TODO
|
||||
@param help_string: string used to indicate what the menu do (can be show as a tooltip).
|
||||
@param help_string(unicode): string used to indicate what the menu do (can be show as a tooltip).
|
||||
/!\ use D_() instead of _() for translations
|
||||
@param type: one of:
|
||||
@param type(unicode): one of:
|
||||
- C.MENU_GLOBAL: classical menu, can be shown in a menubar on top (e.g. something like File/Open)
|
||||
- C.MENU_ROOM: like a global menu, but only shown in multi-user chat
|
||||
menu_data must contain a "room_jid" data
|
||||
|
|
@ -1018,7 +1033,7 @@ class SAT(service.Service):
|
|||
menu_data must contain a "room_jid" data
|
||||
- C.MENU_ROSTER_GROUP_CONTEXT: contextual menu, used with group (e.g.: publish microblog, group is already filled)
|
||||
menu_data must contain a "group" data
|
||||
@return: menu_id (same as callback_id)
|
||||
@return (unicode): menu_id (same as callback_id)
|
||||
"""
|
||||
|
||||
if callable(callback):
|
||||
|
|
@ -1038,13 +1053,22 @@ class SAT(service.Service):
|
|||
if menu_data['path'] == path and menu_data['type'] == type_:
|
||||
raise exceptions.ConflictError(_("A menu with the same path and type already exists"))
|
||||
|
||||
menu_data = {'path': path,
|
||||
path_canonical = self._getMenuCanonicalPath(path)
|
||||
menu_key = (type_, path_canonical)
|
||||
|
||||
if menu_key in self._menus_paths:
|
||||
raise exceptions.ConflictError(u"this menu path is already used: {path} ({menu_key})".format(
|
||||
path=path_canonical, menu_key=menu_key))
|
||||
|
||||
menu_data = {'path': tuple(path),
|
||||
'path_canonical': path_canonical,
|
||||
'security_limit': security_limit,
|
||||
'help_string': help_string,
|
||||
'type': type_
|
||||
}
|
||||
|
||||
self._menus[callback_id] = menu_data
|
||||
self._menus_paths[menu_key] = callback_id
|
||||
|
||||
return callback_id
|
||||
|
||||
|
|
@ -1077,6 +1101,29 @@ class SAT(service.Service):
|
|||
|
||||
return ret
|
||||
|
||||
def _launchMenu(self, menu_type, path, data=None, security_limit=C.NO_SECURITY_LIMIT, profile_key=C.PROF_KEY_NONE):
|
||||
client = self.getClient(profile_key)
|
||||
return self.launchMenu(client, menu_type, path, data, security_limit)
|
||||
|
||||
def launchMenu(self, client, menu_type, path, data=None, security_limit=C.NO_SECURITY_LIMIT):
|
||||
"""launch action a menu action
|
||||
|
||||
@param menu_type(unicode): type of menu to launch
|
||||
@param path(iterable[unicode]): canonical path of the menu
|
||||
@params data(dict): menu data
|
||||
@raise NotFound: this path is not known
|
||||
"""
|
||||
# FIXME: manage security_limit here
|
||||
# defaut security limit should be high instead of C.NO_SECURITY_LIMIT
|
||||
canonical_path = self._getMenuCanonicalPath(path)
|
||||
menu_key = (menu_type, canonical_path)
|
||||
try:
|
||||
callback_id = self._menus_paths[menu_key]
|
||||
except KeyError:
|
||||
raise exceptions.NotFound(u"Can't find menu {path} ({menu_type})".format(
|
||||
path=canonical_path, menu_type=menu_type))
|
||||
return self.launchCallback(callback_id, data, client.profile)
|
||||
|
||||
def getMenuHelp(self, menu_id, language=''):
|
||||
"""return the help string of the menu
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue