[IMP] qweb: add t-options on t-esc and t-raw who use 'ir.qweb.field' models

Remove 'field' from 'value_to_html' method attributes
't-esc' escape the result of the widget.
(Added 'remove_span' as option for monetary widget)

E.g.: <t t-esc="32" t-options='t-options='{"widget": "monetary", "display_currency": website.currency_id, "remove_span": True}'>
result: $ 365.00
This commit is contained in:
Christophe Matthieu
2016-07-05 18:43:09 +02:00
parent 7e1f2eb91e
commit 3407fb70e4
6 changed files with 284 additions and 179 deletions
@@ -7,11 +7,14 @@ class TestFloatExport(common.TransactionCase):
self.Model = self.registry('decimal.precision.test')
def get_converter(self, name):
converter = self.registry('ir.qweb.field.float')
field = self.Model._fields[name]
float_obj = self.registry('ir.qweb.field.float')
_, precision = self.Model._fields[name].digits or (None, None)
return lambda value, options=None: converter.value_to_html(
self.cr, self.uid, value, field, options=options, context=None)
def converter(value, options=None):
record = self.Model.new(self.cr, self.uid, {name: value}, context=None)
return float_obj.record_to_html(
self.cr, self.uid, record, name, options or {}, context=None)
return converter
def test_basic_float(self):
converter = self.get_converter('float')
@@ -30,12 +33,12 @@ class TestFloatExport(common.TransactionCase):
converter(42.12345),
"42.12")
converter = self.get_converter('float_4')
converter = self.get_converter('float') # don't use float_4 because the field value 42.12345 is orm converted to 42.1235
self.assertEqual(
converter(42.0),
converter(42.0, {'precision': 4}),
'42.0000')
self.assertEqual(
converter(42.12345),
converter(42.12345, {'precision': 4}),
'42.1234')
def test_precision_domain(self):
@@ -57,7 +60,7 @@ class TestFloatExport(common.TransactionCase):
converter(42.0, {'decimal_precision': 'B'}),
'42.000000')
converter = self.get_converter('float_4')
converter = self.get_converter('float') # don't use float_4 because the field value 42.12345 is orm converted to 42.1235
self.assertEqual(
converter(42.12345, {'decimal_precision': 'A'}),
'42.12')
+1 -1
View File
@@ -12,7 +12,7 @@ class BarcodeConverter(osv.AbstractModel):
_name = 'ir.qweb.field.barcode'
_inherit = 'ir.qweb.field'
def value_to_html(self, cr, uid, value, field, options=None, context=None):
def value_to_html(self, cr, uid, value, options, context=None):
options = options or {}
barcode_type = options.get('type', 'Code128')
barcode = self.pool['report'].barcode(
+75 -73
View File
@@ -82,7 +82,7 @@ class FieldConverter(models.AbstractModel):
return data
@api.model
def value_to_html(self, value, field, options=None):
def value_to_html(self, value, options):
""" value_to_html(value, field, options=None)
Converts a single value to its HTML version/output
@@ -97,15 +97,8 @@ class FieldConverter(models.AbstractModel):
Converts the specified field of the browse_record ``record`` to HTML
:rtype: unicode
"""
field = record._fields[field_name]
try:
value = record[field_name]
return False if value is False else record.env[self._name].value_to_html(value, field, options=options)
except Exception:
_logger.warning("Could not get field %s for model %s",
field_name, record._name, exc_info=True)
return None
value = record[field_name]
return False if value is False else record.env[self._name].value_to_html(value, options=options)
@api.model
def user_lang(self):
@@ -126,7 +119,7 @@ class IntegerConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
def value_to_html(self, value, field, options=None):
def value_to_html(self, value, options):
return unicodifier(self.user_lang().format('%d', value, grouping=True))
@@ -135,15 +128,15 @@ class FloatConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
def precision(self, field, options=None):
_, precision = field.digits or (None, None)
return precision
def value_to_html(self, value, options):
if 'decimal_precision' in options:
precision = self.env['decimal.precision'].search([('name', '=', options['decimal_precision'])]).digits
else:
precision = options['precision']
@api.model
def value_to_html(self, value, field, options=None):
precision = self.precision(field, options=options)
fmt = '%f' if precision is None else '%.{precision}f'
lang = self.user_lang()
formatted = lang.format(fmt.format(precision=precision), value, grouping=True)
# %f does not strip trailing zeroes. %g does but its precision causes
@@ -152,15 +145,23 @@ class FloatConverter(models.AbstractModel):
# strip trailing 0.
if precision is None:
formatted = re.sub(r'(?:(0|\d+?)0+)$', r'\1', formatted)
return unicodifier(formatted)
@api.model
def record_to_html(self, record, field_name, options, values=None):
if 'precision' not in options and 'decimal_precision' not in options:
_, precision = record._fields[field_name].digits or (None, None)
options = dict(options, precision=precision)
return super(FloatConverter, self).record_to_html(record, field_name, options, values=values)
class DateConverter(models.AbstractModel):
_name = 'ir.qweb.field.date'
_inherit = 'ir.qweb.field'
@api.model
def value_to_html(self, value, field, options=None):
def value_to_html(self, value, options):
if not value or len(value) < 10:
return ''
lang = self.user_lang()
@@ -183,7 +184,7 @@ class DateTimeConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
def value_to_html(self, value, field, options=None):
def value_to_html(self, value, options):
if not value:
return ''
lang = self.user_lang()
@@ -211,7 +212,7 @@ class TextConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
def value_to_html(self, value, field, options=None):
def value_to_html(self, value, options):
"""
Escapes the value and converts newlines to br. This is bullshit.
"""
@@ -223,11 +224,16 @@ class SelectionConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
def value_to_html(self, value, field, options=None):
def value_to_html(self, value, options):
if not value:
return ''
selection = dict(field.get_description(self.env)['selection'])
return html_escape(unicodifier(selection[value]) or u'', options)
return html_escape(unicodifier(options['selection'][value]) or u'', options)
@api.model
def record_to_html(self, record, field_name, options, values=None):
if 'selection' not in options:
options = dict(options, selection=dict(record._fields[field_name].get_description(self.env)['selection']))
return super(SelectionConverter, self).record_to_html(record, field_name, options, values=values)
class ManyToOneConverter(models.AbstractModel):
@@ -235,8 +241,7 @@ class ManyToOneConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
def record_to_html(self, record, field_name, options, values=None):
value = record[field_name]
def value_to_html(self, value, options):
if not value:
return False
value = value.sudo().display_name
@@ -250,7 +255,7 @@ class HTMLConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
def value_to_html(self, value, field, options=None):
def value_to_html(self, value, options):
return unicodifier(value) or u''
@@ -267,7 +272,7 @@ class ImageConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
def value_to_html(self, value, field, options=None):
def value_to_html(self, value, options):
try:
image = Image.open(StringIO(value.decode('base64')))
image.verify()
@@ -297,27 +302,9 @@ class MonetaryConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
def record_to_html(self, record, field_name, options, values=None):
# for backward compatibility to remove after v10
if isinstance(options.get('display_currency'), str):
_logger.warning("Use new syntax for '%s' monetary widget 'display_currency' option (instead of deprecated JSON syntax) to improve speed." % options['display_currency'])
options['display_currency'] = safe_eval.safe_eval(options['display_currency'], mode='eval', globals_dict=values)
if isinstance(options.get('from_currency'), str):
_logger.warning("Use new syntax for '%s' monetary widget 'from_currency' option (instead of deprecated JSON syntax) to improve speed." % options['from_currency'])
options['from_currency'] = safe_eval.safe_eval(options['from_currency'], mode='eval', globals_dict=values)
# end backward
def value_to_html(self, value, options):
display_currency = options['display_currency']
field = record._fields[field_name]
from_amount = record[field_name]
#currency should be specified by monetary field
if not options.get('display_currency') and field.type == 'monetary' and field.currency_field:
options['display_currency'] = record[field.currency_field]
pre, value, post = self._format_func_monetary(from_amount, options['display_currency'], options.get('from_currency'))
return u'{pre}<span class="oe_currency_value">{0}</span>{post}'.format(value, pre=pre, post=post)
def _format_func_monetary(self, from_amount, display_currency, from_currency=None):
# lang.format mandates a sprintf-style format. These formats are non-
# minimal (they have a default fixed precision instead), and
# lang.format will not set one by default. currency.round will not
@@ -325,11 +312,11 @@ class MonetaryConverter(models.AbstractModel):
# (integer > 0) from the currency's rounding (a float generally < 1.0).
fmt = "%.{0}f".format(display_currency.decimal_places)
if from_currency:
from_amount = from_currency.compute(from_amount, display_currency)
if options.get('from_currency'):
value = options['from_currency'].compute(value, display_currency)
lang = self.user_lang()
formatted_amount = lang.format(fmt, display_currency.round(from_amount),
formatted_amount = lang.format(fmt, display_currency.round(value),
grouping=True, monetary=True).replace(r' ', u'\N{NO-BREAK SPACE}')
pre = post = u''
@@ -338,8 +325,17 @@ class MonetaryConverter(models.AbstractModel):
else:
post = u'\N{NO-BREAK SPACE}{symbol}'.format(symbol=display_currency.symbol)
return pre, formatted_amount, post
return u'{pre}<span class="oe_currency_value">{0}</span>{post}'.format(formatted_amount, pre=pre, post=post)
@api.model
def record_to_html(self, record, field_name, options, values=None):
options = dict(options)
#currency should be specified by monetary field
field = record._fields[field_name]
if not options.get('display_currency') and field.type == 'monetary' and field.currency_field:
options['display_currency'] = record[field.currency_field]
return self.value_to_html(record[field_name], options)
TIMEDELTA_UNITS = (
('year', 3600 * 24 * 365),
@@ -368,10 +364,12 @@ class DurationConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
def value_to_html(self, value, field, options=None):
def value_to_html(self, value, options):
units = dict(TIMEDELTA_UNITS)
if value < 0:
raise ValueError(_("Durations can't be negative"))
if not options or options.get('unit') not in units:
raise ValueError(_("A unit must be provided to duration widgets"))
@@ -396,48 +394,52 @@ class RelativeDatetimeConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
def value_to_html(self, value, field, options=None):
def value_to_html(self, value, options):
locale = babel.Locale.parse(self.user_lang().code)
if isinstance(value, basestring):
value = fields.Datetime.from_string(value)
# value should be a naive datetime in UTC. So is fields.Datetime.now()
reference = fields.Datetime.from_string(field.now())
reference = fields.Datetime.from_string(options['now'])
return unicodifier(babel.dates.format_timedelta(value - reference, add_direction=True, locale=locale))
@api.model
def record_to_html(self, record, field_name, options, values=None):
if 'now' not in options:
options = dict(options, now=record._fields[field_name].now())
return super(RelativeDatetimeConverter, self).record_to_html(record, field_name, options, values=values)
class Contact(models.AbstractModel):
_name = 'ir.qweb.field.contact'
_inherit = 'ir.qweb.field.many2one'
@api.model
def record_to_html(self, record, field_name, options, values=None):
opf = options and options.get('fields') or ["name", "address", "phone", "mobile", "fax", "email"]
def value_to_html(self, value, options):
if not value.exists():
return False
value_rec = record[field_name]
if not value_rec:
return None
value_rec = value_rec.sudo().with_context(show_address=True)
value = value_rec.name_get()[0][1]
opf = options and options.get('fields') or ["name", "address", "phone", "mobile", "fax", "email"]
value = value.sudo().with_context(show_address=True)
name_get = value.name_get()[0][1]
val = {
'name': value.split("\n")[0],
'address': escape("\n".join(value.split("\n")[1:])).strip(),
'phone': value_rec.phone,
'mobile': value_rec.mobile,
'fax': value_rec.fax,
'city': value_rec.city,
'country_id': value_rec.country_id.display_name,
'website': value_rec.website,
'email': value_rec.email,
'name': name_get.split("\n")[0],
'address': escape("\n".join(name_get.split("\n")[1:])).strip(),
'phone': value.phone,
'mobile': value.mobile,
'fax': value.fax,
'city': value.city,
'country_id': value.country_id.display_name,
'website': value.website,
'email': value.email,
'fields': opf,
'object': value_rec,
'object': value,
'options': options
}
return unicodifier(self.env.ref('base.contact').render(val, engine='ir.qweb'))
return self.env['ir.qweb'].render('base.contact', val)
class QwebView(models.AbstractModel):
+51 -4
View File
@@ -2,11 +2,11 @@
import ast
from urlparse import urlparse
from lxml import html
import math
from .qweb import QWeb, Contextifier
from .assetsbundle import AssetsBundle
from lxml import etree
from collections import OrderedDict
from openerp import api, models, tools
from openerp.tools import safe_eval
@@ -144,6 +144,34 @@ class IrQWeb(models.AbstractModel, QWeb):
))
]
# for backward compatibility to remove after v10
def _compile_widget_options(self, el, directive_type):
field_options = super(IrQWeb, self)._compile_widget_options(el, directive_type)
if ('t-%s-options' % directive_type) in el.attrib:
if tools.config['dev_mode']:
_logger.warning("Use new syntax t-options instead of t-%s-options" % directive_type)
if not field_options:
field_options = el.attrib.pop('t-%s-options' % directive_type)
if field_options and 'monetary' in field_options:
try:
options = "{'widget': 'monetary'"
for k, v in json.loads(field_options).iteritems():
print k, v
if k in ('display_currency', 'from_currency'):
options = "%s, '%s': %s" % (options, k, v)
else:
options = "%s, '%s': '%s'" % (options, k, v)
options = "%s}" % options
field_options = options
_logger.warning("Use new syntax for '%s' monetary widget t-options (python dict instead of deprecated JSON syntax)." % etree.tostring(el))
except ValueError:
pass
return field_options
# end backward
# method called by computing code
@tools.conditional(
@@ -231,16 +259,35 @@ class IrQWeb(models.AbstractModel, QWeb):
converter = self.env[model] if model in self.env else self.env['ir.qweb.field']
# get content
content = converter.record_to_html(record, field_name, field_options, values)
content = converter.record_to_html(record, field_name, field_options, values=values)
attributes = converter.attributes(record, field_name, field_options, values)
return (attributes, content, inherit_branding or translate)
def _get_widget(self, value, expression, tagName, field_options, options, values):
field_options['type'] = field_options['widget']
field_options['tagName'] = tagName
field_options['expression'] = expression
# field converter
model = 'ir.qweb.field.' + field_options['type']
converter = self.env[model] if model in self.env else self.env['ir.qweb.field']
# get content
content = converter.value_to_html(value, field_options)
attributes = OrderedDict()
attributes['data-oe-type'] = field_options['type']
attributes['data-oe-expression'] = field_options['expression']
return (attributes, content, None)
# formating methods
def _format_func_monetary(self, value, display_currency, from_currency=None):
pre, value, post = self.env['ir.qweb.field.monetary']._format_func_monetary(value, display_currency, from_currency)
return u'{pre}{0}{post}'.format(value, pre=pre, post=post)
options = {}
options['display_currency'] = display_currency
options['from_currency'] = from_currency
return self.env['ir.qweb.field.monetary'].value_to_html(value, options)
def _format_func_htmlcontact(self, value, options, values=None):
return self.pool['ir.qweb.field.contact'].record_to_html(
+131 -61
View File
@@ -6,7 +6,8 @@ import re
import traceback
from itertools import count
from textwrap import dedent
from werkzeug.utils import escape
import werkzeug
from werkzeug.utils import escape as _escape
from itertools import izip, tee
import __builtin__
builtin_defaults = {name: getattr(__builtin__, name) for name in dir(__builtin__)}
@@ -141,16 +142,8 @@ class QWebException(Exception):
def __repr__(self):
return str(self)
import werkzeug
from werkzeug.utils import escape
# Avoid DeprecationWarning while still remaining compatible with werkzeug pre-0.9
if getattr(werkzeug, '__version__', '0.0') < '0.9.0':
def qweb_escape(text):
return escape(unicodifier(text), quote=True)
else:
def qweb_escape(text):
return escape(unicodifier(text))
escape = (lambda text: _escape(text, quote=True)) if getattr(werkzeug, '__version__', '0.0') < '0.9.0' else _escape
def unicodifier(val):
if val is None or val is False:
@@ -493,14 +486,14 @@ class QWeb(object):
* collections
* itertools
Define:
* qweb_escape
* escape
* unicodifier (empty string for a None or False, otherwise unicode string)
"""
return ast.parse(dedent("""
from collections import OrderedDict
import itertools
from itertools import repeat, imap
from openerp.addons.base.ir.ir_qweb.qweb import qweb_escape, unicodifier, foreach_iterator
from openerp.addons.base.ir.ir_qweb.qweb import escape, unicodifier, foreach_iterator
"""))
def _create_def(self, options, body, prefix='fn', lineno=None):
@@ -684,11 +677,11 @@ class QWeb(object):
return [
'debug',
'groups', 'foreach', 'if', 'else',
'field',
'field', 'esc', 'raw',
'tag',
'call',
'set',
'esc', 'raw', 'content',
'content',
]
def _is_static_node(self, el):
@@ -704,7 +697,7 @@ class QWeb(object):
content = self._compile_directive_content(el, options)
if el.tag == 't':
return content
tag = u'<%s%s' % (el.tag, u''.join([u' %s="%s"' % (name, qweb_escape(value)) for name, value in el.attrib.iteritems()]))
tag = u'<%s%s' % (el.tag, u''.join([u' %s="%s"' % (name, escape(unicodifier(value))) for name, value in el.attrib.iteritems()]))
if el.tag in self._void_elements:
return [self._append(ast.Str(tag + '/>'))] + content
else:
@@ -795,7 +788,7 @@ class QWeb(object):
# append(u' ')
# append(name)
# append(u'="')
# append(qweb_escape(value))
# append(escape(unicodifier((value)))
# append(u'"')
body.append(ast.For(
target=ast.Tuple(elts=[ast.Name(id='name', ctx=ast.Store()), ast.Name(id='value', ctx=ast.Store())], ctx=ast.Store()),
@@ -829,8 +822,12 @@ class QWeb(object):
self._append(ast.Name(id='name', ctx=ast.Load())),
self._append(ast.Str(u'="')),
self._append(ast.Call(
func=ast.Name(id='qweb_escape', ctx=ast.Load()),
args=[ast.Name(id='value', ctx=ast.Load())], keywords=[],
func=ast.Name(id='escape', ctx=ast.Load()),
args=[ast.Call(
func=ast.Name(id='unicodifier', ctx=ast.Load()),
args=[ast.Name(id='value', ctx=ast.Load())], keywords=[],
starargs=None, kwargs=None
)], keywords=[],
starargs=None, kwargs=None
)),
self._append(ast.Str(u'"')),
@@ -844,6 +841,8 @@ class QWeb(object):
def _compile_tag(self, el, content, options, attr_already_created=False):
""" Compile the tag of the given element into a list of AST nodes. """
if el.tag == 't':
return content
body = [self._append(ast.Str(u'<%s' % el.tag))]
body.extend(self._compile_all_attributes(el, options, attr_already_created))
if el.tag in self._void_elements:
@@ -869,8 +868,6 @@ class QWeb(object):
def _compile_directive_tag(self, el, options):
el.attrib.pop('t-tag', None)
content = self._compile_directives(el, options)
if el.tag == 't':
return content
return self._compile_tag(el, content, options, False)
def _compile_directive_set(self, el, options):
@@ -925,44 +922,102 @@ class QWeb(object):
)]
def _compile_directive_esc(self, el, options):
return [
# content = t-raw value
ast.Assign(
targets=[ast.Name(id='content', ctx=ast.Store())],
value=self._compile_expr0(el.attrib.pop('t-esc')),
),
# if content is not None: display
self._if_content_is_not_Falsy(
# append(escape($content))
body=[self._append(ast.Call(
func=ast.Name(id='qweb_escape', ctx=ast.Load()),
args=[ast.Name(id='content', ctx=ast.Load())], keywords=[],
starargs=None, kwargs=None
))],
# append(body default value)
orelse=self._compile_directive_content(el, options),
)
]
field_options = self._compile_widget_options(el, 'esc')
content = self._compile_widget(el, el.attrib.pop('t-esc'), field_options, 'escape')
return content + self._compile_widget_value(el, options)
def _compile_directive_raw(self, el, options):
return [
# content = t-raw value
ast.Assign(
targets=[ast.Name(id='content', ctx=ast.Store())],
value=self._compile_expr0(el.attrib.pop('t-raw')),
field_options = self._compile_widget_options(el, 'raw')
content = self._compile_widget(el, el.attrib.pop('t-raw'), field_options)
return content + self._compile_widget_value(el, options)
def _compile_widget(self, el, expression, field_options, escape=None):
# if isinstance(value, basestring):
# value = escape(unicodifier(value))
escaped = ast.If(
test=ast.Call(
func=ast.Name(id='isinstance', ctx=ast.Load()),
args=[
ast.Name(id='content', ctx=ast.Load()),
ast.Name(id='basestring', ctx=ast.Load())
], keywords=[],
starargs=None, kwargs=None
),
self._if_content_is_not_Falsy(
# append($content)
body=[self._append(ast.Call(
func=ast.Name(id='unicodifier', ctx=ast.Load()),
args=[ast.Name(id='content', ctx=ast.Load())], keywords=[],
body=[ast.Assign(
targets=[ast.Name(id='content', ctx=ast.Store())],
value=ast.Call(
func=ast.Name(id=escape, ctx=ast.Load()),
args=[ast.Call(
func=ast.Name(id='unicodifier', ctx=ast.Load()),
args=[ast.Name(id='content', ctx=ast.Load())], keywords=[],
starargs=None, kwargs=None
)], keywords=[],
starargs=None, kwargs=None
))],
# append(body default value)
orelse=self._compile_directive_content(el, options),
)
) if escape else ast.Name(id='content', ctx=ast.Load())
)],
orelse=[]
)
if field_options:
return [
# value = t-(esc|raw)
ast.Assign(
targets=[ast.Name(id='content', ctx=ast.Store())],
value=self._compile_expr0(expression)
),
escaped,
# t_attrs, content, force_display = self._get_widget(value, expression, tagName, field options, template options, values)
ast.Assign(
targets=[ast.Tuple(elts=[
ast.Name(id='t_attrs', ctx=ast.Store()),
ast.Name(id='content', ctx=ast.Store()),
ast.Name(id='force_display', ctx=ast.Store())
], ctx=ast.Store())],
value=ast.Call(
func=ast.Attribute(
value=ast.Name(id='self', ctx=ast.Load()),
attr='_get_widget',
ctx=ast.Load()
),
args=[
ast.Name(id='content', ctx=ast.Load()),
ast.Str(expression),
ast.Str(el.tag),
field_options and self._compile_expr(field_options) or ast.Dict(keys=[], values=[]),
ast.Name(id='options', ctx=ast.Load()),
ast.Name(id='values', ctx=ast.Load()),
],
keywords=[], starargs=None, kwargs=None
)
)
]
return [
# t_attrs, content, force_display = OrderedDict(), t-(esc|raw), None
ast.Assign(
targets=[ast.Tuple(elts=[
ast.Name(id='t_attrs', ctx=ast.Store()),
ast.Name(id='content', ctx=ast.Store()),
ast.Name(id='force_display', ctx=ast.Store()),
], ctx=ast.Store())],
value=ast.Tuple(elts=[
ast.Call(
func=ast.Name(id='OrderedDict', ctx=ast.Load()),
args=[],
keywords=[], starargs=None, kwargs=None
),
self._compile_expr0(expression),
ast.Name(id='None', ctx=ast.Load()),
], ctx=ast.Load())
),
escaped
]
# for backward compatibility to remove after v10
def _compile_widget_options(self, el, directive_type):
return el.attrib.pop('t-options', None)
# end backward
def _compile_directive_content(self, el, options):
body = []
if el.text is not None:
@@ -1060,10 +1115,10 @@ class QWeb(object):
"t-field must have at least a dot like 'record.field_name'"
expression = el.attrib.pop('t-field')
field_options = el.attrib.pop('t-field-options', None)
field_options = self._compile_widget_options(el, 'field')
record, field_name = expression.rsplit('.', 1)
content = [
return [
# t_attrs, content, force_display = self._get_field(record, field_name, expression, tagName, field options, template options, values)
ast.Assign(
targets=[ast.Tuple(elts=[
@@ -1089,8 +1144,9 @@ class QWeb(object):
keywords=[], starargs=None, kwargs=None
)
)
]
] + self._compile_widget_value(el, options)
def _compile_widget_value(self, el, options):
# if force_display:
# display the tag without content
orelse = [ast.If(
@@ -1145,17 +1201,22 @@ class QWeb(object):
]
# if content is not None:
# display the tag
# display the tag (unicodifier(content))
# else
# if default_content:
# display the tag with default content
# elif force_display:
# display the tag without content
content.append(self._if_content_is_not_Falsy(
body=self._compile_tag(el, [self._append(ast.Name(id='content', ctx=ast.Load()))], options, True),
return [self._if_content_is_not_Falsy(
body=self._compile_tag(el, [self._append(
ast.Call(
func=ast.Name(id='unicodifier', ctx=ast.Load()),
args=[ast.Name(id='content', ctx=ast.Load())], keywords=[],
starargs=None, kwargs=None
)
)], options, True),
orelse=orelse,
))
return content
)]
def _compile_directive_call(self, el, options):
tmpl = el.attrib.pop('t-call')
@@ -1299,7 +1360,16 @@ class QWeb(object):
* string or None: content
* boolean: force_display display the tag if the content and default_content are None
"""
return (OrderedDict(), escape(unicodifier(getattr(record, field_name, None))), False)
return self._get_widget(getattr(record, field_name, None), expression, tagName, field_options, options, values)
def _get_widget(self, value, expression, tagName, field_options, options, values):
"""
:returns: tuple:
* OrderedDict: attributes
* string or None: content
* boolean: force_display display the tag if the content and default_content are None
"""
return (OrderedDict(), value, False)
# compile expression
@@ -30,8 +30,11 @@ class TestExport(common.TransactionCase):
break
except KeyError: pass
return lambda value, options=None, context=None: model.value_to_html(
self.cr, self.uid, value, field, options=options, context=context)
def converter(value, options=None, context=None):
record = self.Model.new(self.cr, self.uid, {name: value}, context=context)
return model.record_to_html(
self.cr, self.uid, record, name, options or {}, context=context)
return converter
class TestBasicExport(TestExport):
_model = 'test_converter.test_model'
@@ -188,36 +191,23 @@ class TestTextExport(TestBasicExport):
class TestMany2OneExport(TestBasicExport):
def test_many2one(self):
Sub = self.registry('test_converter.test_model.sub')
converter = self.get_converter('many2one')
id0 = self.Model.create(self.cr, self.uid, {
'many2one': Sub.create(self.cr, self.uid, {'name': "Foo"})
})
id1 = self.Model.create(self.cr, self.uid, {
'many2one': Sub.create(self.cr, self.uid, {'name': "Fo<b>o</b>"})
})
def converter(record):
converter = self.registry('ir.qweb.field.many2one')
field_name = 'many2one'
return converter.record_to_html(self.cr, self.uid, record, field_name, {})
value = converter(self.Model.browse(self.cr, self.uid, id0))
value = converter(Sub.create(self.cr, self.uid, {'name': "Foo"}))
self.assertEqual(value, "Foo")
value = converter(self.Model.browse(self.cr, self.uid, id1))
value = converter(Sub.create(self.cr, self.uid, {'name': "Fo<b>o</b>"}))
self.assertEqual(value, "Fo&lt;b&gt;o&lt;/b&gt;")
class TestBinaryExport(TestBasicExport):
def test_image(self):
field = self.get_field('binary')
converter = self.registry('ir.qweb.field.image')
with open(os.path.join(directory, 'test_vectors', 'image'), 'rb') as f:
content = f.read()
encoded_content = content.encode('base64')
value = converter.value_to_html(self.cr, self.uid, encoded_content, field)
value = converter.value_to_html(self.cr, self.uid, encoded_content, {})
self.assertEqual(
value, '<img src="data:image/jpeg;base64,%s">' % (
@@ -229,30 +219,23 @@ class TestBinaryExport(TestBasicExport):
with self.assertRaises(ValueError):
converter.value_to_html(
self.cr, self.uid, 'binary', content.encode('base64'), field)
self.cr, self.uid, 'binary', content.encode('base64'), {})
with open(os.path.join(directory, 'test_vectors', 'pptx'), 'rb') as f:
content = f.read()
with self.assertRaises(ValueError):
converter.value_to_html(
self.cr, self.uid, 'binary', content.encode('base64'), field)
self.cr, self.uid, 'binary', content.encode('base64'), {})
class TestSelectionExport(TestBasicExport):
def test_selection(self):
[record] = self.Model.browse(self.cr, self.uid, [self.Model.create(self.cr, self.uid, {
'selection': 4,
'selection_str': 'C',
})])
converter = self.registry('ir.qweb.field.selection')
field_name = 'selection'
value = converter.record_to_html(self.cr, self.uid, record, field_name, {})
converter = self.get_converter('selection')
value = converter(4)
self.assertEqual(value, e(u"réponse <D>"))
field_name = 'selection_str'
value = converter.record_to_html(self.cr, self.uid, record, field_name, {})
converter = self.get_converter('selection_str')
value = converter('C')
self.assertEqual(value, u"Qu'est-ce qu'il fout ce maudit pancake, tabernacle ?")
class TestHTMLExport(TestBasicExport):