From 6b074b372972456c1cbb84af7ec4c1de82193487 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Mon, 25 Mar 2019 16:11:25 +0100 Subject: [PATCH 1/2] web_ckeditor4: split default config --- web_ckeditor4/__manifest__.py | 2 +- web_ckeditor4/static/src/js/web_ckeditor4.js | 115 +++--------------- .../static/src/js/web_ckeditor4_config.js | 99 +++++++++++++++ web_ckeditor4/templates/assets.xml | 20 +++ web_ckeditor4/views/qweb.xml | 20 --- 5 files changed, 138 insertions(+), 118 deletions(-) create mode 100644 web_ckeditor4/static/src/js/web_ckeditor4_config.js create mode 100644 web_ckeditor4/templates/assets.xml delete mode 100644 web_ckeditor4/views/qweb.xml diff --git a/web_ckeditor4/__manifest__.py b/web_ckeditor4/__manifest__.py index 28825be1..530c6f9e 100644 --- a/web_ckeditor4/__manifest__.py +++ b/web_ckeditor4/__manifest__.py @@ -13,7 +13,7 @@ 'web', ], 'data': [ - 'views/qweb.xml', + 'templates/assets.xml', ], 'css': [ 'static/src/css/web_ckeditor4.css', diff --git a/web_ckeditor4/static/src/js/web_ckeditor4.js b/web_ckeditor4/static/src/js/web_ckeditor4.js index db327286..de070903 100644 --- a/web_ckeditor4/static/src/js/web_ckeditor4.js +++ b/web_ckeditor4/static/src/js/web_ckeditor4.js @@ -25,114 +25,35 @@ odoo.define('web_ckeditor4', function(require){ var core = require('web.core'); var session = require('web.session'); var formats = require('web.formats'); + var ckconfig = require('web_ckeditor4.config'); - var ckeditor_addFunction_org = CKEDITOR.tools.addFunction; - //this is a quite complicated way to kind of monkey patch the private - //method onDomReady of ckeditor's plugin wysiwigarea, which causes problems - //when the editor is about to be destroyed but because of OpenERP's - //architecture updated one last time with its current value - CKEDITOR.tools.addFunction = function(fn, scope) - { - if(scope && scope._ && scope._.attrChanges && scope._.detach) - { - var scope_reference = scope; - return ckeditor_addFunction_org(function() - { - var self = this, - self_arguments=arguments; - setTimeout(function() - { - if(self.editor) - { - fn.apply(self, self_arguments); - } - }, 0); - }, scope); - } - return ckeditor_addFunction_org(fn, scope); - }; - - CKEDITOR.on('dialogDefinition', function(e) - { - _.each(e.data.definition.contents, function(element) - { - if(!element || element.filebrowser!='uploadButton') - { - return - } - _.each(element.elements, function(element) - { - if(!element.onClick || element.type!='fileButton') - { - return - } - var onClick_org = element.onClick; - element.onClick = function(e1) - { - onClick_org.apply(this, arguments); - _.each(jQuery('#'+this.domId).closest('table') - .find('iframe').contents().find(':file') - .get(0).files, - function(file) - { - var reader = new FileReader(); - reader.onload = function(load_event) - { - CKEDITOR.tools.callFunction( - e.editor._.filebrowserFn, - load_event.target.result, - ''); - } - reader.readAsDataURL(file); - }); - return false; - } - }); - }); - }); - function filter_html(value, ckeditor_filter, ckeditor_writer) - { - var fragment = CKEDITOR.htmlParser.fragment.fromHtml(value); - ckeditor_filter.applyTo(fragment); - ckeditor_writer.reset(); - fragment.writeHtml(ckeditor_writer); - return ckeditor_writer.getHtml(); - }; - - var default_ckeditor_filter = new CKEDITOR.filter( - { - '*': - { - attributes: 'href,src,style,alt,width,height,dir', - styles: '*', - classes: '*', - }, - 'html head title meta style body p div span a h1 h2 h3 h4 h5 img br hr table tr th td ul ol li dd dt strong pre b i': true, - }); - var default_ckeditor_writer = new CKEDITOR.htmlParser.basicWriter(); var FieldCKEditor4 = core.form_widget_registry.get('text').extend({ ckeditor_config: function () { - return { - removePlugins: this._getRemovePlugins(), - removeButtons: this._getRemoveButtons(), - filebrowserImageUploadUrl: 'dummy', - extraPlugins: 'filebrowser', - // this is '#39' per default which screws up single quoted text in ${} - entities_additional: '' - }; + return { + removePlugins: this._getRemovePlugins(), + removeButtons: this._getRemoveButtons(), + filebrowserImageUploadUrl: 'dummy', + extraPlugins: 'filebrowser', + // this is '#39' per default which screws up single quoted text in ${} + entities_additional: '' + }; }, - ckeditor_filter: default_ckeditor_filter, - ckeditor_writer: default_ckeditor_writer, + ckeditor_filter: ckconfig.default_ckeditor_filter, + ckeditor_writer: ckconfig.default_ckeditor_writer, _getRemovePlugins: function () { return 'iframe,flash,forms,smiley,pagebreak,stylescombo'; }, _getRemoveButtons: function () { return ''; }, + init: function () { + this._super.apply(this, arguments); + this.editor_lang = session.user_context.lang.split('_')[0]; + }, start: function() { this._super.apply(this, arguments); - CKEDITOR.lang.load(session.user_context.lang.split('_')[0], 'en', function() {}); + CKEDITOR.lang.load(this.editor_lang, 'en', function() {}); }, initialize_content: function() { @@ -146,7 +67,7 @@ odoo.define('web_ckeditor4', function(require){ this.editor = CKEDITOR.replace(this.$el.get(0), _.extend( { - language: session.user_context.lang.split('_')[0], + language: this.editor_lang, on: { 'change': function() @@ -166,7 +87,7 @@ odoo.define('web_ckeditor4', function(require){ }, filter_html: function(value) { - return filter_html(value, this.ckeditor_filter, this.ckeditor_writer); + return ckconfig.filter_html(value, this.ckeditor_filter, this.ckeditor_writer); }, render_value: function() { diff --git a/web_ckeditor4/static/src/js/web_ckeditor4_config.js b/web_ckeditor4/static/src/js/web_ckeditor4_config.js new file mode 100644 index 00000000..8f02cfb4 --- /dev/null +++ b/web_ckeditor4/static/src/js/web_ckeditor4_config.js @@ -0,0 +1,99 @@ +/* -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV () +# All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################*/ + +odoo.define('web_ckeditor4.config', function (require) { + "use strict"; + + var ckeditor_addFunction_org = CKEDITOR.tools.addFunction; + // this is a quite complicated way to kind of monkey patch the private + // method onDomReady of ckeditor's plugin wysiwigarea, which causes problems + // when the editor is about to be destroyed but because of OpenERP's + // architecture updated one last time with its current value + CKEDITOR.tools.addFunction = function (fn, scope) { + if (scope && scope._ && scope._.attrChanges && scope._.detach) { + return ckeditor_addFunction_org(function () { + var self = this, + self_arguments = arguments; + setTimeout(function () { + if (self.editor) { + fn.apply(self, self_arguments); + } + }, 0); + }, scope); + } + return ckeditor_addFunction_org(fn, scope); + }; + + CKEDITOR.on('dialogDefinition', function (e) { + _.each(e.data.definition.contents, function (element) { + if (!element || element.filebrowser != 'uploadButton') { + return + } + _.each(element.elements, function (element) { + if (!element.onClick || element.type != 'fileButton') { + return + } + var onClick_org = element.onClick; + element.onClick = function (e1) { + onClick_org.apply(this, arguments); + _.each($('#' + this.domId).closest('table') + .find('iframe').contents().find(':file') + .get(0).files, + function (file) { + var reader = new FileReader(); + reader.onload = function (load_event) { + CKEDITOR.tools.callFunction( + e.editor._.filebrowserFn, + load_event.target.result, + ''); + } + reader.readAsDataURL(file); + }); + return false; + } + }); + }); + }); + var filter_html = function filter_html(value, ckeditor_filter, ckeditor_writer) { + var fragment = CKEDITOR.htmlParser.fragment.fromHtml(value); + ckeditor_filter.applyTo(fragment); + ckeditor_writer.reset(); + fragment.writeHtml(ckeditor_writer); + return ckeditor_writer.getHtml(); + }; + + var default_ckeditor_filter = new CKEDITOR.filter({ + '*': + { + attributes: 'href,src,style,alt,width,height,dir', + styles: '*', + classes: '*', + }, + 'html head title meta style body p div span a h1 h2 h3 h4 h5 img br hr table tr th td ul ol li dd dt strong pre b i': true, + }); + var default_ckeditor_writer = new CKEDITOR.htmlParser.basicWriter(); + return { + 'filter_html': filter_html, + 'default_ckeditor_filter': default_ckeditor_filter, + 'default_ckeditor_writer': default_ckeditor_writer + } +}); diff --git a/web_ckeditor4/templates/assets.xml b/web_ckeditor4/templates/assets.xml new file mode 100644 index 00000000..d3882fee --- /dev/null +++ b/web_ckeditor4/templates/assets.xml @@ -0,0 +1,20 @@ + + + - - From 9f289485935a90a6631f9fa76f360c7080321382 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Mon, 25 Mar 2019 16:24:20 +0100 Subject: [PATCH 2/2] web_ckeditor4: fix no re-init on form discard When you hit "cancel" button or when you navigate away from the form, for instance by clicking on the breadcrumb or on "edit translations", we have to remove the CKEditor widget. It happens the same if you click on a main menu item (eg: you are on sale order create form and you click on Sales -> Quotations to get back to the tree view). BUT then if you hit "create" Odoo's form machinery is not initializing the widget anymore, which really sounds inconsistent. If the widget is not initialized again it means that if CKEditor got destroyed there's no way to re-init again. Here we make sure that on create (no id on datarecord) if the editor is not initialized yet we force it. --- web_ckeditor4/static/src/js/web_ckeditor4.js | 42 +++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/web_ckeditor4/static/src/js/web_ckeditor4.js b/web_ckeditor4/static/src/js/web_ckeditor4.js index de070903..a976b349 100644 --- a/web_ckeditor4/static/src/js/web_ckeditor4.js +++ b/web_ckeditor4/static/src/js/web_ckeditor4.js @@ -49,12 +49,32 @@ odoo.define('web_ckeditor4', function(require){ init: function () { this._super.apply(this, arguments); this.editor_lang = session.user_context.lang.split('_')[0]; + this.view.on("load_record", this, this._on_load_record); }, start: function() { this._super.apply(this, arguments); CKEDITOR.lang.load(this.editor_lang, 'en', function() {}); }, + _on_load_record: function() { + /* Fix widget not re-initialized on form discard. + + When you hit "cancel" button or when you navigate away + from the form, for instance by clicking on the breadcrumb + or on "edit translations", we have to remove the CKEditor widget. + + BUT then if you hit "create" Odoo's form machinery is not initializing + the widget anymore (which really sounds inconsistent). + If the widget is not initialized again it means that if CKEditor + got destroyed there's no way to re-init again. + + Here we make sure that on create (no id on datarecord) + if the editor is not initialized yet we force it. + */ + if (!this.view.datarecord.id && !this.editor) { + this.initialize_content(); + } + }, initialize_content: function() { var self = this; @@ -62,8 +82,7 @@ odoo.define('web_ckeditor4', function(require){ if(!this.$el) { return; - } else if (!this.get('effective_readonly')) { - + } else if (!this.get('effective_readonly') && !this.editor) { this.editor = CKEDITOR.replace(this.$el.get(0), _.extend( { @@ -116,6 +135,9 @@ odoo.define('web_ckeditor4', function(require){ } } }, + destroy_content: function () { + this._cleanup_editor(); + }, undelegateEvents: function() { this._cleanup_editor(); @@ -123,35 +145,35 @@ odoo.define('web_ckeditor4', function(require){ }, _cleanup_editor: function() { - if(this.editor && this.editor.status != 'unloaded') + if(this.editor && this.editor.status == 'ready') { - var id = this.editor.id + CKEDITOR.remove(this.editor.name); + $('#cke_' + this.editor.name).remove(); this.editor.removeAllListeners(); this.editor.destroy(); this.editor = null; - $('.' + id).remove(); } }, destroy: function() { - this._cleanup_editor(); - this._super(); - }, - destroy_content: function() - { + this.view.off("load_record", this, this._on_load_record); this._cleanup_editor(); + this._super(); } }); + var FieldCKEditor4Raw = FieldCKEditor4.extend({ filter_html: function(value) { return value; } }); + core.form_widget_registry.add('text_ckeditor4', FieldCKEditor4); core.form_widget_registry.add('text_ckeditor4_raw', FieldCKEditor4Raw); core.form_widget_registry.add('text_html', FieldCKEditor4); core.form_widget_registry.add('html', FieldCKEditor4); + return { 'FieldCKEditor4': FieldCKEditor4, 'FieldCKEditor4Raw': FieldCKEditor4Raw