[ADD] ability to disable fields escaping
bzr revid: xmo@openerp.com-20131216092330-2v50c8uzzsb3qp8q
This commit is contained in:
parent
f9e986ecea
commit
f6a5800d59
|
@ -22,6 +22,13 @@ column, but this can be overridden by providing a ``widget`` as field option.
|
|||
Field options are specified through ``t-field-options``, which must be a JSON
|
||||
object (map). Custom widgets may define their own (possibly mandatory) options.
|
||||
|
||||
Global options
|
||||
--------------
|
||||
|
||||
A global option ``html-escape`` is provided. It defaults to ``True``, and for
|
||||
many (not all) fields it determines whether the field's output will be
|
||||
html-escaped before being output.
|
||||
|
||||
Date and datetime converters
|
||||
----------------------------
|
||||
|
||||
|
@ -88,3 +95,4 @@ The duration must be a positive number, and no rounding is applied.
|
|||
|
||||
.. _ldml date format patterns:
|
||||
http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
|
||||
|
||||
|
|
|
@ -530,7 +530,7 @@ class FieldConverter(osv.AbstractModel):
|
|||
""" Converts a single value to its HTML version/output
|
||||
"""
|
||||
if not value: return ''
|
||||
return werkzeug.utils.escape(value)
|
||||
return value
|
||||
|
||||
def record_to_html(self, cr, uid, field_name, record, column, options=None, context=None):
|
||||
""" Converts the specified field of the browse_record ``record`` to
|
||||
|
@ -554,6 +554,10 @@ class FieldConverter(osv.AbstractModel):
|
|||
cr, uid, field_name, record,
|
||||
record._model._all_columns[field_name].column,
|
||||
options, context=context)
|
||||
if options.get('html-escape', True):
|
||||
content = werkzeug.utils.escape(content)
|
||||
elif hasattr(content, '__html__'):
|
||||
content = content.__html__()
|
||||
except Exception:
|
||||
_logger.warning("Could not get field %s for model %s",
|
||||
field_name, record._model._name, exc_info=True)
|
||||
|
@ -620,7 +624,7 @@ class FloatConverter(osv.AbstractModel):
|
|||
# strip trailing 0.
|
||||
if not precision:
|
||||
formatted = re.sub(r'(?:(0|\d+?)0+)$', r'\1', formatted)
|
||||
return werkzeug.utils.escape(formatted)
|
||||
return formatted
|
||||
|
||||
class DateConverter(osv.AbstractModel):
|
||||
_name = 'ir.qweb.field.date'
|
||||
|
@ -677,7 +681,8 @@ class TextConverter(osv.AbstractModel):
|
|||
Escapes the value and converts newlines to br. This is bullshit.
|
||||
"""
|
||||
if not value: return ''
|
||||
return werkzeug.utils.escape(value).replace('\n', '<br>\n')
|
||||
|
||||
return nl2br(value, options=options)
|
||||
|
||||
class SelectionConverter(osv.AbstractModel):
|
||||
_name = 'ir.qweb.field.selection'
|
||||
|
@ -699,14 +704,14 @@ class ManyToOneConverter(osv.AbstractModel):
|
|||
[read] = record.read([field_name])
|
||||
_, value = read[field_name]
|
||||
|
||||
return werkzeug.utils.escape(value).replace('\n', '<br>\n')
|
||||
return nl2br(value, options=options)
|
||||
|
||||
class HTMLConverter(osv.AbstractModel):
|
||||
_name = 'ir.qweb.field.html'
|
||||
_inherit = 'ir.qweb.field'
|
||||
|
||||
def value_to_html(self, cr, uid, value, column, options=None, context=None):
|
||||
return value or ''
|
||||
return HTMLSafe(value or '')
|
||||
|
||||
class ImageConverter(osv.AbstractModel):
|
||||
""" ``image`` widget rendering, inserts a data:uri-using image tag in the
|
||||
|
@ -729,7 +734,7 @@ class ImageConverter(osv.AbstractModel):
|
|||
except: # image.verify() throws "suitable exceptions", I have no idea what they are
|
||||
raise ValueError("Invalid image content")
|
||||
|
||||
return '<img src="data:%s;base64,%s">' % (Image.MIME[image.format], value)
|
||||
return HTMLSafe('<img src="data:%s;base64,%s">' % (Image.MIME[image.format], value))
|
||||
|
||||
class MonetaryConverter(osv.AbstractModel):
|
||||
""" ``monetary`` converter, has a mandatory option
|
||||
|
@ -783,12 +788,12 @@ class MonetaryConverter(osv.AbstractModel):
|
|||
else:
|
||||
post = u' {symbol}'
|
||||
|
||||
return u'{pre}<span class="oe_currency_value">{0}</span>{post}'.format(
|
||||
return HTMLSafe(u'{pre}<span class="oe_currency_value">{0}</span>{post}'.format(
|
||||
formatted_amount,
|
||||
pre=pre, post=post,
|
||||
).format(
|
||||
symbol=display.symbol,
|
||||
)
|
||||
))
|
||||
|
||||
def display_currency(self, cr, uid, options):
|
||||
return self.qweb_object().eval_object(
|
||||
|
@ -858,6 +863,43 @@ class RelativeDatetimeConverter(osv.AbstractModel):
|
|||
return babel.dates.format_timedelta(
|
||||
value - reference, add_direction=True, locale=locale)
|
||||
|
||||
class HTMLSafe(object):
|
||||
""" HTMLSafe string wrapper, Werkzeug's escape() has special handling for
|
||||
objects with a ``__html__`` methods but AFAIK does not provide any such
|
||||
object.
|
||||
|
||||
Wrapping a string in HTML will prevent its escaping
|
||||
"""
|
||||
__slots__ = ['string']
|
||||
def __init__(self, string):
|
||||
self.string = string
|
||||
def __html__(self):
|
||||
return self.string
|
||||
def __str__(self):
|
||||
s = self.string
|
||||
if isinstance(s, unicode):
|
||||
return s.encode('utf-8')
|
||||
return s
|
||||
def __unicode__(self):
|
||||
s = self.string
|
||||
if isinstance(s, str):
|
||||
return s.decode('utf-8')
|
||||
return s
|
||||
|
||||
def nl2br(string, options=None):
|
||||
""" Converts newlines to HTML linebreaks in ``string``. Automatically
|
||||
escapes content unless options['html-escape'] is set to False, and returns
|
||||
the result wrapped in an HTMLSafe object.
|
||||
|
||||
:param str string:
|
||||
:param dict options:
|
||||
:rtype: HTMLSafe
|
||||
"""
|
||||
if options is None: options = {}
|
||||
|
||||
if options.get('html-escape', True):
|
||||
string = werkzeug.utils.escape(string)
|
||||
return HTMLSafe(string.replace('\n', '<br>\n'))
|
||||
|
||||
def get_field_type(column, options):
|
||||
""" Gets a t-field's effective type from the field's column and its options
|
||||
|
|
|
@ -4,6 +4,8 @@ import os
|
|||
import xml.dom.minidom
|
||||
import datetime
|
||||
|
||||
from werkzeug.utils import escape as e
|
||||
|
||||
from openerp.tests import common
|
||||
from openerp.addons.base.ir import ir_qweb
|
||||
|
||||
|
@ -36,8 +38,8 @@ class TestExport(common.TransactionCase):
|
|||
break
|
||||
except KeyError: pass
|
||||
|
||||
return lambda value, options=None, context=None: model.value_to_html(
|
||||
self.cr, self.uid, value, column, options=options, context=context)
|
||||
return lambda value, options=None, context=None: e(model.value_to_html(
|
||||
self.cr, self.uid, value, column, options=options, context=context))
|
||||
|
||||
class TestBasicExport(TestExport):
|
||||
_model = 'test_converter.test_model'
|
||||
|
@ -223,8 +225,8 @@ class TestMany2OneExport(TestBasicExport):
|
|||
column = self.get_column('many2one')
|
||||
model = self.registry('ir.qweb.field.many2one')
|
||||
|
||||
return model.record_to_html(
|
||||
self.cr, self.uid, 'many2one', record, column)
|
||||
return e(model.record_to_html(
|
||||
self.cr, self.uid, 'many2one', record, column))
|
||||
|
||||
value = converter(self.Model.browse(self.cr, self.uid, id0))
|
||||
self.assertEqual(value, "Foo")
|
||||
|
@ -241,8 +243,8 @@ class TestBinaryExport(TestBasicExport):
|
|||
content = f.read()
|
||||
|
||||
encoded_content = content.encode('base64')
|
||||
value = converter.value_to_html(
|
||||
self.cr, self.uid, encoded_content, column)
|
||||
value = e(converter.value_to_html(
|
||||
self.cr, self.uid, encoded_content, column))
|
||||
self.assertEqual(
|
||||
value, '<img src="data:image/jpeg;base64,%s">' % (
|
||||
encoded_content
|
||||
|
@ -252,15 +254,15 @@ class TestBinaryExport(TestBasicExport):
|
|||
content = f.read()
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
converter.value_to_html(
|
||||
self.cr, self.uid, 'binary', content.encode('base64'), column)
|
||||
e(converter.value_to_html(
|
||||
self.cr, self.uid, 'binary', content.encode('base64'), column))
|
||||
|
||||
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'), column)
|
||||
e(converter.value_to_html(
|
||||
self.cr, self.uid, 'binary', content.encode('base64'), column))
|
||||
|
||||
class TestSelectionExport(TestBasicExport):
|
||||
def test_selection(self):
|
||||
|
|
Loading…
Reference in New Issue