QWeb cleanups
bzr revid: jojo_le_bricolo-20140119174437-kjrr9p1smpdxhd3u
This commit is contained in:
parent
bd444b8a5e
commit
6a4ea0f9a4
|
@ -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.
|
||||
|
|
|
@ -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('<!') and text.endswith('>')):
|
||||
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:
|
||||
|
|
Loading…
Reference in New Issue