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:
Goffi 2017-01-26 20:29:48 +01:00
commit d8b9c85cc0
2 changed files with 164 additions and 65 deletions

View file

@ -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"

View file

@ -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