From ec988d412187877c086e01ea007c1ce07d7b1860 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Tue, 11 Apr 2017 16:23:18 +0200 Subject: [PATCH] [FIX] web(_editor): field widget html The field widget html wasn't implemented yet in the new views. A very basic implementation was done in addon web. It only handled the readonly mode so that the field were displayed in studio. This commit removes this basic implementation from web, and re- introduced the old one, converted, in addon web_editor. --- .../static/src/js/fields/abstract_field.js | 10 + .../web/static/src/js/fields/basic_fields.js | 18 -- .../static/src/js/fields/field_registry.js | 1 - .../src/js/views/form/form_controller.js | 5 + .../static/src/js/views/form/form_renderer.js | 8 + addons/web_editor/static/src/js/backend.js | 180 +++++++++++++++++- addons/web_editor/static/src/xml/backend.xml | 6 - .../static/tests/web_editor_tests.js | 66 ++++++- 8 files changed, 263 insertions(+), 31 deletions(-) diff --git a/addons/web/static/src/js/fields/abstract_field.js b/addons/web/static/src/js/fields/abstract_field.js index 66bf2aaabe7..f36418e3b8b 100644 --- a/addons/web/static/src/js/fields/abstract_field.js +++ b/addons/web/static/src/js/fields/abstract_field.js @@ -191,6 +191,16 @@ var AbstractField = Widget.extend({ */ activate: function () { }, + /** + * This function should be implemented by widgets that are not able to + * notify their environment when their value changes (maybe because their + * are not aware of the changes). It is called before saving, and should + * call _setValue() to notify the environment if the value changed. + * + * @abstract + */ + commitChanges: function () { + }, /** * this method is used to determine if the field value is set to a meaningful * value. This is useful to determine if a field should be displayed as empty diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index 15b8b3930df..af5d9bcf233 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -702,23 +702,6 @@ var FieldText = DebouncedField.extend({ }, }); -var FieldHtml = InputField.extend({ - supportedFieldTypes: ['html'], - text_to_html: function (text) { - var value = text || ""; - if (value.match(/^\s*$/)) { - value = '


'; - } else { - value = "

"+value.split(//).join("

")+"

"; - value = value.replace(/

