diff --git a/addons/decimal_precision/decimal_precision.py b/addons/decimal_precision/decimal_precision.py
index b237f053ebb..5576038d54d 100644
--- a/addons/decimal_precision/decimal_precision.py
+++ b/addons/decimal_precision/decimal_precision.py
@@ -22,9 +22,9 @@
import openerp
from openerp import SUPERUSER_ID
from openerp import tools
-from openerp.osv import osv, fields
+from openerp.osv import orm, fields
-class decimal_precision(osv.osv):
+class decimal_precision(orm.Model):
_name = 'decimal.precision'
_columns = {
'name': fields.char('Usage', size=50, select=True, required=True),
@@ -71,4 +71,29 @@ def get_precision(application):
return (16, res)
return change_digit
+class DecimalPrecisionFloat(orm.AbstractModel):
+ """ Override qweb.field.float to add a `decimal_precision` domain option
+ and use that instead of the column's own value if it is specified
+ """
+ _inherit = 'ir.qweb.field.float'
+
+
+ def precision(self, cr, uid, column, options=None, context=None):
+ dp = options and options.get('decimal_precision')
+ if dp:
+ return self.pool['decimal.precision'].precision_get(
+ cr, uid, dp)
+
+ return super(DecimalPrecisionFloat, self).precision(
+ cr, uid, column, options=options, context=context)
+
+class DecimalPrecisionTestModel(orm.Model):
+ _name = 'decimal.precision.test'
+
+ _columns = {
+ 'float': fields.float(),
+ 'float_2': fields.float(digits=(16, 2)),
+ 'float_4': fields.float(digits=(16, 4)),
+ }
+
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/decimal_precision/tests/__init__.py b/addons/decimal_precision/tests/__init__.py
new file mode 100644
index 00000000000..feee24c8f15
--- /dev/null
+++ b/addons/decimal_precision/tests/__init__.py
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+import test_qweb_float
+
+checks = [
+ test_qweb_float
+]
diff --git a/addons/decimal_precision/tests/test_qweb_float.py b/addons/decimal_precision/tests/test_qweb_float.py
new file mode 100644
index 00000000000..93334170f37
--- /dev/null
+++ b/addons/decimal_precision/tests/test_qweb_float.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+from openerp.tests import common
+
+class TestFloatExport(common.TransactionCase):
+ def setUp(self):
+ super(TestFloatExport, self).setUp()
+ self.Model = self.registry('decimal.precision.test')
+
+ def get_converter(self, name):
+ converter = self.registry('ir.qweb.field.float')
+ column = self.Model._all_columns[name].column
+
+ return lambda value, options=None: converter.value_to_html(
+ self.cr, self.uid, value, column, options=options, context=None)
+
+ def test_basic_float(self):
+ converter = self.get_converter('float')
+ self.assertEqual(
+ converter(42.0),
+ "42.0")
+ self.assertEqual(
+ converter(42.12345),
+ "42.12345")
+
+ converter = self.get_converter('float_2')
+ self.assertEqual(
+ converter(42.0),
+ "42.00")
+ self.assertEqual(
+ converter(42.12345),
+ "42.12")
+
+ converter = self.get_converter('float_4')
+ self.assertEqual(
+ converter(42.0),
+ '42.0000')
+ self.assertEqual(
+ converter(42.12345),
+ '42.1234')
+
+ def test_precision_domain(self):
+ DP = self.registry('decimal.precision')
+ DP.create(self.cr, self.uid, {
+ 'name': 'A',
+ 'digits': 2,
+ })
+ DP.create(self.cr, self.uid, {
+ 'name': 'B',
+ 'digits': 6,
+ })
+
+ converter = self.get_converter('float')
+ self.assertEqual(
+ converter(42.0, {'decimal_precision': 'A'}),
+ '42.00')
+ self.assertEqual(
+ converter(42.0, {'decimal_precision': 'B'}),
+ '42.000000')
+
+ converter = self.get_converter('float_4')
+ self.assertEqual(
+ converter(42.12345, {'decimal_precision': 'A'}),
+ '42.12')
+ self.assertEqual(
+ converter(42.12345, {'decimal_precision': 'B'}),
+ '42.123450')
diff --git a/addons/website/models/ir_qweb.py b/addons/website/models/ir_qweb.py
index f348bd7d9c3..948422e8296 100644
--- a/addons/website/models/ir_qweb.py
+++ b/addons/website/models/ir_qweb.py
@@ -39,12 +39,12 @@ class Field(orm.AbstractModel):
_inherit = 'ir.qweb.field'
def attributes(self, cr, uid, field_name, record, options,
- source_element, g_att, t_att, qweb_context):
+ source_element, g_att, t_att, qweb_context, context=None):
column = record._model._all_columns[field_name].column
return itertools.chain(
super(Field, self).attributes(cr, uid, field_name, record, options,
source_element, g_att, t_att,
- qweb_context),
+ qweb_context, context=context),
[('data-oe-translate', 1 if column.translate else 0)]
)
@@ -67,7 +67,27 @@ class Float(orm.AbstractModel):
_name = 'website.qweb.field.float'
_inherit = ['website.qweb.field', 'ir.qweb.field.float']
- value_from_string = float
+ def from_html(self, cr, uid, model, column, element, context=None):
+ lang = self.user_lang(cr, uid, context=context)
+
+ value = element.text_content().strip()
+
+ return float(value.replace(lang.thousands_sep, '')
+ .replace(lang.decimal_point, '.'))
+
+class Date(orm.AbstractModel):
+ _name = 'website.qweb.field.date'
+ _inherit = ['website.qweb.field', 'ir.qweb.field.date']
+
+ def from_html(self, cr, uid, model, column, element, context=None):
+ raise NotImplementedError("Can not parse and save localized dates")
+
+class DateTime(orm.AbstractModel):
+ _name = 'website.qweb.field.datetime'
+ _inherit = ['website.qweb.field', 'ir.qweb.field.datetime']
+
+ def from_html(self, cr, uid, model, column, element, context=None):
+ raise NotImplementedError("Can not parse and save localized datetimes")
class Text(orm.AbstractModel):
_name = 'website.qweb.field.text'
@@ -127,7 +147,7 @@ class Image(orm.AbstractModel):
_inherit = ['website.qweb.field', 'ir.qweb.field.image']
def to_html(self, cr, uid, field_name, record, options,
- source_element, t_att, g_att, qweb_context):
+ source_element, t_att, g_att, qweb_context, context=None):
assert source_element.nodeName != '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 " \
@@ -135,9 +155,9 @@ class Image(orm.AbstractModel):
return super(Image, self).to_html(
cr, uid, field_name, record, options,
- source_element, t_att, g_att, qweb_context)
+ source_element, t_att, g_att, qweb_context, context=context)
- def record_to_html(self, cr, uid, field_name, record, column, options=None):
+ def record_to_html(self, cr, uid, field_name, record, column, options=None, context=None):
cls = ''
if 'class' in options:
cls = ' class="%s"' % werkzeug.utils.escape(options['class'])
@@ -183,4 +203,9 @@ class Monetary(orm.AbstractModel):
_inherit = ['website.qweb.field', 'ir.qweb.field.monetary']
def from_html(self, cr, uid, model, column, element, context=None):
- return float(element.find('span').text)
+ lang = self.user_lang(cr, uid, context=context)
+
+ value = element.find('span').text.strip()
+
+ return float(value.replace(lang.thousands_sep, '')
+ .replace(lang.decimal_point, '.'))
diff --git a/addons/website/models/ir_ui_view.py b/addons/website/models/ir_ui_view.py
index d6ad607fa3f..c85245bb188 100644
--- a/addons/website/models/ir_ui_view.py
+++ b/addons/website/models/ir_ui_view.py
@@ -1,8 +1,10 @@
# -*- coding: utf-8 -*-
+from urlparse import urlparse
from lxml import etree, html
-from openerp.osv import osv, fields
+from openerp.osv import osv, fields
+from openerp.addons.base import ir
class view(osv.osv):
_inherit = "ir.ui.view"
@@ -82,15 +84,39 @@ class view(osv.osv):
return out
def replace_arch_section(self, cr, uid, view_id, section_xpath, replacement, context=None):
- arch = replacement
- if section_xpath:
- previous_arch = etree.fromstring(self.browse(cr, uid, view_id, context=context).arch.encode('utf-8'))
+ # remove branding from replacement section
+ for att in ir.ir_ui_view.MOVABLE_BRANDING:
+ replacement.attrib.pop(att, None)
+
+ if not section_xpath:
+ # replace all of the arch, not just a fragment
+ arch = replacement
+ else:
+ arch = etree.fromstring(self.browse(cr, uid, view_id, context=context).arch.encode('utf-8'))
# ensure there's only one match
- [previous_section] = previous_arch.xpath(section_xpath)
+ [previous_section] = arch.xpath(section_xpath)
+
previous_section.getparent().replace(previous_section, replacement)
- arch = previous_arch
+
return arch
+ def _normalize_urls(self, element):
+ attr = None
+ if element.tag == 'form':
+ attr = 'action' if 'action' in element.attrib else None
+ elif element.tag in ['a', 'link']:
+ attr = 'href' if 'href' in element.attrib else None
+ elif element.tag in ['frame', 'iframe', 'script']:
+ attr = 'src' if 'src' in element.attrib else None
+ if attr:
+ value = element.attrib[attr]
+ if not urlparse(value).scheme:
+ element.attrib.pop(attr)
+ element.attrib['t-' + attr] = value
+ for el in list(element):
+ self._normalize_urls(el)
+ return element
+
def save(self, cr, uid, res_id, value, xpath=None, context=None):
""" Update a view section. The view section may embed fields to write
@@ -103,6 +129,8 @@ class view(osv.osv):
arch_section = html.fromstring(
value, parser=html.HTMLParser(encoding='utf-8'))
+ self._normalize_urls(arch_section)
+
if xpath is None:
# value is an embedded field on its own, not a view section
self.save_embedded_field(cr, uid, arch_section, context=context)
diff --git a/addons/website/models/website.py b/addons/website/models/website.py
index c79ada6e9e4..b6379bcbb79 100644
--- a/addons/website/models/website.py
+++ b/addons/website/models/website.py
@@ -386,6 +386,17 @@ class base_language_install(osv.osv):
'website_ids': fields.many2many('website', string='Websites to translate'),
}
+ def default_get(self, cr, uid, fields, context=None):
+ if context is None:
+ context = {}
+ defaults = super(base_language_install, self).default_get(cr, uid, fields, context)
+ website_id = context.get('params', {}).get('website_id')
+ if website_id:
+ if 'website_ids' not in defaults:
+ defaults['website_ids'] = []
+ defaults['website_ids'].append(website_id)
+ return defaults
+
def lang_install(self, cr, uid, ids, context=None):
if context is None:
context = {}
diff --git a/addons/website/security/ir.model.access.csv b/addons/website/security/ir.model.access.csv
index 4fe9a0888b4..431170e462b 100644
--- a/addons/website/security/ir.model.access.csv
+++ b/addons/website/security/ir.model.access.csv
@@ -5,7 +5,10 @@ access_website_converter_test,access_website_converter_test,model_website_conver
access_website_converter_test_sub,access_website_converter_test_sub,model_website_converter_test_sub,,1,1,1,1
access_website_qweb,access_website_qweb,model_website_qweb,,0,0,0,0
access_website_qweb_field,access_website_qweb_field,model_website_qweb_field,,0,0,0,0
+access_website_qweb_field_integer,access_website_qweb_field_integer,model_website_qweb_field_integer,,0,0,0,0
access_website_qweb_field_float,access_website_qweb_field_float,model_website_qweb_field_float,,0,0,0,0
+access_website_qweb_field_date,access_website_qweb_field_date,model_website_qweb_field_date,,0,0,0,0
+access_website_qweb_field_datetime,access_website_qweb_field_datetime,model_website_qweb_field_datetime,,0,0,0,0
access_website_qweb_field_text,access_website_qweb_field_text,model_website_qweb_field_text,,0,0,0,0
access_website_qweb_field_selection,access_website_qweb_field_selection,model_website_qweb_field_selection,,0,0,0,0
access_website_qweb_field_many2one,access_website_qweb_field_many2one,model_website_qweb_field_many2one,,0,0,0,0
diff --git a/addons/website/static/src/js/website.editor.js b/addons/website/static/src/js/website.editor.js
index c1f05d9b661..59b8ec4fe0f 100644
--- a/addons/website/static/src/js/website.editor.js
+++ b/addons/website/static/src/js/website.editor.js
@@ -350,6 +350,7 @@
// The detail attribute indicates the number of times a mouse button has been pressed
// we just want the triple click
if (e.detail !== 3) { return; }
+ e.preventDefault();
// Get closest block-level element to the triple-clicked
// element (using ckeditor's block list because why not)
diff --git a/addons/website/tests/test_converter.py b/addons/website/tests/test_converter.py
index fb846342718..1086ec8e8d3 100644
--- a/addons/website/tests/test_converter.py
+++ b/addons/website/tests/test_converter.py
@@ -28,7 +28,7 @@ class TestConvertBack(common.TransactionCase):
e.setAttribute('t-field', field_value)
rendered = self.registry('website.qweb').render_tag_field(
- e, {'field': field_value}, '', ir_qweb.QWebContext({
+ e, {'field': field_value}, '', ir_qweb.QWebContext(self.cr, self.uid, {
'record': record,
}))
element = html.fromstring(
@@ -53,6 +53,7 @@ class TestConvertBack(common.TransactionCase):
def test_float(self):
self.field_roundtrip('float', 42.567890)
+ self.field_roundtrip('float', 324542.567890)
def test_numeric(self):
self.field_roundtrip('numeric', 42.77)
diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml
index 19ff01ba997..db34bf9a15e 100644
--- a/addons/website/views/website_templates.xml
+++ b/addons/website/views/website_templates.xml
@@ -111,7 +111,7 @@
-