diff --git a/openerp/addons/base/ir/ir_qweb/fields.py b/openerp/addons/base/ir/ir_qweb/fields.py
index c0feaa27aa8..c8d93037c26 100644
--- a/openerp/addons/base/ir/ir_qweb/fields.py
+++ b/openerp/addons/base/ir/ir_qweb/fields.py
@@ -5,7 +5,7 @@ from odoo import api, fields, models, _
from PIL import Image
from cStringIO import StringIO
import babel
-from openerp.tools import html_escape as escape, posix_to_ldml, safe_eval
+from openerp.tools import html_escape as escape, posix_to_ldml, safe_eval, float_utils
from .qweb import unicodifier
import logging
@@ -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
@@ -91,21 +91,14 @@ class FieldConverter(models.AbstractModel):
return html_escape(unicodifier(value) or u'', options)
@api.model
- def record_to_html(self, record, field_name, options, values=None):
- """ record_to_html(record, field_name, options, values)
+ def record_to_html(self, record, field_name, options):
+ """ record_to_html(record, field_name, options)
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,16 +128,19 @@ 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)
+ if precision is None:
+ fmt = '%f'
+ else:
+ value = float_utils.float_round(value, precision_digits=precision)
+ fmt = '%.{precision}f'.format(precision=precision)
+
+ formatted = self.user_lang().format(fmt, value, grouping=True)
# %f does not strip trailing zeroes. %g does but its precision causes
# it to switch to scientific notation starting at a million *and* to
@@ -152,15 +148,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):
+ 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)
+
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 +187,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 +215,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 +227,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):
+ 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)
class ManyToOneConverter(models.AbstractModel):
@@ -235,8 +244,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 +258,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 +275,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,25 +305,8 @@ class MonetaryConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
- def record_to_html(self, record, field_name, options, values=None):
- field = record._fields[field_name]
-
- # 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
-
- display_currency = False
- #currency should be specified by monetary field
- if field.type == 'monetary' and field.currency_field:
- display_currency = record[field.currency_field]
- #otherwise fall back to old method
- if not display_currency:
- display_currency = options['display_currency']
+ def value_to_html(self, value, options):
+ display_currency = options['display_currency']
# lang.format mandates a sprintf-style format. These formats are non-
# minimal (they have a default fixed precision instead), and
@@ -324,26 +315,30 @@ class MonetaryConverter(models.AbstractModel):
# (integer > 0) from the currency's rounding (a float generally < 1.0).
fmt = "%.{0}f".format(display_currency.decimal_places)
- from_amount = record[field_name]
-
if options.get('from_currency'):
- from_amount = options['from_currency'].compute(from_amount, display_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''
if display_currency.position == 'before':
- pre = u'{symbol}\N{NO-BREAK SPACE}'
+ pre = u'{symbol}\N{NO-BREAK SPACE}'.format(symbol=display_currency.symbol)
else:
- post = u'\N{NO-BREAK SPACE}{symbol}'
+ post = u'\N{NO-BREAK SPACE}{symbol}'.format(symbol=display_currency.symbol)
- return u'{pre}
{0}{post}'.format(
- formatted_amount, pre=pre, post=post,
- ).format(
- symbol=display_currency.symbol,
- )
+ return u'{pre}
{0}{post}'.format(formatted_amount, pre=pre, post=post)
+
+ @api.model
+ def record_to_html(self, record, field_name, options):
+ 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),
@@ -372,10 +367,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"))
@@ -400,48 +397,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):
+ 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)
+
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):
@@ -449,7 +450,7 @@ class QwebView(models.AbstractModel):
_inherit = 'ir.qweb.field.many2one'
@api.model
- def record_to_html(self, record, field_name, options, values=None):
+ def record_to_html(self, record, field_name, options):
if not getattr(record, field_name):
return None
diff --git a/openerp/addons/base/ir/ir_qweb/ir_qweb.py b/openerp/addons/base/ir/ir_qweb/ir_qweb.py
index 166858edd2c..88e6ee95307 100644
--- a/openerp/addons/base/ir/ir_qweb/ir_qweb.py
+++ b/openerp/addons/base/ir/ir_qweb/ir_qweb.py
@@ -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,11 +144,41 @@ 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(
+ # in non-xml-debug mode we want assets to be cached forever, and the admin can force a cache clear
+ # by restarting the server after updating the source code (or using the "Clear server cache" in debug tools)
'xml' not in tools.config['dev_mode'],
- tools.ormcache('xmlid', 'options.get("lang", "en_US")', 'css', 'js', 'debug', 'async', 'values.get("time_cache_assets")'),
+ tools.ormcache('xmlid', 'options.get("lang", "en_US")', 'css', 'js', 'debug', 'async'),
)
def _get_asset(self, xmlid, options, css=True, js=True, debug=False, async=False, values=None):
files, remains = self._get_asset_content(xmlid, options)
@@ -231,33 +261,30 @@ 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)
attributes = converter.attributes(record, field_name, field_options, values)
return (attributes, content, inherit_branding or translate)
- # formating methods
- def _format_func_monetary(self, value, display_currency=None, from_currency=None):
- env = display_currency.env
- precision = int(round(math.log10(display_currency.rounding)))
- fmt = "%.{0}f".format(-precision if precision < 0 else 0)
- lang = env['res.lang']._lang_get(env.context.get('lang', 'en_US'))
- formatted_amount = lang.format(fmt, value, grouping=True, monetary=True).replace(r' ', u'\N{NO-BREAK SPACE}')
- pre = post = u''
- if display_currency.position == 'before':
- pre = u'{symbol}\N{NO-BREAK SPACE}'
- else:
- post = u'\N{NO-BREAK SPACE}{symbol}'
- return u'{pre}{0}{post}'.format(
- formatted_amount, pre=pre, post=post
- ).format(symbol=display_currency.symbol,)
+ 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
- def _format_func_htmlcontact(self, value, options, values=None):
- return self.pool['ir.qweb.field.contact'].record_to_html(
- self.env.cr, self.env.uid, dict(contact=value), 'contact', dict(options or {}), context=self.env.context
- )
+ # 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)
# compile expression add safe_eval
+
def _compile_expr(self, expr):
""" Compiles a purported Python expression to ast, verifies that it's safe
(according to safe_eval's semantics) and alter its variable references to
diff --git a/openerp/addons/base/ir/ir_qweb/qweb.py b/openerp/addons/base/ir/ir_qweb/qweb.py
index dda4e1546c2..759559e1be3 100644
--- a/openerp/addons/base/ir/ir_qweb/qweb.py
+++ b/openerp/addons/base/ir/ir_qweb/qweb.py
@@ -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):
@@ -924,45 +921,6 @@ class QWeb(object):
value=value
)]
- 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),
- )
- ]
-
- 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')),
- ),
- 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=[],
- starargs=None, kwargs=None
- ))],
- # append(body default value)
- orelse=self._compile_directive_content(el, options),
- )
- ]
-
def _compile_directive_content(self, el, options):
body = []
if el.text is not None:
@@ -1048,6 +1006,103 @@ class QWeb(object):
def _compile_tail(self, el):
return el.tail is not None and [self._append(ast.Str(unicodifier(el.tail)))] or []
+ def _compile_directive_esc(self, 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):
+ 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
+ ),
+ 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
+ ) 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_field(self, el, options):
""" Compile something like ``
+1 555 555 8069`` """
node_name = el.tag
@@ -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
diff --git a/openerp/addons/test_converter/tests/test_html.py b/openerp/addons/test_converter/tests/test_html.py
index 58128176eac..f1d7dfc4ef7 100644
--- a/openerp/addons/test_converter/tests/test_html.py
+++ b/openerp/addons/test_converter/tests/test_html.py
@@ -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'
@@ -104,7 +107,7 @@ class TestCurrencyExport(TestExport):
'widget': 'monetary',
'display_currency': dest,
}
- return converter.record_to_html(self.cr, self.uid, obj, 'value', options, {'obj': obj})
+ return converter.record_to_html(self.cr, self.uid, obj, 'value', options)
def test_currency_post(self):
currency = self.create(self.Currency, name="Test", symbol=u"test")
@@ -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
o"})
- })
-
- 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
o"}))
self.assertEqual(value, "Fo<b>o</b>")
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, '

' % (
@@ -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
"))
- 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):