<\/p>/g, '').replace('

', '

').replace('

', '

'); - } - return value; - }, - _render: function () { - this.$el.html(this.text_to_html(this.value)); - }, -}); - /** * Displays a handle to modify the sequence. */ @@ -2068,7 +2051,6 @@ return { FieldDomain: FieldDomain, FieldFloat: FieldFloat, FieldFloatTime: FieldFloatTime, - FieldHtml: FieldHtml, FieldInteger: FieldInteger, FieldMonetary: FieldMonetary, FieldPercentPie: FieldPercentPie, diff --git a/addons/web/static/src/js/fields/field_registry.js b/addons/web/static/src/js/fields/field_registry.js index 83d28c1649f..00423d670fb 100644 --- a/addons/web/static/src/js/fields/field_registry.js +++ b/addons/web/static/src/js/fields/field_registry.js @@ -20,7 +20,6 @@ registry .add('abstract', AbstractField) .add('input', basic_fields.InputField) .add('integer', basic_fields.FieldInteger) - .add('html', basic_fields.FieldHtml) .add('boolean', basic_fields.FieldBoolean) .add('date', basic_fields.FieldDate) .add('datetime', basic_fields.FieldDateTime) diff --git a/addons/web/static/src/js/views/form/form_controller.js b/addons/web/static/src/js/views/form/form_controller.js index 7435332ecf4..f97a93e14e8 100644 --- a/addons/web/static/src/js/views/form/form_controller.js +++ b/addons/web/static/src/js/views/form/form_controller.js @@ -214,6 +214,11 @@ var FormController = BasicController.extend({ saveRecord: function (options) { var self = this; options = options || {}; + + // some field widgets (e.g. 'html') can't detect (all) their changes, so + // we ask them to commit their current value before saving + this.renderer.commitChanges(); + var stayInEdit = 'stayInEdit' in options ? options.stayInEdit : false; var shouldReload = 'reload' in options ? options.reload : true; if (!this.model.isDirty(this.handle) && !this.model.isNew(this.handle)) { diff --git a/addons/web/static/src/js/views/form/form_renderer.js b/addons/web/static/src/js/views/form/form_renderer.js index 23ed3a08ad3..e2ec7b3de70 100644 --- a/addons/web/static/src/js/views/form/form_renderer.js +++ b/addons/web/static/src/js/views/form/form_renderer.js @@ -76,6 +76,14 @@ return BasicRenderer.extend({ } }); }, + /** + * Calls 'commitChanges' on all field widgets, so that they can notify the + * environment with their current value (useful for widgets that can't + * detect when their value changes, e.g. field 'html'). + */ + commitChanges: function () { + _.invoke(this.widgets, 'commitChanges'); + }, /** * returns the active tab pages for each notebook * diff --git a/addons/web_editor/static/src/js/backend.js b/addons/web_editor/static/src/js/backend.js index f7d50e1a6dd..7642588ca26 100644 --- a/addons/web_editor/static/src/js/backend.js +++ b/addons/web_editor/static/src/js/backend.js @@ -2,18 +2,188 @@ odoo.define('web_editor.backend', function (require) { 'use strict'; var AbstractField = require('web.AbstractField'); +var basic_fields = require('web.basic_fields'); var core = require('web.core'); var session = require('web.session'); var field_registry = require('web.field_registry'); +var transcoder = require('web_editor.transcoder'); + +var DebouncedField = basic_fields.DebouncedField; var QWeb = core.qweb; /** - * FieldTextHtml Widget - * Intended for FieldText widgets meant to display HTML content. This - * widget will instantiate an iframe with the editor summernote improved by odoo + * FieldTextHtmlSimple Widget + * Intended to display HTML content. This widget uses the summernote editor + * improved by odoo. + * + * FIXME: this field has a custom handling of the translation feature, which + * hasn't been re-introduced yet (because this feature hasn't been introduced + * yet in the fields in general) */ +var FieldTextHtmlSimple = DebouncedField.extend({ + className: 'oe_form_field oe_form_field_html_text', + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * Summernote doesn't notify for all changes (e.g. changing the background + * color). Moreover, we can't detect that this field looses the focus, so + * we can't notify the environment that the value may have changed at that + * moment. So we always send the current value before saving. + * + * @override + */ + commitChanges: function () { + this._setValue(this._getValue()); + }, + /** + * @override + */ + isSet: function () { + return this.value && this.value !== "


" && this.value.match(/\S/); + }, + /** + * Do not re-render this field if it was the origin of the onchange call. + * + * @override + */ + reset: function (record, event) { + this._reset(record, event); + if (!event || event.target !== this) { + if (this.mode === 'edit') { + this.$content.html(this._textToHtml(this.value)); + } else { + this._renderReadonly(); + } + } + return $.when(); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @private + * @returns {Object} the summernote configuration + */ + _getSummernoteConfig: function () { + var config = { + focus: false, + height: 180, + toolbar: [ + ['style', ['style']], + ['font', ['bold', 'italic', 'underline', 'clear']], + ['fontsize', ['fontsize']], + ['color', ['color']], + ['para', ['ul', 'ol', 'paragraph']], + ['table', ['table']], + ['insert', ['link', 'picture']], + ['history', ['undo', 'redo']] + ], + prettifyHtml: false, + styleWithSpan: false, + inlinemedia: ['p'], + lang: "odoo", + onChange: this._onInput.bind(this), + }; + if (this.getSession().debug) { + config.toolbar.splice(7, 0, ['view', ['codeview']]); + } + return config; + }, + /** + * @override + * @private + */ + _getValue: function () { + if (this.nodeOptions['style-inline']) { + transcoder.class_to_style(this.$content); + transcoder.font_to_img(this.$content); + } + return this.$content.html(); + }, + /** + * @override + * @private + */ + _renderEdit: function () { + this.$textarea = $('