diff --git a/lib/nulib/python/nulib/web/api.py b/lib/nulib/python/nulib/web/api.py index 66fb2c5..8243fa4 100644 --- a/lib/nulib/python/nulib/web/api.py +++ b/lib/nulib/python/nulib/web/api.py @@ -2,7 +2,7 @@ __all__ = ( '_weburl', '_ensure_session', - 'u', 'uval', + 'u', 'uval', 'uval_would_escape', ) from types import UnicodeType, StringType @@ -28,11 +28,21 @@ def u(text): text = unicode(text, "utf-8") return text -def uval(value): - """Transformer value en unicode, en particulier s'il est callable ou s'il - a une méthode render() +def uval_would_escape(value): + """Tester si uval() mettrait value en échappement + """ + if value is None: return False + if hasattr(value, 'render'): return False + elif callable(value): return False + return True + +def uval(value, escape=True): + """Transformer value en unicode pour affichage. + Si value est un objet qui a une méthode render() ou qui est callable(), + alors utiliser le résultat comme valeur sinon utiliser web.websafe() """ if value is None: return None if hasattr(value, 'render'): value = value.render() - if callable(value): value = value() + elif callable(value): value = value() + elif escape: value = web.websafe(value) return u(value) diff --git a/lib/nulib/python/nulib/web/bootstrap.py b/lib/nulib/python/nulib/web/bootstrap.py index 2b4b180..e191f87 100644 --- a/lib/nulib/python/nulib/web/bootstrap.py +++ b/lib/nulib/python/nulib/web/bootstrap.py @@ -3,7 +3,7 @@ __all__ = ( 'load', 'table', 'tr', 'td', - 'Menu', 'Action', + 'Menu', 'Action', 'litteral', 'p', 'About', 'Alert', 'set_alert', ) @@ -15,33 +15,31 @@ from ..base import isseq, seqof, Undef from ..ext import web from . import ui -from .api import _ensure_session, u, uval +from .api import _ensure_session, u, uval, uval_would_escape from .model import Row, TableSchema -from .ui import Action, p - -Undef = Undef() # spécifique à ce module +from .ui import Action, litteral, p def load(prefix=None, icons=True, charset="utf-8"): if prefix is None: prefix = "/static/bootstrap/" elif not prefix.endswith("/"): prefix = prefix + "/" lines = [ - '' % charset, - '', - '', + u"""""" % charset, + u"""""", + u"""""", ] if icons: lines.extend([ - '' % prefix, - '' % prefix, + u"""""" % prefix, + u"""""" % prefix, ]) lines.extend([ - ui.css("%sbootstrap/css/bootstrap.min.css" % prefix), - ui.css("%sbslocal.css" % prefix), - ui.js("%sjquery.min.js" % prefix), - ui.js("%sbootstrap/js/bootstrap.min.js" % prefix), + ui.css(u"%sbootstrap/css/bootstrap.min.css" % prefix), + ui.css(u"%sbslocal.css" % prefix), + ui.js(u"%sjquery.min.js" % prefix), + ui.js(u"%sbootstrap/js/bootstrap.min.js" % prefix), u"", ]) return u"\n".join(lines) @@ -63,7 +61,7 @@ def trd_joinc(css): classes = [] for css in seqof(css): classes.append(TRD_CLASS_MAP.get(css, css)) - css = ' '.join(classes) + css = " ".join(classes) return css def td(value, schema=None, css=Undef, tag=Undef, **kw): @@ -72,9 +70,9 @@ def td(value, schema=None, css=Undef, tag=Undef, **kw): if schema: value = schema.html(value) if css is Undef: css = schema.css(value) - if tag is Undef: tag = 'td' + if tag is Undef: tag = "td" css = trd_joinc(css) - return u'<%s%s>%s%s>' % (tag, ui.classif(css), value, tag) + return u"<%s%s>%s%s>" % (tag, ui.classif(css), value, tag) def tr(values, schema=None, css=Undef, celltag=Undef, **kw): css = Undef.sa(css, kw, 'c') @@ -84,7 +82,7 @@ def tr(values, schema=None, css=Undef, celltag=Undef, **kw): tdschema = lambda value, index: schema.cell_schema(value, index) css = trd_joinc(css) lines = [] - lines.append(u'
et
+ text doit avoir le cas échéant déjà été traité avec uval """ text = u(text) if text[0:1] == '<': return text @@ -71,7 +81,8 @@ class Action(odict): """Un lien ou une action d'un formulaire url: cible de l'action ou du lien (avec querystring) - title: titre du lien ou de l'élément de menu + text: titre du lien ou de l'élément de menu + title: description de l'action. vaut text par défaut id: identifiant dans un menu in_profiles (p): profils dans lequel l'action est valide. le premier profil est le profil par défaut @@ -112,28 +123,39 @@ class Action(odict): self[querykey] = query self[urlkey] = url - def __init__(self, url, title=None, id=None, in_profiles=None, css=None, accesskey=None, query=None, method=None, **kw): + def __init__(self, url, text=Undef, id=Undef, in_profiles=Undef, css=Undef, accesskey=Undef, query=Undef, title=Undef, method=Undef, **kw): + text = Undef.sa(text, kw, 't', None) + id = Undef.sa(id, kw, None, None) + in_profiles = Undef.sa(in_profiles, kw, 'p', None) + css = Undef.sa(css, kw, 'c', None) + accesskey = Undef.sa(accesskey, kw, 'ak', None) + query = Undef.sa(query, kw, 'q', None) + title = Undef.sa(title, kw, 'nt') + method = Undef.sa(method, kw, 'm', None) super(Action, self).__init__(**kw) if isseq(url): - if url[1:2]: title = url[1] + if url[1:2]: text = url[1] if url[2:3]: id = url[2] if url[3:4]: in_profiles = url[3] if url[4:5]: css = url[4] if url[5:6]: accesskey = url[5] - if url[6:7]: method = url[6] + if url[6:7]: title = url[6] + if url[7:8]: method = url[7] url = url[0] if url[0:1] else None if url is None: raise ValueError("url is required") - if title is None: title = url + if text is None: text = url if in_profiles is None: in_profiles = kw.pop('p', None) if css is None: css = kw.pop('c', None) if accesskey is None: accesskey = kw.pop('ak', None) if query is None: query = kw.pop('q', None) + if title is Undef: title = text if method is None: method = 'get' - self.title = title + self.text = text self.id = id self.css = css self.accesskey = accesskey + self.title = title self.method = method self.url = url @@ -207,26 +229,30 @@ class Menu(odict): self.nextid += 1 return nextid - def __init__(self, title, mitems=None, profiles=None, - id=None, in_profiles=None, css=None, **kw): - if in_profiles is None: in_profiles = kw.pop('p', None) - if css is None: css = kw.pop('c', None) + def __init__(self, text=None, mitems=None, profiles=None, + id=None, in_profiles=Undef, css=Undef, title=Undef, **kw): + in_profiles = Undef.sa(in_profiles, kw, 'p', None) + css = Undef.sa(css, kw, 'c', None) + title = Undef.sa(title, kw, 'nt') super(Menu, self).__init__(**kw) self.__dict__['nextid'] = 0 self.__dict__['idmap'] = {} - if title is None: title = u"" + + if text is None: text = u"" if id is None: id = self.__nextid() - self.title = title - self.mitems = [] + if title is Undef: title = text + self.text = text profiles = seqof(profiles, None) self.profiles = profiles self.default_profile = profiles[0] if profiles is not None else None self.id = str(id) self.in_profiles = seqof(p, None) self.css = css + self.title = title self.sel_id = None self.sel_profile = None self.active = False + self.mitems = [] for mitem in seqof(mitems): self.add(mitem) @@ -252,8 +278,8 @@ class Menu(odict): else: mitem.active = False - def select(self, id, profile=None, **kw): - if profile is None: profile = kw.pop('p', None) + def select(self, id, profile=Undef, **kw): + profile = Undef.sa(profile, kw, 'p', None) # d'abord déselectionner tout le monde self.reset_selection() # ensuite chercher le mitem à sélectionner @@ -277,10 +303,10 @@ class Menu(odict): return True return False - def get_mitem(self, id=None, **kw): + def get_mitem(self, id=Undef, **kw): """retourner l'élément de menu correspondant à la sélection courante """ - if id is None: id = self.sel_id + id = Undef.sa(id, kw, None, self.sel_id) if id is None: return None id = str(id) mitem = self.idmap.get(id, None)