core (memory/params): added extra argument to filter out params notably in getParamsUI:
In some case, it may be desirable for a frontend to not expose some parameters to user (e.g. it is the case on Android with the `autoconnect_backend` parameter). An new `extra` parameter has been added to a couple of parameters method for that: it can contain the `ignore` key with a list of [category, name] of parameters to skip.
This commit is contained in:
parent
610a8ff10b
commit
87659cf0f0
1 changed files with 244 additions and 232 deletions
|
|
@ -31,6 +31,7 @@ from twisted.python.failure import Failure
|
|||
from twisted.words.xish import domish
|
||||
from twisted.words.protocols.jabber import jid
|
||||
from sat.tools.xml_tools import paramsXML2XMLUI, getText
|
||||
from sat.tools.common import data_format
|
||||
from xml.sax.saxutils import quoteattr
|
||||
|
||||
# TODO: params should be rewritten using Twisted directly instead of minidom
|
||||
|
|
@ -553,7 +554,7 @@ class Params(object):
|
|||
"""Helper method to get a specific attribute.
|
||||
|
||||
/!\ This method would return encrypted password values,
|
||||
to get the plain values you have to use _asyncGetParamA.
|
||||
to get the plain values you have to use asyncGetParamA.
|
||||
@param name: name of the parameter
|
||||
@param category: category of the parameter
|
||||
@param attr: name of the attribute (default: "value")
|
||||
|
|
@ -602,12 +603,12 @@ class Params(object):
|
|||
return value
|
||||
return self._getAttr(node[1], attr, value)
|
||||
|
||||
def asyncGetStringParamA(
|
||||
async def asyncGetStringParamA(
|
||||
self, name, category, attr="value", security_limit=C.NO_SECURITY_LIMIT,
|
||||
profile_key=C.PROF_KEY_NONE):
|
||||
d = self.asyncGetParamA(name, category, attr, security_limit, profile_key)
|
||||
d.addCallback(self._type_to_str)
|
||||
return d
|
||||
profile=C.PROF_KEY_NONE):
|
||||
value = await self.asyncGetParamA(
|
||||
name, category, attr, security_limit, profile_key=profile)
|
||||
return self._type_to_str(value)
|
||||
|
||||
def asyncGetParamA(
|
||||
self,
|
||||
|
|
@ -668,56 +669,50 @@ class Params(object):
|
|||
lambda value: self._asyncGetAttr(node[1], attr, value, profile)
|
||||
)
|
||||
|
||||
def asyncGetParamsValuesFromCategory(self, category, security_limit, profile_key):
|
||||
def _getParamsValuesFromCategory(
|
||||
self, category, security_limit, app, extra_s, profile_key):
|
||||
client = self.host.getClient(profile_key)
|
||||
extra = data_format.deserialise(extra_s)
|
||||
return defer.ensureDeferred(self.getParamsValuesFromCategory(
|
||||
client, category, security_limit, app, extra))
|
||||
|
||||
async def getParamsValuesFromCategory(
|
||||
self, client, category, security_limit, app='', extra=None):
|
||||
"""Get all parameters "attribute" for a category
|
||||
|
||||
@param category(unicode): the desired category
|
||||
@param security_limit(int): NO_SECURITY_LIMIT (-1) to return all the params.
|
||||
Otherwise sole the params which have a security level defined *and*
|
||||
lower or equal to the specified value are returned.
|
||||
@param profile_key: %(doc_profile_key)s
|
||||
@param app(str): see [getParams]
|
||||
@param extra(dict): see [getParams]
|
||||
@return (dict): key: param name, value: param value (converted to string if needed)
|
||||
"""
|
||||
# TODO: manage category of general type (without existant profile)
|
||||
profile = self.getProfileName(profile_key)
|
||||
if not profile:
|
||||
log.error(_("Asking params for inexistant profile"))
|
||||
return ""
|
||||
|
||||
def setValue(value, ret, name):
|
||||
ret[name] = value
|
||||
|
||||
def returnCategoryXml(prof_xml):
|
||||
ret = {}
|
||||
names_d_list = []
|
||||
for category_node in prof_xml.getElementsByTagName("category"):
|
||||
if category_node.getAttribute("name") == category:
|
||||
for param_node in category_node.getElementsByTagName("param"):
|
||||
name = param_node.getAttribute("name")
|
||||
if not name:
|
||||
log.warning(
|
||||
"ignoring attribute without name: {}".format(
|
||||
param_node.toxml()
|
||||
)
|
||||
if extra is None:
|
||||
extra = {}
|
||||
prof_xml = await self._constructProfileXml(client, security_limit, app, extra)
|
||||
ret = {}
|
||||
for category_node in prof_xml.getElementsByTagName("category"):
|
||||
if category_node.getAttribute("name") == category:
|
||||
for param_node in category_node.getElementsByTagName("param"):
|
||||
name = param_node.getAttribute("name")
|
||||
if not name:
|
||||
log.warning(
|
||||
"ignoring attribute without name: {}".format(
|
||||
param_node.toxml()
|
||||
)
|
||||
continue
|
||||
d = self.asyncGetStringParamA(
|
||||
name,
|
||||
category,
|
||||
security_limit=security_limit,
|
||||
profile_key=profile,
|
||||
)
|
||||
d.addCallback(setValue, ret, name)
|
||||
names_d_list.append(d)
|
||||
break
|
||||
continue
|
||||
value = await self.asyncGetStringParamA(
|
||||
name, category, security_limit=security_limit,
|
||||
profile=client.profile)
|
||||
|
||||
prof_xml.unlink()
|
||||
dlist = defer.gatherResults(names_d_list)
|
||||
dlist.addCallback(lambda __: ret)
|
||||
return ret
|
||||
ret[name] = value
|
||||
break
|
||||
|
||||
d = self._constructProfileXml(security_limit, "", profile)
|
||||
return d.addCallback(returnCategoryXml)
|
||||
prof_xml.unlink()
|
||||
return ret
|
||||
|
||||
def _getParam(
|
||||
self, category, name, type_=C.INDIVIDUAL, cache=None, profile=C.PROF_KEY_NONE
|
||||
|
|
@ -749,7 +744,7 @@ class Params(object):
|
|||
return None
|
||||
return cache[(category, name)]
|
||||
|
||||
def _constructProfileXml(self, security_limit, app, profile):
|
||||
async def _constructProfileXml(self, client, security_limit, app, extra):
|
||||
"""Construct xml for asked profile, filling values when needed
|
||||
|
||||
/!\ as noticed in doc, don't forget to unlink the minidom.Document
|
||||
|
|
@ -760,166 +755,163 @@ class Params(object):
|
|||
@param profile: profile name (not key !)
|
||||
@return: a deferred that fire a minidom.Document of the profile xml (cf warning above)
|
||||
"""
|
||||
profile = client.profile
|
||||
|
||||
def checkNode(node):
|
||||
"""Check the node against security_limit and app"""
|
||||
return self.checkSecurityLimit(node, security_limit) and self.checkApp(
|
||||
node, app
|
||||
)
|
||||
|
||||
def constructProfile(ignore, profile_cache):
|
||||
# init the result document
|
||||
prof_xml = minidom.parseString("<params/>")
|
||||
cache = {}
|
||||
|
||||
for type_node in self.dom.documentElement.childNodes:
|
||||
if type_node.nodeName != C.GENERAL and type_node.nodeName != C.INDIVIDUAL:
|
||||
continue
|
||||
# we use all params, general and individual
|
||||
for cat_node in type_node.childNodes:
|
||||
if cat_node.nodeName != "category":
|
||||
continue
|
||||
category = cat_node.getAttribute("name")
|
||||
dest_params = {} # result (merged) params for category
|
||||
if category not in cache:
|
||||
# we make a copy for the new xml
|
||||
cache[category] = dest_cat = cat_node.cloneNode(True)
|
||||
to_remove = []
|
||||
for node in dest_cat.childNodes:
|
||||
if node.nodeName != "param":
|
||||
continue
|
||||
if not checkNode(node):
|
||||
to_remove.append(node)
|
||||
continue
|
||||
dest_params[node.getAttribute("name")] = node
|
||||
for node in to_remove:
|
||||
dest_cat.removeChild(node)
|
||||
new_node = True
|
||||
else:
|
||||
# It's not a new node, we use the previously cloned one
|
||||
dest_cat = cache[category]
|
||||
new_node = False
|
||||
params = cat_node.getElementsByTagName("param")
|
||||
|
||||
for param_node in params:
|
||||
# we have to merge new params (we are parsing individual parameters, we have to add them
|
||||
# to the previously parsed general ones)
|
||||
name = param_node.getAttribute("name")
|
||||
if not checkNode(param_node):
|
||||
continue
|
||||
if name not in dest_params:
|
||||
# this is reached when a previous category exists
|
||||
dest_params[name] = param_node.cloneNode(True)
|
||||
dest_cat.appendChild(dest_params[name])
|
||||
|
||||
profile_value = self._getParam(
|
||||
category,
|
||||
name,
|
||||
type_node.nodeName,
|
||||
cache=profile_cache,
|
||||
profile=profile,
|
||||
)
|
||||
if profile_value is not None:
|
||||
# there is a value for this profile, we must change the default
|
||||
if dest_params[name].getAttribute("type") == "list":
|
||||
for option in dest_params[name].getElementsByTagName(
|
||||
"option"
|
||||
):
|
||||
if option.getAttribute("value") == profile_value:
|
||||
option.setAttribute("selected", "true")
|
||||
else:
|
||||
try:
|
||||
option.removeAttribute("selected")
|
||||
except NotFoundErr:
|
||||
pass
|
||||
elif dest_params[name].getAttribute("type") == "jids_list":
|
||||
jids = profile_value.split("\t")
|
||||
for jid_elt in dest_params[name].getElementsByTagName(
|
||||
"jid"
|
||||
):
|
||||
dest_params[name].removeChild(
|
||||
jid_elt
|
||||
) # remove all default
|
||||
for jid_ in jids: # rebuilt the children with use values
|
||||
try:
|
||||
jid.JID(jid_)
|
||||
except (
|
||||
RuntimeError,
|
||||
jid.InvalidFormat,
|
||||
AttributeError,
|
||||
):
|
||||
log.warning(
|
||||
"Incorrect jid value found in jids list: [{}]".format(
|
||||
jid_
|
||||
)
|
||||
)
|
||||
else:
|
||||
jid_elt = prof_xml.createElement("jid")
|
||||
jid_elt.appendChild(prof_xml.createTextNode(jid_))
|
||||
dest_params[name].appendChild(jid_elt)
|
||||
else:
|
||||
dest_params[name].setAttribute("value", profile_value)
|
||||
if new_node:
|
||||
prof_xml.documentElement.appendChild(dest_cat)
|
||||
|
||||
to_remove = []
|
||||
for cat_node in prof_xml.documentElement.childNodes:
|
||||
# we remove empty categories
|
||||
if cat_node.getElementsByTagName("param").length == 0:
|
||||
to_remove.append(cat_node)
|
||||
for node in to_remove:
|
||||
prof_xml.documentElement.removeChild(node)
|
||||
return prof_xml
|
||||
"""Check the node against security_limit, app and extra"""
|
||||
return (self.checkSecurityLimit(node, security_limit)
|
||||
and self.checkApp(node, app)
|
||||
and self.checkExtra(node, extra))
|
||||
|
||||
if profile in self.params:
|
||||
d = defer.succeed(None)
|
||||
profile_cache = self.params[profile]
|
||||
else:
|
||||
# profile is not in cache, we load values in a short time cache
|
||||
profile_cache = {}
|
||||
d = self.loadIndParams(profile, profile_cache)
|
||||
await self.loadIndParams(profile, profile_cache)
|
||||
|
||||
return d.addCallback(constructProfile, profile_cache)
|
||||
# init the result document
|
||||
prof_xml = minidom.parseString("<params/>")
|
||||
cache = {}
|
||||
|
||||
for type_node in self.dom.documentElement.childNodes:
|
||||
if type_node.nodeName != C.GENERAL and type_node.nodeName != C.INDIVIDUAL:
|
||||
continue
|
||||
# we use all params, general and individual
|
||||
for cat_node in type_node.childNodes:
|
||||
if cat_node.nodeName != "category":
|
||||
continue
|
||||
category = cat_node.getAttribute("name")
|
||||
dest_params = {} # result (merged) params for category
|
||||
if category not in cache:
|
||||
# we make a copy for the new xml
|
||||
cache[category] = dest_cat = cat_node.cloneNode(True)
|
||||
to_remove = []
|
||||
for node in dest_cat.childNodes:
|
||||
if node.nodeName != "param":
|
||||
continue
|
||||
if not checkNode(node):
|
||||
to_remove.append(node)
|
||||
continue
|
||||
dest_params[node.getAttribute("name")] = node
|
||||
for node in to_remove:
|
||||
dest_cat.removeChild(node)
|
||||
new_node = True
|
||||
else:
|
||||
# It's not a new node, we use the previously cloned one
|
||||
dest_cat = cache[category]
|
||||
new_node = False
|
||||
params = cat_node.getElementsByTagName("param")
|
||||
|
||||
for param_node in params:
|
||||
# we have to merge new params (we are parsing individual parameters, we have to add them
|
||||
# to the previously parsed general ones)
|
||||
name = param_node.getAttribute("name")
|
||||
if not checkNode(param_node):
|
||||
continue
|
||||
if name not in dest_params:
|
||||
# this is reached when a previous category exists
|
||||
dest_params[name] = param_node.cloneNode(True)
|
||||
dest_cat.appendChild(dest_params[name])
|
||||
|
||||
profile_value = self._getParam(
|
||||
category,
|
||||
name,
|
||||
type_node.nodeName,
|
||||
cache=profile_cache,
|
||||
profile=profile,
|
||||
)
|
||||
if profile_value is not None:
|
||||
# there is a value for this profile, we must change the default
|
||||
if dest_params[name].getAttribute("type") == "list":
|
||||
for option in dest_params[name].getElementsByTagName(
|
||||
"option"
|
||||
):
|
||||
if option.getAttribute("value") == profile_value:
|
||||
option.setAttribute("selected", "true")
|
||||
else:
|
||||
try:
|
||||
option.removeAttribute("selected")
|
||||
except NotFoundErr:
|
||||
pass
|
||||
elif dest_params[name].getAttribute("type") == "jids_list":
|
||||
jids = profile_value.split("\t")
|
||||
for jid_elt in dest_params[name].getElementsByTagName(
|
||||
"jid"
|
||||
):
|
||||
dest_params[name].removeChild(
|
||||
jid_elt
|
||||
) # remove all default
|
||||
for jid_ in jids: # rebuilt the children with use values
|
||||
try:
|
||||
jid.JID(jid_)
|
||||
except (
|
||||
RuntimeError,
|
||||
jid.InvalidFormat,
|
||||
AttributeError,
|
||||
):
|
||||
log.warning(
|
||||
"Incorrect jid value found in jids list: [{}]".format(
|
||||
jid_
|
||||
)
|
||||
)
|
||||
else:
|
||||
jid_elt = prof_xml.createElement("jid")
|
||||
jid_elt.appendChild(prof_xml.createTextNode(jid_))
|
||||
dest_params[name].appendChild(jid_elt)
|
||||
else:
|
||||
dest_params[name].setAttribute("value", profile_value)
|
||||
if new_node:
|
||||
prof_xml.documentElement.appendChild(dest_cat)
|
||||
|
||||
to_remove = []
|
||||
for cat_node in prof_xml.documentElement.childNodes:
|
||||
# we remove empty categories
|
||||
if cat_node.getElementsByTagName("param").length == 0:
|
||||
to_remove.append(cat_node)
|
||||
for node in to_remove:
|
||||
prof_xml.documentElement.removeChild(node)
|
||||
|
||||
return prof_xml
|
||||
|
||||
|
||||
def _getParamsUI(self, security_limit, app, extra_s, profile_key):
|
||||
client = self.host.getClient(profile_key)
|
||||
extra = data_format.deserialise(extra_s)
|
||||
return defer.ensureDeferred(self.getParamsUI(client, security_limit, app, extra))
|
||||
|
||||
async def getParamsUI(self, client, security_limit, app, extra=None):
|
||||
"""Get XMLUI to handle parameters
|
||||
|
||||
def getParamsUI(self, security_limit, app, profile_key):
|
||||
"""
|
||||
@param security_limit: NO_SECURITY_LIMIT (-1) to return all the params.
|
||||
Otherwise sole the params which have a security level defined *and*
|
||||
lower or equal to the specified value are returned.
|
||||
Otherwise sole the params which have a security level defined *and*
|
||||
lower or equal to the specified value are returned.
|
||||
@param app: name of the frontend requesting the parameters, or '' to get all parameters
|
||||
@param profile_key: Profile key which can be either a magic (eg: @DEFAULT@) or the name of an existing profile.
|
||||
@return: a SàT XMLUI for parameters
|
||||
@param extra (dict, None): extra options. Key can be:
|
||||
- ignore: list of (category/name) values to remove from parameters
|
||||
@return(str): a SàT XMLUI for parameters
|
||||
"""
|
||||
profile = self.getProfileName(profile_key)
|
||||
if not profile:
|
||||
log.error(_("Asking params for inexistant profile"))
|
||||
return ""
|
||||
d = self.getParams(security_limit, app, profile)
|
||||
return d.addCallback(lambda param_xml: paramsXML2XMLUI(param_xml))
|
||||
param_xml = await self.getParams(client, security_limit, app, extra)
|
||||
return paramsXML2XMLUI(param_xml)
|
||||
|
||||
def getParams(self, security_limit, app, profile_key):
|
||||
async def getParams(self, client, security_limit, app, extra=None):
|
||||
"""Construct xml for asked profile, take params xml as skeleton
|
||||
|
||||
@param security_limit: NO_SECURITY_LIMIT (-1) to return all the params.
|
||||
Otherwise sole the params which have a security level defined *and*
|
||||
lower or equal to the specified value are returned.
|
||||
Otherwise sole the params which have a security level defined *and*
|
||||
lower or equal to the specified value are returned.
|
||||
@param app: name of the frontend requesting the parameters, or '' to get all parameters
|
||||
@param extra (dict, None): extra options. Key can be:
|
||||
- ignore: list of (category/name) values to remove from parameters
|
||||
@param profile_key: Profile key which can be either a magic (eg: @DEFAULT@) or the name of an existing profile.
|
||||
@return: XML of parameters
|
||||
"""
|
||||
profile = self.getProfileName(profile_key)
|
||||
if not profile:
|
||||
log.error(_("Asking params for inexistant profile"))
|
||||
return defer.succeed("")
|
||||
|
||||
def returnXML(prof_xml):
|
||||
return_xml = prof_xml.toxml()
|
||||
prof_xml.unlink()
|
||||
return "\n".join((line for line in return_xml.split("\n") if line))
|
||||
|
||||
return self._constructProfileXml(security_limit, app, profile).addCallback(
|
||||
returnXML
|
||||
)
|
||||
if extra is None:
|
||||
extra = {}
|
||||
prof_xml = await self._constructProfileXml(client, security_limit, app, extra)
|
||||
return_xml = prof_xml.toxml()
|
||||
prof_xml.unlink()
|
||||
return "\n".join((line for line in return_xml.split("\n") if line))
|
||||
|
||||
def _getParamNode(self, name, category, type_="@ALL@"): # FIXME: is type_ useful ?
|
||||
"""Return a node from the param_xml
|
||||
|
|
@ -1128,6 +1120,7 @@ class Params(object):
|
|||
|
||||
def checkApp(self, node, app):
|
||||
"""Check the given node against the given app.
|
||||
|
||||
@param node: parameter node
|
||||
@param app: name of the frontend requesting the parameters, or '' to get all parameters
|
||||
@return: True if this node concerns the given app.
|
||||
|
|
@ -1136,6 +1129,24 @@ class Params(object):
|
|||
return True
|
||||
return node.getAttribute("app") == app
|
||||
|
||||
def checkExtra(self, node, extra):
|
||||
"""Check the given node against the extra filters.
|
||||
|
||||
@param node: parameter node
|
||||
@param app: name of the frontend requesting the parameters, or '' to get all parameters
|
||||
@return: True if node doesn't match category/name of extra['ignore'] list
|
||||
"""
|
||||
ignore_list = extra.get('ignore')
|
||||
if not ignore_list:
|
||||
return True
|
||||
category = node.parentNode.getAttribute('name')
|
||||
name = node.getAttribute('name')
|
||||
ignore = [category, name] in ignore_list
|
||||
if ignore:
|
||||
log.debug(f"Ignoring parameter {category}/{name} as requested")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def makeOptions(options, selected=None):
|
||||
"""Create option XML form dictionary
|
||||
|
|
|
|||
Loading…
Reference in a new issue