From 6a4ea0f9a4ecab0e1164151fe5588cbdd5cd1658 Mon Sep 17 00:00:00 2001 From: Jojo le bricolo <> Date: Sun, 19 Jan 2014 18:44:37 +0100 Subject: [PATCH] QWeb cleanups bzr revid: jojo_le_bricolo-20140119174437-kjrr9p1smpdxhd3u --- openerp/addons/base/ir/ir_qweb.py | 82 ++++++++++++++-------------- openerp/addons/base/ir/ir_ui_view.py | 73 +++++++++++++------------ 2 files changed, 79 insertions(+), 76 deletions(-) diff --git a/openerp/addons/base/ir/ir_qweb.py b/openerp/addons/base/ir/ir_qweb.py index aef88a1b375..77a3c8435d6 100644 --- a/openerp/addons/base/ir/ir_qweb.py +++ b/openerp/addons/base/ir/ir_qweb.py @@ -20,6 +20,9 @@ from openerp.tools.translate import _ _logger = logging.getLogger(__name__) +#-------------------------------------------------------------------- +# QWeb template engine +#-------------------------------------------------------------------- class QWebException(Exception): def __init__(self, message, template=None, node=None, attribute=None): @@ -28,7 +31,6 @@ class QWebException(Exception): self.node = node self.attribute = attribute - class QWebContext(dict): def __init__(self, cr, uid, data, loader=None, templates=None, context=None): self.cr = cr @@ -59,29 +61,30 @@ class QWebContext(dict): class QWeb(orm.AbstractModel): """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). + The templating engine use a very simple syntax based "magic" xml + attributes, to produce textual output (even non-xml). - QWebXml: - the template engine core implements the basic magic attributes: + The core magic attributes are: - t-att t-raw t-esc t-if t-foreach t-set t-call t-trim + flow attributes: + t-if t-foreach t-call + output attributes: + t-att t-raw t-esc t-trim - - loader: function that return a template + assignation attribute: + t-set - QWeb rendering can be used for many tasks. As a result, customizations - made by one task context (to either the main qweb rendering or to specific - fields rendering) could break other tasks. - - To avoid that, ``ir.qweb`` was consciously made inheritable and the "root" - of an object hierarchy. If you need extensions or alterations which could - be incompatible with other subsystems, you should create a local object - inheriting from ``ir.qweb`` and customize that. + QWeb can be extended like any OpenERP model and new attributes can be + added. If you need to customize t-fields rendering, subclass the ir.qweb.field model (and its sub-models) then override :meth:`~.get_converter_for` to fetch the right field converters for your qweb model. + + Beware that if you need extensions or alterations which could be + incompatible with other subsystems, you should create a local object + inheriting from ``ir.qweb`` and customize that. """ _name = 'ir.qweb' @@ -121,7 +124,7 @@ class QWeb(orm.AbstractModel): def register_tag(self, tag, func): self._render_tag[tag] = func - def load_document(self, x, into, context): + def load_document(self, x, qcontext): """ Loads an XML document and installs any contained template in the engine """ @@ -134,18 +137,20 @@ class QWeb(orm.AbstractModel): for n in dom.documentElement.childNodes: if n.nodeType == self.node.ELEMENT_NODE and n.getAttribute('t-name'): - self.add_template(into, str(n.getAttribute("t-name")), n, context) + name = str(n.getAttribute("t-name")) + + qcontext.templates[name] = n - def add_template(self, into, name, node, context): - into[name] = node + def get_template(self, name, qcontext): + if qcontext.loader and name not in qcontext.templates: + xml_doc = qcontext.loader(name) + self.load_document(xml_doc, qcontext=qcontext) - def get_template(self, name, context): - if context.loader and name not in context.templates: - xml_doc = context.loader(name) - self.load_document(xml_doc, into=context.templates, context=context) + if name in qcontext.templates: + return qcontext.templates[name] - if name in context.templates: - return context.templates[name] + import pdb + pdb.set_trace() e = KeyError('qweb: template "%s" not found' % name) setattr(e, 'qweb_template', name) @@ -191,32 +196,19 @@ class QWeb(orm.AbstractModel): else: return 0 - @openerp.tools.ormcache() - def get_template_xmlid(self, cr, uid, id): - imd = self.pool['ir.model.data'] - domain = [('model', '=', 'ir.ui.view'), ('res_id', '=', id)] - xmlid = imd.search_read(cr, uid, domain, ['module', 'name'])[0] - return '%s.%s' % (xmlid['module'], xmlid['name']) - def render(self, cr, uid, id_or_xml_id, v=None, loader=None, context=None): - if isinstance(id_or_xml_id, list): - id_or_xml_id = id_or_xml_id[0] - tname = id_or_xml_id - if isinstance(id_or_xml_id, (int, long)): - tname = self.get_template_xmlid(cr, uid, tname) - if v is None: v = {} if not isinstance(v, QWebContext): v = QWebContext(cr, uid, v, loader=loader, context=context) - v['__template__'] = tname + v['__template__'] = id_or_xml_id stack = v.get('__stack__', []) if stack: v['__caller__'] = stack[-1] - stack.append(tname) + stack.append(id_or_xml_id) v['__stack__'] = stack v['xmlid'] = str(stack[0]) # Temporary fix - return self.render_node(self.get_template(tname, v), v) + return self.render_node(self.get_template(id_or_xml_id, v), v) def render_node(self, e, v): r = "" @@ -314,10 +306,13 @@ class QWeb(orm.AbstractModel): def render_att_href(self, e, an, av, v): return self.url_for(e, an, av, v) + def render_att_src(self, e, an, av, v): return self.url_for(e, an, av, v) + def render_att_action(self, e, an, av, v): return self.url_for(e, an, av, v) + def url_for(self, e, an, av, v): if 'url_for' not in v: raise KeyError("qweb: no 'url_for' found in context") @@ -384,7 +379,7 @@ class QWeb(orm.AbstractModel): return "" def render_tag_call(self, e, t_att, g_att, v): - d = v if 'import' in t_att else v.copy() + d = v.copy() d[0] = self.render_element(e, t_att, g_att, d) return self.render(None, None, self.eval_format(t_att["call"], d), d) @@ -423,6 +418,9 @@ class QWeb(orm.AbstractModel): return self.pool.get('ir.qweb.field.' + field_type, self.pool['ir.qweb.field']) +#-------------------------------------------------------------------- +# QWeb Fields converters +#-------------------------------------------------------------------- class FieldConverter(osv.AbstractModel): """ Used to convert a t-field specification into an output HTML field. diff --git a/openerp/addons/base/ir/ir_ui_view.py b/openerp/addons/base/ir/ir_ui_view.py index 00a541858cf..806d108f4e6 100644 --- a/openerp/addons/base/ir/ir_ui_view.py +++ b/openerp/addons/base/ir/ir_ui_view.py @@ -40,6 +40,8 @@ from openerp.osv.orm import browse_record, browse_record_list _logger = logging.getLogger(__name__) +MOVABLE_BRANDING = ['data-oe-model', 'data-oe-id', 'data-oe-field', 'data-oe-xpath'] + class view_custom(osv.osv): _name = 'ir.ui.view.custom' _order = 'create_date desc' # search(limit=1) should return the last customization @@ -71,7 +73,6 @@ class view(osv.osv): 'type': fields.selection([ ('tree','Tree'), ('form','Form'), - ('mdx','mdx'), ('graph', 'Graph'), ('calendar', 'Calendar'), ('diagram','Diagram'), @@ -186,8 +187,15 @@ class view(osv.osv): self.pool.get('ir.model.data').write(cr, openerp.SUPERUSER_ID, view_.model_data_id.id, {'noupdate': True}) return ret - # default view selection + def copy(self, cr, uid, id, default=None, context=None): + if not default: + default = {} + default.update({ + 'model_ids': [], + }) + return super(view, self).copy(cr, uid, id, default, context=context) + # default view selection def default_view(self, cr, uid, model, view_type, context=None): """ Fetches the default view for the provided (model, view_type) pair: view with no parent (inherit_id=Fase) with the lowest priority. @@ -207,8 +215,9 @@ class view(osv.osv): return False return ids[0] - # inheritance - + #------------------------------------------------------ + # Inheritance mecanism + #------------------------------------------------------ def get_inheriting_views_arch(self, cr, uid, view_id, model, context=None): """Retrieves the architecture of views that inherit from the given view, from the sets of views that should currently be used in the system. During the module upgrade phase it @@ -422,8 +431,13 @@ class view(osv.osv): return dict(view, arch=etree.tostring(arch, encoding='utf-8')) - # postprocessing: groups, modifiers, ... - + #------------------------------------------------------ + # Postprocessing: translation, groups and modifiers + #------------------------------------------------------ + # TODO: + # - split postprocess so that it can be used instead of translate_qweb + # - remove group processing from ir_qweb + #------------------------------------------------------ def postprocess(self, cr, user, model, node, view_id, in_tree_view, model_fields, context=None): """Return the description of the fields in the node. @@ -685,26 +699,25 @@ class view(osv.osv): raise orm.except_orm('View error', msg) return arch, fields - # view used as templates - + #------------------------------------------------------ + # QWeb template views + #------------------------------------------------------ @tools.ormcache_context(accepted_keys=('lang','inherit_branding', 'editable', 'translatable')) - def read_template(self, cr, uid, id_, context=None): - try: - id_ = int(id_) - except ValueError: - if '.' not in id_: - raise ValueError('Invalid id: %r' % (id_,)) - IMD = self.pool['ir.model.data'] - m, _, n = id_.partition('.') - _, id_ = IMD.get_object_reference(cr, uid, m, n) + def read_template(self, cr, uid, xml_id, context=None): + if '.' not in xml_id: + raise ValueError('Invalid template id: %r' % (xml_id,)) - arch = self.read_combined(cr, uid, id_, fields=['arch'], context=context)['arch'] + m, n = xml_id.split('.', 1) + _, view_id = self.pool['ir.model.data'].get_object_reference(cr, uid, m, n) + + arch = self.read_combined(cr, uid, view_id, fields=['arch'], context=context)['arch'] arch_tree = etree.fromstring(arch) if 'lang' in context: - arch_tree = self.translate_qweb(cr, uid, id_, arch_tree, context['lang'], context) + arch_tree = self.translate_qweb(cr, uid, view_id, arch_tree, context['lang'], context) + self.distribute_branding(arch_tree) - root = etree.Element('tpl') + root = etree.Element('templates') root.append(arch_tree) arch = etree.tostring(root, encoding='utf-8', xml_declaration=True) return arch @@ -771,8 +784,6 @@ class view(osv.osv): text = h.unescape(text.strip()) if len(text) < 2 or (text.startswith('')): return None - # if text == 'Our Events': - # from pudb import set_trace;set_trace() ############################## Breakpoint ############################## return Translations._get_source(cr, uid, 'website', 'view', lang, text, id_) if arch.tag not in ['script']: @@ -791,16 +802,18 @@ class view(osv.osv): self.translate_qweb(cr, uid, id_, node, lang, context) return arch - def render(self, cr, uid, id_or_xml_id, values=None, engine='ir.qweb', context=None): + def render(self, cr, uid, xml_id, values=None, engine='ir.qweb', context=None): if not context: context = {} def loader(name): return self.read_template(cr, uid, name, context=context) - return self.pool[engine].render(cr, uid, id_or_xml_id, values, loader=loader, context=context) + return self.pool[engine].render(cr, uid, xml_id, values, loader=loader, context=context) - # maybe used to print the workflow ? + #------------------------------------------------------ + # Misc + #------------------------------------------------------ def graph_get(self, cr, uid, id, model, node_obj, conn_obj, src_node, des_node, label, scale, context=None): nodes=[] @@ -869,14 +882,6 @@ class view(osv.osv): 'blank_nodes': blank_nodes, 'node_parent_field': _Model_Field,} - def copy(self, cr, uid, id, default=None, context=None): - if not default: - default = {} - default.update({ - 'model_ids': [], - }) - return super(view, self).copy(cr, uid, id, default, context=context) - def _validate_custom_views(self, cr, uid, model): """Validate architecture of custom views (= without xml id) for a given model. This method is called at the end of registry update. @@ -892,4 +897,4 @@ class view(osv.osv): ids = map(itemgetter(0), cr.fetchall()) return self._check_xml(cr, uid, ids) -MOVABLE_BRANDING = ['data-oe-model', 'data-oe-id', 'data-oe-field', 'data-oe-xpath'] +# vim:et: