From a32c1d3c8dfd3d4684f89a6181f3a093dde26ae6 Mon Sep 17 00:00:00 2001 From: fwi-odoo Date: Tue, 26 Jul 2016 12:06:35 +0200 Subject: [PATCH] [MIG] web_editor: migrate to new api --- addons/web_editor/__init__.py | 2 + addons/web_editor/__openerp__.py | 3 + addons/web_editor/controllers/__init__.py | 3 + addons/web_editor/controllers/main.py | 67 +++++++++---------- addons/web_editor/models/__init__.py | 2 + addons/web_editor/models/ir_attachment.py | 38 +++-------- addons/web_editor/models/ir_http.py | 10 +-- addons/web_editor/models/ir_qweb.py | 73 ++++++++------------- addons/web_editor/models/ir_translation.py | 10 +-- addons/web_editor/models/ir_ui_view.py | 72 +++++++++----------- addons/web_editor/models/test_models.py | 60 ++++++++--------- addons/web_editor/static/src/xml/editor.xml | 4 +- addons/web_editor/tests/__init__.py | 2 + addons/web_editor/tests/test_converter.py | 6 +- addons/web_editor/tests/test_ui.py | 7 +- addons/web_editor/views/iframe.xml | 4 +- addons/website/tests/test_views.py | 50 +++++++------- 17 files changed, 194 insertions(+), 219 deletions(-) diff --git a/addons/web_editor/__init__.py b/addons/web_editor/__init__.py index 03ab5da2fb6..d8b1dcd9413 100644 --- a/addons/web_editor/__init__.py +++ b/addons/web_editor/__init__.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + import controllers import models import tests diff --git a/addons/web_editor/__openerp__.py b/addons/web_editor/__openerp__.py index 69583b9f150..4b6cf3fcc8a 100644 --- a/addons/web_editor/__openerp__.py +++ b/addons/web_editor/__openerp__.py @@ -1,3 +1,6 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + { 'name': 'Web Editor', 'category': 'Hidden', diff --git a/addons/web_editor/controllers/__init__.py b/addons/web_editor/controllers/__init__.py index 8ee9bae18d9..f48716d3a69 100644 --- a/addons/web_editor/controllers/__init__.py +++ b/addons/web_editor/controllers/__init__.py @@ -1 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + import main diff --git a/addons/web_editor/controllers/main.py b/addons/web_editor/controllers/main.py index fa891ee296c..324b3caf564 100644 --- a/addons/web_editor/controllers/main.py +++ b/addons/web_editor/controllers/main.py @@ -1,14 +1,17 @@ # -*- coding: utf-8 -*- -from openerp.http import request, STATIC_CACHE -from openerp import http -import json -import io -from PIL import Image, ImageFont, ImageDraw -from openerp import tools +# Part of Odoo. See LICENSE file for full copyright and licensing details. + import cStringIO -import werkzeug.wrappers -import time +import io +import json import logging +import time +import werkzeug.wrappers +from PIL import Image, ImageFont, ImageDraw + +from odoo.http import request +from odoo import http, tools + logger = logging.getLogger(__name__) @@ -25,8 +28,6 @@ class Web_Editor(http.Controller): #------------------------------------------------------ @http.route('/web_editor/field/html', type='http', auth="user") def FieldTextHtml(self, model=None, res_id=None, field=None, callback=None, **kwargs): - cr, uid, context = request.cr, request.uid, request.context - kwargs.update( model=model, res_id=res_id, @@ -39,17 +40,16 @@ class Web_Editor(http.Controller): kwargs[k] = int(kwargs[k]) trans = dict( - lang=kwargs.get('lang', context.get('lang')), + lang=kwargs.get('lang', request.env.context.get('lang')), translatable=kwargs.get('translatable'), edit_translations=kwargs.get('edit_translations'), editable=kwargs.get('enable_editor')) - context.update(trans) kwargs.update(trans) record = None if model and kwargs.get('res_id'): - record = request.registry[model].browse(cr, uid, kwargs.get('res_id'), context) + record = request.env[model].with_context(trans).browse(kwargs.get('res_id')) kwargs.update(content=record and getattr(record, field) or "") @@ -141,23 +141,23 @@ class Web_Editor(http.Controller): # the upload argument doesn't allow us to access the files if more than # one file is uploaded, as upload references the first file # therefore we have to recover the files from the request object - Attachments = request.registry['ir.attachment'] # registry for the attachment table + Attachments = request.env['ir.attachment'] # registry for the attachment table uploads = [] message = None if not upload: # no image provided, storing the link and the image name name = url.split("/").pop() # recover filename - attachment_id = Attachments.create(request.cr, request.uid, { + attachment = Attachments.create({ 'name': name, 'type': 'url', 'url': url, 'public': True, 'res_model': 'ir.ui.view', - }, request.context) - uploads += Attachments.read(request.cr, request.uid, [attachment_id], ['name', 'mimetype', 'checksum', 'url'], request.context) + }) + uploads += attachment.read(['name', 'mimetype', 'checksum', 'url']) else: # images provided try: - attachment_ids = [] + attachments = request.env['ir.attachment'] for c_file in request.httprequest.files.getlist('upload'): data = c_file.read() try: @@ -172,15 +172,15 @@ class Web_Editor(http.Controller): except IOError, e: pass - attachment_id = Attachments.create(request.cr, request.uid, { + attachment = Attachments.create({ 'name': c_file.filename, 'datas': data.encode('base64'), 'datas_fname': c_file.filename, 'public': True, 'res_model': 'ir.ui.view', - }, request.context) - attachment_ids.append(attachment_id) - uploads += Attachments.read(request.cr, request.uid, attachment_ids, ['name', 'mimetype', 'checksum', 'url'], request.context) + }) + attachments += attachment + uploads += attachments.read(['name', 'mimetype', 'checksum', 'url']) except Exception, e: logger.exception("Failed to upload image to attachment") message = unicode(e) @@ -199,25 +199,26 @@ class Web_Editor(http.Controller): Returns a dict mapping attachments which would not be removed (if any) mapped to the views preventing their removal """ - cr, uid, context = request.cr, request.uid, request.context - Attachment = request.registry['ir.attachment'] - Views = request.registry['ir.ui.view'] + Attachment = attachments_to_remove = request.env['ir.attachment'] + Views = request.env['ir.ui.view'] - attachments_to_remove = [] # views blocking removal of the attachment removal_blocked_by = {} - for attachment in Attachment.browse(cr, uid, ids, context=context): + for attachment in Attachment.browse(ids): # in-document URLs are html-escaped, a straight search will not # find them url = tools.html_escape(attachment.local_url) - ids = Views.search(cr, uid, ["|", ('arch_db', 'like', '"%s"' % url), ('arch_db', 'like', "'%s'" % url)], context=context) + views = Views.search([ + "|", + ('arch_db', 'like', '"%s"' % url), + ('arch_db', 'like', "'%s'" % url) + ]) - if ids: - removal_blocked_by[attachment.id] = Views.read( - cr, uid, ids, ['name'], context=context) + if views: + removal_blocked_by[attachment.id] = views.read(['name']) else: - attachments_to_remove.append(attachment.id) + attachments_to_remove += attachment if attachments_to_remove: - Attachment.unlink(cr, uid, attachments_to_remove, context=context) + attachments_to_remove.unlink() return removal_blocked_by diff --git a/addons/web_editor/models/__init__.py b/addons/web_editor/models/__init__.py index e4dbfa5ad4f..feae3b90394 100644 --- a/addons/web_editor/models/__init__.py +++ b/addons/web_editor/models/__init__.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + import ir_attachment import ir_qweb import ir_ui_view diff --git a/addons/web_editor/models/ir_attachment.py b/addons/web_editor/models/ir_attachment.py index 135d82bc592..0c5f0e53de8 100644 --- a/addons/web_editor/models/ir_attachment.py +++ b/addons/web_editor/models/ir_attachment.py @@ -1,36 +1,18 @@ # -*- coding: utf-8 -*- -import openerp -from openerp.osv import osv, fields +# Part of Odoo. See LICENSE file for full copyright and licensing details. -import contextlib -from sys import maxint -from openerp.http import request -import datetime -import hashlib -import time -import os - -from openerp.tools import html_escape as escape, ustr, image_resize_and_sharpen, image_save_for_web -from PIL import Image -import cStringIO - -import logging -logger = logging.getLogger(__name__) +from odoo import api, models, fields -class ir_attachment(osv.osv): +class IrAttachment(models.Model): _inherit = "ir.attachment" - def _local_url_get(self, cr, uid, ids, name, arg, context=None): - result = {} - for attach in self.browse(cr, uid, ids, context=context): - if attach.url: - result[attach.id] = attach.url - else: - result[attach.id] = '/web/image/%s?unique=%s' % (attach.id, attach.checksum) - return result + local_url = fields.Char("Attachment URL", compute='_compute_local_url') - _columns = { - 'local_url': fields.function(_local_url_get, string="Attachment URL", type='char'), - } + @api.one + def _compute_local_url(self): + if self.url: + self.local_url = self.url + else: + self.local_url = '/web/image/%s?unique=%s' % (self.id, self.checksum) diff --git a/addons/web_editor/models/ir_http.py b/addons/web_editor/models/ir_http.py index c72fee55e83..238b488716e 100644 --- a/addons/web_editor/models/ir_http.py +++ b/addons/web_editor/models/ir_http.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- -from openerp.osv import orm -from openerp.http import request +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models +from odoo.http import request -class ir_http(orm.AbstractModel): +class IrHttp(models.AbstractModel): _inherit = 'ir.http' def _dispatch(self): @@ -13,4 +15,4 @@ class ir_http(orm.AbstractModel): request.context['edit_translations'] = True if request.context.get('lang') != "en_US" and 'translatable' not in request.context: request.context['translatable'] = True - return super(ir_http, self)._dispatch() + return super(IrHttp, self)._dispatch() diff --git a/addons/web_editor/models/ir_qweb.py b/addons/web_editor/models/ir_qweb.py index b1c0a165e44..ca16f9e86a9 100644 --- a/addons/web_editor/models/ir_qweb.py +++ b/addons/web_editor/models/ir_qweb.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + """ Web_editor-context rendering needs to add some metadata to rendered and allow to edit fields, as well as render a few fields differently. -Also, adds methods to convert values back to openerp models. +Also, adds methods to convert values back to Odoo models. """ import ast @@ -22,22 +24,19 @@ import pytz from dateutil import parser from lxml import etree, html from PIL import Image as I -import openerp.modules +import odoo.modules -from odoo import api - -import openerp -from openerp.osv import orm, fields -from openerp.tools import ustr, DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT -from openerp.tools import html_escape as escape -from openerp.addons.base.ir import ir_qweb +from odoo import api, models, fields +from odoo.tools import ustr +from odoo.tools import html_escape as escape +from odoo.addons.base.ir import ir_qweb REMOTE_CONNECTION_TIMEOUT = 2.5 logger = logging.getLogger(__name__) -class QWeb(orm.AbstractModel): +class QWeb(models.AbstractModel): """ QWeb object for rendering editor stuff """ _inherit = 'ir.qweb' @@ -69,7 +68,7 @@ class QWeb(orm.AbstractModel): #------------------------------------------------------ -class Field(orm.AbstractModel): +class Field(models.AbstractModel): _name = 'ir.qweb.field' _inherit = 'ir.qweb.field' @@ -98,14 +97,14 @@ class Field(orm.AbstractModel): return self.value_from_string(element.text_content().strip()) -class Integer(orm.AbstractModel): +class Integer(models.AbstractModel): _name = 'ir.qweb.field.integer' _inherit = 'ir.qweb.field.integer' value_from_string = int -class Float(orm.AbstractModel): +class Float(models.AbstractModel): _name = 'ir.qweb.field.float' _inherit = 'ir.qweb.field.float' @@ -117,7 +116,7 @@ class Float(orm.AbstractModel): .replace(lang.decimal_point, '.')) -class ManyToOne(orm.AbstractModel): +class ManyToOne(models.AbstractModel): _name = 'ir.qweb.field.many2one' _inherit = 'ir.qweb.field.many2one' @@ -146,7 +145,7 @@ class ManyToOne(orm.AbstractModel): return None -class Contact(orm.AbstractModel): +class Contact(models.AbstractModel): _name = 'ir.qweb.field.contact' _inherit = 'ir.qweb.field.contact' @@ -165,18 +164,7 @@ class Contact(orm.AbstractModel): return node and node.__html__() -def parse_fuzzy(in_format, value): - day_first = in_format.find('%d') < in_format.find('%m') - - if '%y' in in_format: - year_first = in_format.find('%y') < in_format.find('%d') - else: - year_first = in_format.find('%Y') < in_format.find('%d') - - return parser.parse(value, dayfirst=day_first, yearfirst=year_first) - - -class Date(orm.AbstractModel): +class Date(models.AbstractModel): _name = 'ir.qweb.field.date' _inherit = 'ir.qweb.field.date' @@ -192,11 +180,10 @@ class Date(orm.AbstractModel): if not value: return False - datetime.datetime.strptime(value, DEFAULT_SERVER_DATE_FORMAT) return value -class DateTime(orm.AbstractModel): +class DateTime(models.AbstractModel): _name = 'ir.qweb.field.datetime' _inherit = 'ir.qweb.field.datetime' @@ -205,13 +192,11 @@ class DateTime(orm.AbstractModel): attrs = super(DateTime, self).attributes(record, field_name, options, values) value = record[field_name] if isinstance(value, basestring): - value = datetime.datetime.strptime( - value, DEFAULT_SERVER_DATETIME_FORMAT) + value = fields.Datetime.from_string(value) if value: # convert from UTC (server timezone) to user timezone - value = fields.datetime.context_timestamp( - self._cr, self._uid, timestamp=value, context=self.env.context) - value = value.strftime(DEFAULT_SERVER_DATETIME_FORMAT) + value = fields.Datetime.context_timestamp(self, timestamp=value) + value = fields.Datetime.to_string(value) attrs['data-oe-original'] = value return attrs @@ -240,10 +225,10 @@ class DateTime(orm.AbstractModel): exc_info=True) # format back to string - return dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT) + return fields.Datetime.to_string(dt) -class Text(orm.AbstractModel): +class Text(models.AbstractModel): _name = 'ir.qweb.field.text' _inherit = 'ir.qweb.field.text' @@ -252,7 +237,7 @@ class Text(orm.AbstractModel): return html_to_text(element) -class Selection(orm.AbstractModel): +class Selection(models.AbstractModel): _name = 'ir.qweb.field.selection' _inherit = 'ir.qweb.field.selection' @@ -270,7 +255,7 @@ class Selection(orm.AbstractModel): value, selection)) -class HTML(orm.AbstractModel): +class HTML(models.AbstractModel): _name = 'ir.qweb.field.html' _inherit = 'ir.qweb.field.html' @@ -284,7 +269,7 @@ class HTML(orm.AbstractModel): return '\n'.join(content) -class Image(orm.AbstractModel): +class Image(models.AbstractModel): """ Widget options: @@ -361,7 +346,7 @@ class Image(orm.AbstractModel): if sep and sep != '/': rest.replace(sep, '/') - path = openerp.modules.get_module_resource( + path = odoo.modules.get_module_resource( match.group('module'), 'static', *(rest.split('/'))) if not path: @@ -403,7 +388,7 @@ class Image(orm.AbstractModel): return out.getvalue().encode('base64') -class Monetary(orm.AbstractModel): +class Monetary(models.AbstractModel): _name = 'ir.qweb.field.monetary' _inherit = 'ir.qweb.field.monetary' @@ -417,7 +402,7 @@ class Monetary(orm.AbstractModel): .replace(lang.decimal_point, '.')) -class Duration(orm.AbstractModel): +class Duration(models.AbstractModel): _name = 'ir.qweb.field.duration' _inherit = 'ir.qweb.field.duration' @@ -435,14 +420,14 @@ class Duration(orm.AbstractModel): return float(value) -class RelativeDatetime(orm.AbstractModel): +class RelativeDatetime(models.AbstractModel): _name = 'ir.qweb.field.relative' _inherit = 'ir.qweb.field.relative' # get formatting from ir.qweb.field.relative but edition/save from datetime -class QwebView(orm.AbstractModel): +class QwebView(models.AbstractModel): _name = 'ir.qweb.field.qweb' _inherit = 'ir.qweb.field.qweb' diff --git a/addons/web_editor/models/ir_translation.py b/addons/web_editor/models/ir_translation.py index df56cf41011..0dedc93cc84 100644 --- a/addons/web_editor/models/ir_translation.py +++ b/addons/web_editor/models/ir_translation.py @@ -3,14 +3,16 @@ from lxml import etree -from openerp import models, api -from openerp.tools.translate import encode, xml_translate, html_translate +from odoo import models, api +from odoo.tools.translate import encode, xml_translate, html_translate + def edit_translation_mapping(data): data = dict(data, model=data['name'].partition(',')[0]) return '%(value)s' % data -class ir_translation(models.Model): + +class IrTranslation(models.Model): _inherit = 'ir.translation' @api.model @@ -18,7 +20,7 @@ class ir_translation(models.Model): if self._context.get('edit_translations'): self.insert_missing(field, records) return edit_translation_mapping - return super(ir_translation, self)._get_terms_mapping(field, records) + return super(IrTranslation, self)._get_terms_mapping(field, records) @api.multi def save_html(self, value): diff --git a/addons/web_editor/models/ir_ui_view.py b/addons/web_editor/models/ir_ui_view.py index f770a40d980..bb6ff4b7411 100644 --- a/addons/web_editor/models/ir_ui_view.py +++ b/addons/web_editor/models/ir_ui_view.py @@ -1,40 +1,33 @@ # -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + import copy -import openerp -from openerp.exceptions import AccessError -from openerp.osv import osv from lxml import etree, html -from openerp import api + +from odoo.exceptions import AccessError +from odoo import api, models -class view(osv.osv): +class IrUiView(models.Model): _inherit = 'ir.ui.view' - @api.cr_uid_ids_context - def render(self, cr, uid, id_or_xml_id, values=None, engine='ir.qweb', context=None): - if isinstance(id_or_xml_id, list): - id_or_xml_id = id_or_xml_id[0] - if not values: - values = {} - if values.get('editable'): + @api.multi + def render(self, values=None, engine='ir.qweb'): + if values and values.get('editable'): try: - if not isinstance(id_or_xml_id, (int, long)): - if '.' not in id_or_xml_id: - raise ValueError('Invalid template id: %r' % (id_or_xml_id,)) - id_or_xml_id = self.get_view_id(cr, uid, id_or_xml_id, context=context) - self.check_access_rights(cr, uid, 'write') - self.check_access_rule(cr, uid, [id_or_xml_id], 'write', context=context) - + self.check_access_rights('write') + self.check_access_rule('write') except AccessError: values['editable'] = False - return super(view, self).render(cr, uid, id_or_xml_id, values=values, engine=engine, context=context) + return super(IrUiView, self).render(values=values, engine=engine) #------------------------------------------------------ # Save from html #------------------------------------------------------ - def extract_embedded_fields(self, cr, uid, arch, context=None): + @api.model + def extract_embedded_fields(self, arch): return arch.xpath('//*[@data-oe-model != "ir.ui.view"]') @api.model @@ -62,12 +55,12 @@ class view(osv.osv): return etree.tostring( arch_no_whitespace, encoding='unicode', pretty_print=True) - def replace_arch_section(self, cr, uid, view_id, section_xpath, replacement, context=None): + @api.multi + def replace_arch_section(self, section_xpath, replacement): # the root of the arch section shouldn't actually be replaced as it's # not really editable itself, only the content truly is editable. - - [view] = self.browse(cr, uid, [view_id], context=context) - arch = etree.fromstring(view.arch.encode('utf-8')) + self.ensure_one() + arch = etree.fromstring(self.arch.encode('utf-8')) # => get the replacement root if not section_xpath: root = arch @@ -84,7 +77,8 @@ class view(osv.osv): return arch - def to_field_ref(self, cr, uid, el, context=None): + @api.model + def to_field_ref(self, el): # filter out meta-information inserted in the document attributes = dict((k, v) for k, v in el.items() if not k.startswith('data-oe-')) @@ -94,34 +88,28 @@ class view(osv.osv): out.tail = el.tail return out - def save(self, cr, uid, res_id, value, xpath=None, context=None): + @api.multi + def save(self, value, xpath=None): """ Update a view section. The view section may embed fields to write - :param str model: - :param int res_id: :param str xpath: valid xpath to the tag to replace """ - res_id = int(res_id) - arch_section = html.fromstring( value, parser=html.HTMLParser(encoding='utf-8')) 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) + self.save_embedded_field(arch_section) return - for el in self.extract_embedded_fields(cr, uid, arch_section, context=context): - self.save_embedded_field(cr, uid, el, context=context) + for el in self.extract_embedded_fields(arch_section): + self.save_embedded_field(el) # transform embedded field back to t-field - el.getparent().replace(el, self.to_field_ref(cr, uid, el, context=context)) + el.getparent().replace(el, self.to_field_ref(el)) - arch = self.replace_arch_section(cr, uid, res_id, xpath, arch_section, context=context) - self.write(cr, uid, res_id, { - 'arch': self._pretty_arch(arch) - }, context=context) + for view in self: + arch = view.replace_arch_section(xpath, arch_section) + view.write({'arch': view._pretty_arch(arch)}) - view = self.browse(cr, openerp.SUPERUSER_ID, res_id, context=context) - if view.model_data_id: - view.model_data_id.write({'noupdate': True}) + self.sudo().mapped('model_data_id').write({'noupdate': True}) diff --git a/addons/web_editor/models/test_models.py b/addons/web_editor/models/test_models.py index 716e42a4dc3..c4ac2c748da 100644 --- a/addons/web_editor/models/test_models.py +++ b/addons/web_editor/models/test_models.py @@ -1,43 +1,41 @@ # -*- coding: utf-8 -*- -from openerp.osv import orm, fields +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models, fields -class test_converter(orm.Model): +class ConverterTest(models.Model): _name = 'web_editor.converter.test' # disable translation export for those brilliant field labels and values _translate = False - _columns = { - 'char': fields.char(), - 'integer': fields.integer(), - 'float': fields.float(), - 'numeric': fields.float(digits=(16, 2)), - 'many2one': fields.many2one('web_editor.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 "), - ]), - '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=u"Lorsqu'un pancake prend l'avion à destination de Toronto et " - u"qu'il fait une escale technique à St Claude, on dit:"), - 'html': fields.html(), - 'text': fields.text(), - } + char = fields.Char() + integer = fields.Integer() + float = fields.Float() + numeric = fields.Float(digits=(16, 2)) + many2one = fields.Many2one('web_editor.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 "), + ]) + 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=u"Lorsqu'un pancake prend l'avion à destination de Toronto et " + u"qu'il fait une escale technique à St Claude, on dit:") + html = fields.Html() + text = fields.Text() -class test_converter_sub(orm.Model): +class ConverterTestSub(models.Model): _name = 'web_editor.converter.test.sub' - _columns = { - 'name': fields.char(), - } + name = fields.Char() diff --git a/addons/web_editor/static/src/xml/editor.xml b/addons/web_editor/static/src/xml/editor.xml index ff22af71f3d..138c84da6f0 100644 --- a/addons/web_editor/static/src/xml/editor.xml +++ b/addons/web_editor/static/src/xml/editor.xml @@ -110,7 +110,7 @@ — or —
- +
@@ -238,7 +238,7 @@ - +