diff --git a/addons/website/models/__init__.py b/addons/website/models/__init__.py index 5fcab989d6c..af592ba012c 100644 --- a/addons/website/models/__init__.py +++ b/addons/website/models/__init__.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- -import ir_fields import view import website import ir_qweb + +import test_models diff --git a/addons/website/models/ir_fields.py b/addons/website/models/ir_fields.py deleted file mode 100644 index fbccf9c296d..00000000000 --- a/addons/website/models/ir_fields.py +++ /dev/null @@ -1,87 +0,0 @@ -# -*- coding: utf-8 -*- -from lxml import etree, html - -from openerp.osv import orm, fields -from openerp.tools import ustr - -class converter(orm.Model): - _inherit = 'ir.fields.converter' - - def _html_to_integer(self, cr, uid, model, column, value, context=None): - return int(value.text_content().strip()), [] - - def _html_to_float(self, cr, uid, model, column, value, context=None): - return float(value.text_content().strip()), [] - - def _html_to_passthrough(self, cr, uid, model, column, value, context=None): - return value.text_content().strip(), [] - - _html_to_char = _html_to_date = _html_to_datetime = _html_to_passthrough - - def _html_to_text(self, cr, uid, model, column, value, context=None): - return value.text_content(), [] - - def _html_to_selection(self, cr, uid, model, column, value, context=None): - text = value.text_content().strip() - - selection = column.reify(cr, uid, model, column, context=context) - for k, v in selection: - if isinstance(v, str): - v = ustr(v) - if text == v: - return k, [] - - warning = u"No value found for label %s in selection %s" % (text, selection) - # FIXME: ? - return False, [Warning(warning.encode('utf-8'))] - - def _html_to_many2one(self, cr, uid, model, column, value, context=None): - matches = self.pool[column._obj].name_search( - cr, uid, name=value.text_content().strip(), context=context) - # FIXME: more than one match, error reporting - return matches[0][0], [] - - def _html_to_html(self, cr, uid, model, column, value, context=None): - content = [] - if value.text: content.append(value.text) - content.extend(html.tostring(child) - for child in value.iterchildren(tag=etree.Element)) - return '\n'.join(content), [] - - -class test_converter(orm.Model): - _name = 'website.converter.test' - - _columns = { - 'char': fields.char(), - 'integer': fields.integer(), - 'float': fields.float(), - 'numeric': fields.float(digits=(16, 2)), - 'many2one': fields.many2one('website.converter.test.sub'), - 'binary': fields.binary(), - 'date': fields.date(), - 'datetime': fields.datetime(), - 'selection': fields.selection([ - (1, "réponse A"), - (2, "réponse B"), - (3, "réponse C"), - (4, "réponse D"), - ]), - 'selection_str': fields.selection([ - ('A', "Qu'il n'est pas arrivé à Toronto"), - ('B', "Qu'il était supposé arriver à Toronto"), - ('C', "Qu'est-ce qu'il fout ce maudit pancake, tabernacle ?"), - ('D', "La réponse D"), - ], string="Lorsqu'un pancake prend l'avion à destination de Toronto et " - "qu'il fait une escale technique à St Claude, on dit:"), - 'html': fields.html(), - 'text': fields.text(), - } - - -class test_converter_sub(orm.Model): - _name = 'website.converter.test.sub' - - _columns = { - 'name': fields.char(), - } diff --git a/addons/website/models/ir_qweb.py b/addons/website/models/ir_qweb.py index ec60ce9d0e2..dd6a8327433 100644 --- a/addons/website/models/ir_qweb.py +++ b/addons/website/models/ir_qweb.py @@ -9,8 +9,10 @@ Also, adds methods to convert values back to openerp models. import itertools import werkzeug.utils +from lxml import etree, html from openerp.osv import orm, fields +from openerp.tools import ustr class QWeb(orm.AbstractModel): """ QWeb object for rendering stuff in the website context @@ -37,26 +39,71 @@ class Field(orm.AbstractModel): [('data-oe-translate', 1 if column.translate else 0)] ) + def value_from_string(self, value): + return value + + def from_html(self, cr, uid, model, column, element, context=None): + return self.value_from_string(element.text_content().strip()) + +class Integer(orm.AbstractModel): + _name = 'website.qweb.field.integer' + _inherit = ['website.qweb.field'] + + value_from_string = int + class Float(orm.AbstractModel): _name = 'website.qweb.field.float' _inherit = ['website.qweb.field', 'ir.qweb.field.float'] + value_from_string = float + class Text(orm.AbstractModel): _name = 'website.qweb.field.text' _inherit = ['website.qweb.field', 'ir.qweb.field.text'] + def from_html(self, cr, uid, model, column, element, context=None): + return element.text_content() + class Selection(orm.AbstractModel): _name = 'website.qweb.field.selection' _inherit = ['website.qweb.field', 'ir.qweb.field.selection'] + def from_html(self, cr, uid, model, column, element, context=None): + value = element.text_content().strip() + selection = column.reify(cr, uid, model, column, context=context) + for k, v in selection: + if isinstance(v, str): + v = ustr(v) + if value == v: + return k + + raise ValueError(u"No value found for label %s in selection %s" % ( + value, selection)) + class ManyToOne(orm.AbstractModel): _name = 'website.qweb.field.many2one' _inherit = ['website.qweb.field', 'ir.qweb.field.many2one'] + def from_html(self, cr, uid, model, column, element, context=None): + # FIXME: this behavior is really weird, what if the user wanted to edit the name of the related thingy? Should m2os really be editable without a widget? + matches = self.pool[column._obj].name_search( + cr, uid, name=element.text_content().strip(), context=context) + # FIXME: no match? More than 1 match? + assert len(matches) == 1 + return matches[0][0] + class HTML(orm.AbstractModel): _name = 'website.qweb.field.html' _inherit = ['website.qweb.field', 'ir.qweb.field.html'] + def from_html(self, cr, uid, model, column, element, context=None): + content = [] + if element.text: content.append(element.text) + content.extend(html.tostring(child) + for child in element.iterchildren(tag=etree.Element)) + return '\n'.join(content) + + class Image(orm.AbstractModel): """ Widget options: diff --git a/addons/website/models/test_models.py b/addons/website/models/test_models.py new file mode 100644 index 00000000000..d028cdd9864 --- /dev/null +++ b/addons/website/models/test_models.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +from openerp.osv import orm, fields + +class test_converter(orm.Model): + _name = 'website.converter.test' + + _columns = { + 'char': fields.char(), + 'integer': fields.integer(), + 'float': fields.float(), + 'numeric': fields.float(digits=(16, 2)), + 'many2one': fields.many2one('website.converter.test.sub'), + 'binary': fields.binary(), + 'date': fields.date(), + 'datetime': fields.datetime(), + 'selection': fields.selection([ + (1, "réponse A"), + (2, "réponse B"), + (3, "réponse C"), + (4, "réponse D"), + ]), + 'selection_str': fields.selection([ + ('A', "Qu'il n'est pas arrivé à Toronto"), + ('B', "Qu'il était supposé arriver à Toronto"), + ('C', "Qu'est-ce qu'il fout ce maudit pancake, tabernacle ?"), + ('D', "La réponse D"), + ], string="Lorsqu'un pancake prend l'avion à destination de Toronto et " + "qu'il fait une escale technique à St Claude, on dit:"), + 'html': fields.html(), + 'text': fields.text(), + } + + +class test_converter_sub(orm.Model): + _name = 'website.converter.test.sub' + + _columns = { + 'name': fields.char(), + } diff --git a/addons/website/models/view.py b/addons/website/models/view.py index 6ddae129185..aaf25511fde 100644 --- a/addons/website/models/view.py +++ b/addons/website/models/view.py @@ -60,11 +60,9 @@ class view(osv.osv): field = el.get('data-oe-field') column = Model._all_columns[field].column - convert = self.pool['ir.fields.converter'].to_field( - cr, uid, Model, column, fromtype="html", context=context) - value, warnings = convert(el) - # FIXME: report error - if warnings: return + converter = self.pool['website.qweb'].get_converter_for( + el.get('data-oe-type')) + value = converter.from_html(cr, uid, Model, column, el) Model.write(cr, uid, [int(el.get('data-oe-id'))], { field: value diff --git a/addons/website/tests/test_converter.py b/addons/website/tests/test_converter.py index bd7d44906bd..b2f2b690f98 100644 --- a/addons/website/tests/test_converter.py +++ b/addons/website/tests/test_converter.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from collections import namedtuple from functools import partial from xml.dom.minidom import getDOMImplementation @@ -10,19 +9,10 @@ from openerp.tests import common impl = getDOMImplementation() document = impl.createDocument(None, None, None) -Request = namedtuple('Request', 'cr uid registry') -class RegistryProxy(object): - def __init__(self, func): - self.func = func - def __getitem__(self, name): - return self.func(name) - class TestConvertBack(common.TransactionCase): def setUp(self): super(TestConvertBack, self).setUp() - self.Converter = self.registry('ir.fields.converter') - def field_rountrip_result(self, field, value, expected): model = 'website.converter.test' Model = self.registry(model) @@ -36,7 +26,7 @@ class TestConvertBack(common.TransactionCase): field_value = 'record.%s' % field e.setAttribute('t-field', field_value) - rendered = self.registry('ir.qweb').render_tag_field( + rendered = self.registry('website.qweb').render_tag_field( e, {'field': field_value}, '', { 'record': record, }) @@ -44,12 +34,11 @@ class TestConvertBack(common.TransactionCase): rendered, parser=html.HTMLParser(encoding='utf-8')) column = Model._all_columns[field].column + converter = self.registry('website.qweb').get_converter_for( + element.get('data-oe-type')) - from_html = self.Converter.to_field( - self.cr, self.uid, model, column, 'html') - - value_back, warnings = from_html(element) - self.assertEqual(warnings, []) + value_back = converter.from_html( + self.cr, self.uid, model, column, element) if isinstance(expected, str): expected = expected.decode('utf-8') diff --git a/addons/website/tests/test_views.py b/addons/website/tests/test_views.py index 8fc112899f6..812981cd706 100644 --- a/addons/website/tests/test_views.py +++ b/addons/website/tests/test_views.py @@ -31,8 +31,8 @@ class TestViewSaving(common.TransactionCase): h.H3("Column 2"), h.UL( h.LI("Item 1"), - h.LI(h.SPAN("My Company", attrs(model='res.company', id=1, field='name'))), - h.LI(h.SPAN("+00 00 000 00 0 000", attrs(model='res.company', id=1, field='phone'))) + h.LI(h.SPAN("My Company", attrs(model='res.company', id=1, field='name', type='char'))), + h.LI(h.SPAN("+00 00 000 00 0 000", attrs(model='res.company', id=1, field='phone', type='char'))) )) ) self.view_id = self.registry('ir.ui.view').create(self.cr, self.uid, { @@ -46,14 +46,15 @@ class TestViewSaving(common.TransactionCase): self.cr, self.uid, self.arch, context=None) expect = [ - h.SPAN("My Company", attrs(model='res.company', id=1, field='name')), - h.SPAN("+00 00 000 00 0 000", attrs(model='res.company', id=1, field='phone')), + h.SPAN("My Company", attrs(model='res.company', id=1, field='name', type='char')), + h.SPAN("+00 00 000 00 0 000", attrs(model='res.company', id=1, field='phone', type='char')), ] for actual, expected in itertools.izip_longest(fields, expect): self.eq(actual, expected) def test_embedded_save(self): - embedded = h.SPAN("+00 00 000 00 0 000", attrs(model='res.company', id=1, field='phone')) + embedded = h.SPAN("+00 00 000 00 0 000", attrs( + model='res.company', id=1, field='phone', type='char')) self.registry('ir.ui.view').save_embedded_field(self.cr, self.uid, embedded) @@ -118,8 +119,8 @@ class TestViewSaving(common.TransactionCase): h.H3("Column 2"), h.UL( h.LI("Item 1"), - h.LI(h.SPAN("My Company", attrs(model='res.company', id=1, field='name'))), - h.LI(h.SPAN("+00 00 000 00 0 000", attrs(model='res.company', id=1, field='phone'))) + h.LI(h.SPAN("My Company", attrs(model='res.company', id=1, field='name', type='char'))), + h.LI(h.SPAN("+00 00 000 00 0 000", attrs(model='res.company', id=1, field='phone', type='char'))) )) )) @@ -137,8 +138,8 @@ class TestViewSaving(common.TransactionCase): h.H3("Column 2"), h.UL( h.LI("wob wob wob"), - h.LI(h.SPAN("Acme Corporation", attrs(model='res.company', id=1, field='name', expression="bob"))), - h.LI(h.SPAN("+12 3456789", attrs(model='res.company', id=1, field='phone', expression="edmund"))), + h.LI(h.SPAN("Acme Corporation", attrs(model='res.company', id=1, field='name', expression="bob", type='char'))), + h.LI(h.SPAN("+12 3456789", attrs(model='res.company', id=1, field='phone', expression="edmund", type='char'))), ) ), encoding='utf-8') View.save(self.cr, self.uid, res_id=self.view_id, value=replacement, @@ -173,7 +174,7 @@ class TestViewSaving(common.TransactionCase): node = html.tostring(h.SPAN( "Acme Corporation", - attrs(model='res.company', id=company_id, field="name", expression='bob'))) + attrs(model='res.company', id=company_id, field="name", expression='bob', type='char'))) self.registry('ir.ui.view').save(self.cr, self.uid, res_id=company_id,value=node) @@ -184,7 +185,9 @@ class TestViewSaving(common.TransactionCase): def test_field_tail(self): View = self.registry('ir.ui.view') replacement = ET.tostring( - h.LI(h.SPAN("+12 3456789", attrs(model='res.company', id=1, field='phone', expression="edmund")), + h.LI(h.SPAN("+12 3456789", attrs( + model='res.company', id=1, type='char', + field='phone', expression="edmund")), "whop whop" ), encoding="utf-8") View.save(self.cr, self.uid, res_id = self.view_id, value=replacement, @@ -203,7 +206,7 @@ class TestViewSaving(common.TransactionCase): h.H3("Column 2"), h.UL( h.LI("Item 1"), - h.LI(h.SPAN("My Company", attrs(model='res.company', id=1, field='name'))), + h.LI(h.SPAN("My Company", attrs(model='res.company', id=1, field='name', type='char'))), h.LI(h.SPAN({'t-field': "edmund"}), "whop whop"), )) )