Merge pull request #881 from xmo-odoo/master-qweb-lxml-xmo

Convert qweb (Python) to use lxml
This commit is contained in:
xmo-odoo
2014-07-02 19:28:24 +02:00
5 changed files with 85 additions and 103 deletions
+7 -7
View File
@@ -48,17 +48,17 @@ class QWeb(orm.AbstractModel):
def add_template(self, qcontext, name, node):
# preprocessing for multilang static urls
if request.website:
for tag, attr in self.URL_ATTRS.items():
for e in node.getElementsByTagName(tag):
url = e.getAttribute(attr)
for tag, attr in self.URL_ATTRS.iteritems():
for e in node.iterdescendants(tag=tag):
url = e.get(attr)
if url:
e.setAttribute(attr, qcontext.get('url_for')(url))
e.set(attr, qcontext.get('url_for')(url))
super(QWeb, self).add_template(qcontext, name, node)
def render_att_att(self, element, attribute_name, attribute_value, qwebcontext):
att, val = super(QWeb, self).render_att_att(element, attribute_name, attribute_value, qwebcontext)
if request.website and att == self.URL_ATTRS.get(element.nodeName) and isinstance(val, basestring):
if request.website and att == self.URL_ATTRS.get(element.tag) and isinstance(val, basestring):
val = qwebcontext.get('url_for')(val)
return att, val
@@ -78,7 +78,7 @@ class Field(orm.AbstractModel):
attrs = [('data-oe-translate', 1 if column.translate else 0)]
placeholder = options.get('placeholder') \
or source_element.getAttribute('placeholder') \
or source_element.get('placeholder') \
or getattr(column, 'placeholder', None)
if placeholder:
attrs.append(('placeholder', placeholder))
@@ -275,7 +275,7 @@ class Image(orm.AbstractModel):
def to_html(self, cr, uid, field_name, record, options,
source_element, t_att, g_att, qweb_context, context=None):
assert source_element.nodeName != 'img',\
assert source_element.tag != 'img',\
"Oddly enough, the root tag of an image field can not be img. " \
"That is because the image goes into the tag, or it gets the " \
"hose again."
+5 -9
View File
@@ -1,9 +1,8 @@
# -*- coding: utf-8 -*-
import textwrap
import unittest2
from xml.dom.minidom import getDOMImplementation
from lxml import html
from lxml import etree, html
from lxml.builder import E
from openerp.tests import common
@@ -11,9 +10,6 @@ from openerp.addons.base.ir import ir_qweb
from openerp.addons.website.models.ir_qweb import html_to_text
from openerp.addons.website.models.website import slugify, unslug
impl = getDOMImplementation()
document = impl.createDocument(None, None, None)
class TestUnslug(unittest2.TestCase):
def test_unslug(self):
tests = {
@@ -150,9 +146,9 @@ class TestConvertBack(common.TransactionCase):
})
[record] = Model.browse(self.cr, self.uid, [id])
e = document.createElement('span')
e = etree.Element('span')
field_value = 'record.%s' % field
e.setAttribute('t-field', field_value)
e.set('t-field', field_value)
rendered = self.registry('website.qweb').render_tag_field(
e, {'field': field_value}, '', ir_qweb.QWebContext(self.cr, self.uid, {
@@ -231,9 +227,9 @@ class TestConvertBack(common.TransactionCase):
id = Model.create(self.cr, self.uid, {field: sub_id})
[record] = Model.browse(self.cr, self.uid, [id])
e = document.createElement('span')
e = etree.Element('span')
field_value = 'record.%s' % field
e.setAttribute('t-field', field_value)
e.set('t-field', field_value)
rendered = self.registry('website.qweb').render_tag_field(
e, {'field': field_value}, '', ir_qweb.QWebContext(self.cr, self.uid, {
+64 -70
View File
@@ -4,6 +4,7 @@ import cStringIO
import datetime
import hashlib
import json
import itertools
import logging
import math
import os
@@ -11,15 +12,13 @@ import re
import sys
import textwrap
import uuid
import xml # FIXME use lxml and etree
import itertools
import lxml.html
import werkzeug
from subprocess import Popen, PIPE
from urlparse import urlparse
import babel
import babel.dates
import werkzeug
from lxml import etree, html
from PIL import Image
import openerp.http
@@ -116,7 +115,6 @@ class QWeb(orm.AbstractModel):
_name = 'ir.qweb'
node = xml.dom.Node
_void_elements = frozenset([
'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen',
'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'])
@@ -164,18 +162,17 @@ class QWeb(orm.AbstractModel):
if hasattr(document, 'documentElement'):
dom = document
elif document.startswith("<?xml"):
dom = xml.dom.minidom.parseString(document)
dom = etree.fromstring(document)
else:
dom = xml.dom.minidom.parse(document)
dom = etree.parse(document)
for node in dom.documentElement.childNodes:
if node.nodeType == self.node.ELEMENT_NODE:
if node.getAttribute('t-name'):
name = str(node.getAttribute("t-name"))
self.add_template(qwebcontext, name, node)
if res_id and node.tagName == "t":
self.add_template(qwebcontext, res_id, node)
res_id = None
for node in dom:
if node.get('t-name'):
name = str(node.get("t-name"))
self.add_template(qwebcontext, name, node)
if res_id and node.tag == "t":
self.add_template(qwebcontext, res_id, node)
res_id = None
def get_template(self, name, qwebcontext):
origin_template = qwebcontext.get('__caller__') or qwebcontext['__stack__'][0]
@@ -246,53 +243,50 @@ class QWeb(orm.AbstractModel):
return self.render_node(self.get_template(id_or_xml_id, qwebcontext), qwebcontext)
def render_node(self, element, qwebcontext):
result = ""
if element.nodeType == self.node.TEXT_NODE or element.nodeType == self.node.CDATA_SECTION_NODE:
result = element.data.encode("utf8")
elif element.nodeType == self.node.ELEMENT_NODE:
generated_attributes = ""
t_render = None
template_attributes = {}
for (attribute_name, attribute_value) in element.attributes.items():
attribute_name = str(attribute_name)
if attribute_name == "groups":
cr = qwebcontext.get('request') and qwebcontext['request'].cr or None
uid = qwebcontext.get('request') and qwebcontext['request'].uid or None
can_see = self.user_has_groups(cr, uid, groups=attribute_value) if cr and uid else False
if not can_see:
if qwebcontext.get('editable') and not qwebcontext.get('editable_no_editor'):
errmsg = _("Editor disabled because some content can not be seen by a user who does not belong to the groups %s")
raise openerp.http.Retry(
_("User does not belong to groups %s") % attribute_value, {
'editable_no_editor': errmsg % attribute_value
})
return ''
generated_attributes = ""
t_render = None
template_attributes = {}
for (attribute_name, attribute_value) in element.attrib.iteritems():
attribute_name = str(attribute_name)
if attribute_name == "groups":
cr = qwebcontext.get('request') and qwebcontext['request'].cr or None
uid = qwebcontext.get('request') and qwebcontext['request'].uid or None
can_see = self.user_has_groups(cr, uid, groups=attribute_value) if cr and uid else False
if not can_see:
if qwebcontext.get('editable') and not qwebcontext.get('editable_no_editor'):
errmsg = _("Editor disabled because some content can not be seen by a user who does not belong to the groups %s")
raise openerp.http.Retry(
_("User does not belong to groups %s") % attribute_value, {
'editable_no_editor': errmsg % attribute_value
})
return ''
if isinstance(attribute_value, unicode):
attribute_value = attribute_value.encode("utf8")
attribute_value = attribute_value.encode("utf8")
if attribute_name.startswith("t-"):
for attribute in self._render_att:
if attribute_name[2:].startswith(attribute):
att, val = self._render_att[attribute](self, element, attribute_name, attribute_value, qwebcontext)
generated_attributes += val and ' %s="%s"' % (att, escape(val)) or " "
break
else:
attribute_value = attribute_value.nodeValue.encode("utf8")
if attribute_name.startswith("t-"):
for attribute in self._render_att:
if attribute_name[2:].startswith(attribute):
att, val = self._render_att[attribute](self, element, attribute_name, attribute_value, qwebcontext)
generated_attributes += val and ' %s="%s"' % (att, escape(val)) or " "
break
else:
if attribute_name[2:] in self._render_tag:
t_render = attribute_name[2:]
template_attributes[attribute_name[2:]] = attribute_value
else:
generated_attributes += ' %s="%s"' % (attribute_name, escape(attribute_value))
if 'debug' in template_attributes:
debugger = template_attributes.get('debug', 'pdb')
__import__(debugger).set_trace() # pdb, ipdb, pudb, ...
if t_render:
result = self._render_tag[t_render](self, element, template_attributes, generated_attributes, qwebcontext)
if attribute_name[2:] in self._render_tag:
t_render = attribute_name[2:]
template_attributes[attribute_name[2:]] = attribute_value
else:
result = self.render_element(element, template_attributes, generated_attributes, qwebcontext)
generated_attributes += ' %s="%s"' % (attribute_name, escape(attribute_value))
if 'debug' in template_attributes:
debugger = template_attributes.get('debug', 'pdb')
__import__(debugger).set_trace() # pdb, ipdb, pudb, ...
if t_render:
result = self._render_tag[t_render](self, element, template_attributes, generated_attributes, qwebcontext)
else:
result = self.render_element(element, template_attributes, generated_attributes, qwebcontext)
if element.tail:
result += element.tail
if isinstance(result, unicode):
return result.encode('utf-8')
return result
@@ -306,16 +300,16 @@ class QWeb(orm.AbstractModel):
if inner:
g_inner = inner
else:
g_inner = []
for current_node in element.childNodes:
g_inner = [] if element.text is None else [element.text]
for current_node in element.iterchildren(tag=etree.Element):
try:
g_inner.append(self.render_node(current_node, qwebcontext))
except (QWebException, openerp.http.Retry):
raise
except Exception:
template = qwebcontext.get('__template__')
raise_qweb_exception(message="Could not render element %r" % element.nodeName, node=element, template=template)
name = str(element.nodeName)
raise_qweb_exception(message="Could not render element %r" % element.tag, node=element, template=template)
name = str(element.tag)
inner = "".join(g_inner)
trim = template_attributes.get("trim", 0)
if trim == 0:
@@ -417,7 +411,7 @@ class QWeb(orm.AbstractModel):
def render_tag_call_assets(self, element, template_attributes, generated_attributes, qwebcontext):
""" This special 't-call' tag can be used in order to aggregate/minify javascript and css assets"""
if element.childNodes:
if len(element):
# An asset bundle is rendered in two differents contexts (when genereting html and
# when generating the bundle itself) so they must be qwebcontext free
# even '0' variable is forbidden
@@ -441,7 +435,7 @@ class QWeb(orm.AbstractModel):
def render_tag_field(self, element, template_attributes, generated_attributes, qwebcontext):
""" eg: <span t-record="browse_record(res.partner, 1)" t-field="phone">+1 555 555 8069</span>"""
node_name = element.nodeName
node_name = element.tag
assert node_name not in ("table", "tbody", "thead", "tfoot", "tr", "td",
"li", "ul", "ol", "dl", "dt", "dd"),\
"RTE widgets do not work correctly on %r elements" % node_name
@@ -1039,11 +1033,11 @@ class AssetsBundle(object):
self.parse()
def parse(self):
fragments = lxml.html.fragments_fromstring(self.html)
fragments = html.fragments_fromstring(self.html)
for el in fragments:
if isinstance(el, basestring):
self.remains.append(el)
elif isinstance(el, lxml.html.HtmlElement):
elif isinstance(el, html.HtmlElement):
src = el.get('src', '')
href = el.get('href', '')
atype = el.get('type')
@@ -1063,10 +1057,10 @@ class AssetsBundle(object):
elif el.tag == 'script' and self.can_aggregate(src):
self.javascripts.append(JavascriptAsset(self, url=src))
else:
self.remains.append(lxml.html.tostring(el))
self.remains.append(html.tostring(el))
else:
try:
self.remains.append(lxml.html.tostring(el))
self.remains.append(html.tostring(el))
except Exception:
# notYETimplementederror
raise NotImplementedError
@@ -1256,7 +1250,7 @@ class WebAsset(object):
except Exception:
raise AssetNotFound("Could not find %s" % self.name)
def to_html():
def to_html(self):
raise NotImplementedError()
@lazy_property
+6 -12
View File
@@ -1,13 +1,11 @@
# -*- coding: utf-8 -*-
import cgi
from xml.dom import minidom as dom
from lxml import etree
from openerp.tests import common
from openerp.addons.base.ir import ir_qweb
impl = dom.getDOMImplementation()
document = impl.createDocument(None, None, None)
class TestQWebTField(common.TransactionCase):
def setUp(self):
super(TestQWebTField, self).setUp()
@@ -18,8 +16,7 @@ class TestQWebTField(common.TransactionCase):
self.cr, self.uid, values, context={'inherit_branding': True})
def test_trivial(self):
field = document.createElement('span')
field.setAttribute('t-field', u'company.name')
field = etree.Element('span', {'t-field': u'company.name'})
Companies = self.registry('res.company')
company_id = Companies.create(self.cr, self.uid, {
@@ -38,8 +35,7 @@ class TestQWebTField(common.TransactionCase):
"My Test Company",))
def test_i18n(self):
field = document.createElement('span')
field.setAttribute('t-field', u'company.name')
field = etree.Element('span', {'t-field': u'company.name'})
Companies = self.registry('res.company')
s = u"Testing «ταБЬℓσ»: 1<2 & 4+1>3, now 20% off!"
@@ -59,8 +55,7 @@ class TestQWebTField(common.TransactionCase):
cgi.escape(s.encode('utf-8')),))
def test_reject_crummy_tags(self):
field = document.createElement('td')
field.setAttribute('t-field', u'company.name')
field = etree.Element('td', {'t-field': u'company.name'})
with self.assertRaisesRegexp(
AssertionError,
@@ -70,8 +65,7 @@ class TestQWebTField(common.TransactionCase):
}))
def test_reject_t_tag(self):
field = document.createElement('t')
field.setAttribute('t-field', u'company.name')
field = etree.Element('t', {'t-field': u'company.name'})
with self.assertRaisesRegexp(
AssertionError,
@@ -1,18 +1,16 @@
# -*- encoding: utf-8 -*-
import json
import os
import xml.dom.minidom
import datetime
from lxml import etree
from openerp.tests import common
from openerp.tools import html_escape as e
from openerp.addons.base.ir import ir_qweb
directory = os.path.dirname(__file__)
impl = xml.dom.minidom.getDOMImplementation()
doc = impl.createDocument(None, None, None)
class TestExport(common.TransactionCase):
_model = None
@@ -114,7 +112,7 @@ class TestCurrencyExport(TestExport):
context = dict(inherit_branding=True)
converted = converter.to_html(
self.cr, self.uid, 'value', obj, options,
doc.createElement('span'),
etree.Element('span'),
{'field': 'obj.value', 'field-options': json.dumps(options)},
'', ir_qweb.QWebContext(self.cr, self.uid, {'obj': obj, 'c2': dest, }),
context=context,