From a93bc8bdb01554caa7892d121f1e9ee21dc2d67b Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Thu, 27 Jun 2013 11:13:29 +0200 Subject: [PATCH] qweb templates bzr revid: al@openerp.com-20130627091329-t3hjytf3gt0dmkvo --- openerp/addons/base/ir/ir_ui_view.py | 14 +- openerp/tools/qweb.py | 241 +++++++++++++++++++++++++++ 2 files changed, 252 insertions(+), 3 deletions(-) create mode 100644 openerp/tools/qweb.py diff --git a/openerp/addons/base/ir/ir_ui_view.py b/openerp/addons/base/ir/ir_ui_view.py index d70bf4d02c8..0a8042bd941 100644 --- a/openerp/addons/base/ir/ir_ui_view.py +++ b/openerp/addons/base/ir/ir_ui_view.py @@ -19,13 +19,13 @@ # ############################################################################## import copy - -import logging import itertools -from lxml import etree +import logging import os import time +from lxml import etree + from openerp import tools from openerp.modules import module from openerp.osv import fields, osv, orm @@ -829,6 +829,13 @@ class view(osv.osv): raise orm.except_orm('View error', msg) return arch, fields + def render(self, cr, uid, id_or_xml_id, values, context=None): + def loader(name): + xml = self.read_combined(self, cr, uid, id_or_xml_id, context=context) + return xml['arch'] + engine = openerp.tools.qweb.QWebXml(loader) + return engine.render(id_or_xml_id, values) + class view_sc(osv.osv): _name = 'ir.ui.view_sc' _columns = { @@ -864,5 +871,6 @@ class view_sc(osv.osv): ('shortcut_unique', 'unique(res_id, resource, user_id)', 'Shortcut for this menu already exists!'), ] + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/tools/qweb.py b/openerp/tools/qweb.py new file mode 100644 index 00000000000..6d0eee8feba --- /dev/null +++ b/openerp/tools/qweb.py @@ -0,0 +1,241 @@ +import xml +import re + +class QWebEval(object): + def __init__(self,data): + self.data=data + def __getitem__(self,expr): + if self.data.has_key(expr): + return self.data[expr] + r=None + try: + r=eval(expr,self.data) + except NameError,e: + pass + except AttributeError,e: + pass + except Exception,e: + print "qweb: expression error '%s' "%expr,e + if self.data.has_key("__builtins__"): + del self.data["__builtins__"] + return r + def eval_object(self,expr): + return self[expr] + def eval_str(self,expr): + if expr=="0": + return self.data[0] + if isinstance(self[expr],unicode): + return self[expr].encode("utf8") + return str(self[expr]) + def eval_format(self,expr): + try: + return str(expr%self) + except: + return "qweb: format error '%s' "%expr +# if isinstance(r,unicode): +# return r.encode("utf8") + def eval_bool(self,expr): + if self.eval_object(expr): + return 1 + else: + return 0 + +class QWebXml(object): + """QWeb Xml templating engine + + The templating engine use a very simple syntax, "magic" xml attributes, to + produce any kind of texutal output (even non-xml). + + QWebXml: + the template engine core implements the basic magic attributes: + + t-att t-raw t-esc t-if t-foreach t-set t-call t-trim + + + - loader: function that return a template + + + """ + def __init__(self, loader): + self.loader = loader + self.node=xml.dom.Node + self._t={} + self._render_tag={} + + prefix='render_tag_' + for i in [j for j in dir(self) if j.startswith(prefix)]: + name=i[len(prefix):].replace('_','-') + self._render_tag[name]=getattr(self.__class__,i) + + self._render_att={} + prefix='render_att_' + for i in [j for j in dir(self) if j.startswith(prefix)]: + name=i[len(prefix):].replace('_','-') + self._render_att[name]=getattr(self.__class__,i) + + def register_tag(self,tag,func): + self._render_tag[tag]=func + def add_template(self,x): + if hasattr(x,'documentElement'): + dom=x + elif x.startswith("%s%s"%(name,g_att,pre,inner,name) + else: + return "<%s%s/>"%(name,g_att) + + # Attributes + def render_att_att(self,e,an,av,v): + if an.startswith("t-attf-"): + att,val=an[7:],self.eval_format(av,v) + elif an.startswith("t-att-"): + att,val=(an[6:],self.eval_str(av,v)) + else: + att,val=self.eval_object(av,v) + return ' %s="%s"'%(att,cgi.escape(val,1)) + + # Tags + def render_tag_raw(self,e,t_att,g_att,v): + return self.eval_str(t_att["raw"],v) + def render_tag_rawf(self,e,t_att,g_att,v): + return self.eval_format(t_att["rawf"],v) + def render_tag_esc(self,e,t_att,g_att,v): + return cgi.escape(self.eval_str(t_att["esc"],v)) + def render_tag_escf(self,e,t_att,g_att,v): + return cgi.escape(self.eval_format(t_att["escf"],v)) + def render_tag_foreach(self,e,t_att,g_att,v): + expr=t_att["foreach"] + enum=self.eval_object(expr,v) + if enum!=None: + var=t_att.get('as',expr).replace('.','_') + d=v.copy() + size=-1 + if isinstance(enum,types.ListType): + size=len(enum) + elif isinstance(enum,types.TupleType): + size=len(enum) + elif hasattr(enum,'count'): + size=enum.count() + d["%s_size"%var]=size + d["%s_all"%var]=enum + index=0 + ru=[] + for i in enum: + d["%s_value"%var]=i + d["%s_index"%var]=index + d["%s_first"%var]=index==0 + d["%s_even"%var]=index%2 + d["%s_odd"%var]=(index+1)%2 + d["%s_last"%var]=index+1==size + if index%2: + d["%s_parity"%var]='odd' + else: + d["%s_parity"%var]='even' + if isinstance(i,types.DictType): + d.update(i) + else: + d[var]=i + ru.append(self.render_element(e,g_att,d)) + index+=1 + return "".join(ru) + else: + return "qweb: t-foreach %s not found."%expr + def render_tag_if(self,e,t_att,g_att,v): + if self.eval_bool(t_att["if"],v): + return self.render_element(e,g_att,v) + else: + return "" + def render_tag_call(self,e,t_att,g_att,v): + # TODO t-prefix + if t_att.has_key("import"): + d=v + else: + d=v.copy() + d[0]=self.render_element(e,g_att,d) + return self.render(t_att["call"],d) + def render_tag_set(self,e,t_att,g_att,v): + if t_att.has_key("eval"): + v[t_att["set"]]=self.eval_object(t_att["eval"],v) + else: + v[t_att["set"]]=self.render_element(e,g_att,v) + return "" + +#