[REF] web_editor, mass_mailing: new iframe mechanism

Since we reverted the saas-12.2 editor in favor of the 12.0 one,
we faced a dilemna regarding how the editor iframe is handled in
mass_mailing.

The 12.0 version was using a special controller to get the website
assets required for mass_mailing while the saas-12.2 version used
a completely different mechanism that was, obviously, not handled
by the 12.0 version.

We did not want to reintroduce the old controllers who were
considered to be a hack to load the assets in the mass_mailing
iframe. However, we were very cautious about changing the editor
itself to avoid introducing new bugs in the editor core of 13.0.

The approach we chose at the end, is to use the loadAsset mechanism
introduced in saas-12.2 to load the necessary assets and inject them
in the mass_mailing iframe. This did not reintroduce the controllers
nor did it require to change the code of the editor itself. However,
it required moderate changes to the wysiwyg interface.

Part of PR 35677.

Co-authored-by: Nicolas Bayet <nby@odoo.com>
Co-authored-by: Antoine Guenet <age@odoo.com>
Co-authored-by: Christophe Matthieu <chm@odoo.com>
Co-authored-by: David Monjoie <dmo@odoo.com>
This commit is contained in:
Gorash
2019-08-22 06:41:05 +00:00
committed by David Monjoie
co-authored by Nicolas Bayet Antoine Guenet David Monjoie
parent 1eb1c2b029
commit 56d0c1ed97
17 changed files with 483 additions and 159 deletions
@@ -92,35 +92,6 @@ var MassMailingFieldHtml = FieldHtml.extend({
// Private
//--------------------------------------------------------------------------
/**
* Add generateOptions options to change wysiwyg configuration:
* - Remove table menu in toolbar
* - Remove table popover
* - Add rules for isEditableNode, the TD must content a not TABLE node as
* children to be editable.
*
* @override
*/
_getWysiwygOptions: function () {
var options = this._super();
options.generateOptions = function (options) {
options.toolbar = _.filter(options.toolbar, function (item) {
return item[0] !== 'table';
});
delete options.popover.table;
var isEditableNode = options.isEditableNode;
options.isEditableNode = function (node) {
if (node.tagName === "TD" && !$(node).children('*:not(table):first').length) {
return false;
}
return isEditableNode.call(this, node);
};
return options;
};
return options;
},
/**
* Returns true if must force the user to choose a theme.
*
@@ -293,12 +264,12 @@ var MassMailingFieldHtml = FieldHtml.extend({
} else if ($old_layout.length) {
$contents = ($old_layout.hasClass('oe_structure') ? $old_layout : $old_layout.find('.oe_structure').first()).contents();
} else {
$contents = this.$content.find('.note-editable').contents();
$contents = this.$content.find('.o_editable').contents();
}
$newWrapperContent.append($contents);
this._switchImages(themeParams, $newWrapperContent);
this.$content.find('.note-editable').empty().append($newLayout);
this.$content.find('.o_editable').empty().append($newLayout);
$old_layout.remove();
if (firstChoice) {
@@ -326,6 +297,9 @@ var MassMailingFieldHtml = FieldHtml.extend({
if (this._isFromInline) {
this._fromInline();
}
if (this.snippetsLoaded) {
this._onSnippetsLoaded(this.snippetsLoaded);
}
this._super();
},
/**
@@ -334,6 +308,10 @@ var MassMailingFieldHtml = FieldHtml.extend({
*/
_onSnippetsLoaded: function (ev) {
var self = this;
if (!this.$content) {
this.snippetsLoaded = ev;
return;
}
var $snippetsSideBar = ev.data;
var $themes = $snippetsSideBar.find("#email_designer_themes").children();
var $snippets = $snippetsSideBar.find(".oe_snippet");
@@ -341,7 +319,6 @@ var MassMailingFieldHtml = FieldHtml.extend({
if (config.device.isMobile) {
$snippetsSideBar.hide();
console.log(this.$content[0]);
this.$content.attr('style', 'padding-left: 0px !important');
}
@@ -35,17 +35,23 @@
.dropdown-menu {
position: absolute;
bottom: -1px;
bottom: -2px;
top: -3px !important;
left: -5px !important;
right: 3px !important;
right: -2px !important;
margin: 0;
border-radius: 0;
overflow: auto;
background-color: $o-wysiwyg-bg-color;
z-index: 1050;
.dropdown-item {
&.show {
bottom: 31px !important;
top: -2px !important;
right: 3px !important;
}
.dropdown-item {
padding: 10px 10px;
&:first-child {
display: none;
@@ -182,6 +188,13 @@ body.o_force_mail_theme_choice {
overflow: initial;
}
#web_editor-toolbars .popover .note-color {
.note-back-color-preview .dropdown-toggle,
.note-fore-color-preview .dropdown-toggle {
height: $o-wysiwyg-toolbar-height - 1;
}
}
.oe_structure {
width: 100%;
}
@@ -56,7 +56,6 @@ td:not([align]) {
> .o_mail_wrapper {
width: 100%;
margin: 24px 0;
border-collapse: separate; // Allow the first and last td to have the
// same width
-3
View File
@@ -32,9 +32,6 @@
</template>
<template id="iframe_css_assets_edit">
<t t-call-assets="web.assets_common" t-js="false"/>
<t t-call-assets="web.assets_frontend" t-js="false"/>
<t t-call-assets="web_editor.assets_wysiwyg" t-js="false"/>
<t t-call-assets="mass_mailing.assets_mail_themes" t-js="false"/>
<t t-call-assets="mass_mailing.assets_mail_themes_edition" t-js="false"/>
</template>
@@ -1,9 +1,7 @@
odoo.define('web_editor.transcoder', function (require) {
odoo.define('web_editor.convertInline', function (require) {
'use strict';
var base = require('web_editor.base');
var rulesCache = [];
var FieldHtml = require('web_editor.field.html');
/**
* Returns the css rules which applies on an element, tweaked so that they are
@@ -14,8 +12,11 @@ var rulesCache = [];
*/
function getMatchedCSSRules(a) {
var i, r, k;
var doc = a.ownerDocument;
var rulesCache = a.ownerDocument._rulesCache || (a.ownerDocument._rulesCache = []);
if (!rulesCache.length) {
var sheets = document.styleSheets;
var sheets = doc.styleSheets;
for (i = sheets.length-1 ; i >= 0 ; i--) {
var rules;
// try...catch because browser may not able to enumerate rules for cross-domain sheets
@@ -187,11 +188,13 @@ function getMatchedCSSRules(a) {
* converted to images
*/
function fontToImg($editable) {
var fonts = odoo.__DEBUG__.services["wysiwyg.fonts"];
$editable.find('.fa').each(function () {
var $font = $(this);
var icon, content;
_.find(base.fontIcons, function (font) {
return _.find(base.getCssSelectors(font.parser), function (data) {
_.find(fonts.fontIcons, function (font) {
return _.find(fonts.getCssSelectors(font.parser), function (data) {
if ($font.is(data.selector.replace(/::?before/g, ''))) {
icon = data.names[0].split('-').shift();
content = data.css.match(/content:\s*['"]?(.)['"]?/)[1];
@@ -265,9 +268,6 @@ function applyOverDescendants(node, func) {
* @param {jQuery} $editable
*/
function classToStyle($editable) {
if (!rulesCache.length) {
getMatchedCSSRules($editable[0]);
}
applyOverDescendants($editable[0], function (node) {
var $target = $(node);
var css = getMatchedCSSRules(node);
@@ -328,9 +328,7 @@ function styleToClass($editable) {
$(this).after($('a,img', this));
}).remove();
getMatchedCSSRules($editable[0]);
var $c = $('<span/>').appendTo(document.body);
var $c = $('<span/>').appendTo($editable[0].ownerDocument.body);
applyOverDescendants($editable[0], function (node) {
var $target = $(node);
@@ -342,7 +340,7 @@ function styleToClass($editable) {
}
});
css = ($c.attr('style', style).attr('style') || '').split(/\s*;\s*/);
style = $target.attr('style') || '';
style = ($target.attr('style') || '').replace(/\s*:\s*/, ':').replace(/\s*;\s*/, ';');
_.each(css, function (v) {
style = style.replace(v, '');
});
@@ -364,13 +362,13 @@ function styleToClass($editable) {
* @param {jQuery} $editable
*/
function attachmentThumbnailToLinkImg($editable) {
$editable.find('a[href*="/web/content/"][data-mimetype]:empty').each(function () {
$editable.find('a[href*="/web/content/"][data-mimetype]').filter(':empty, :containsExact( )').each(function () {
var $link = $(this);
var $img = $('<img/>')
.attr('src', $link.css('background-image').replace(/(^url\(['"])|(['"]\)$)/g, ''))
.css('height', Math.max(1, $link.height()) + 'px')
.css('width', Math.max(1, $link.width()) + 'px');
$link.append($img);
$link.prepend($img);
});
}
@@ -384,6 +382,83 @@ function linkImgToAttachmentThumbnail($editable) {
$editable.find('a[href*="/web/content/"][data-mimetype] > img').remove();
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
FieldHtml.include({
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
/**
* @override
*/
commitChanges: function () {
if (this.nodeOptions['style-inline'] && this.mode === "edit") {
this._toInline();
}
return this._super();
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* Converts CSS dependencies to CSS-independent HTML.
* - CSS display for attachment link -> real image
* - Font icons -> images
* - CSS styles -> inline styles
*
* @private
*/
_toInline: function () {
var $editable = this.wysiwyg.getEditable();
var html = this.wysiwyg.getValue();
$editable.html(html);
attachmentThumbnailToLinkImg($editable);
fontToImg($editable);
classToStyle($editable);
this.wysiwyg.setValue($editable.html(), {
notifyChange: false,
});
},
/**
* Revert _toInline changes.
*
* @private
*/
_fromInline: function () {
var $editable = this.wysiwyg.getEditable();
var html = this.wysiwyg.getValue();
$editable.html(html);
styleToClass($editable);
imgToFont($editable);
linkImgToAttachmentThumbnail($editable);
this.wysiwyg.setValue($editable.html(), {
notifyChange: false,
});
},
//--------------------------------------------------------------------------
// Handler
//--------------------------------------------------------------------------
/**
* @override
*/
_onLoadWysiwyg: function () {
if (this.nodeOptions['style-inline'] && this.mode === "edit") {
this._fromInline();
}
this._super();
},
});
return {
fontToImg: fontToImg,
imgToFont: imgToFont,
@@ -392,4 +467,4 @@ return {
attachmentThumbnailToLinkImg: attachmentThumbnailToLinkImg,
linkImgToAttachmentThumbnail: linkImgToAttachmentThumbnail,
};
});
});
@@ -179,8 +179,6 @@ var FieldHtml = basic_fields.DebouncedField.extend(TranslatableFieldMixin, {
* @returns {Object}
*/
_getWysiwygOptions: function () {
var iPhone = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
return Object.assign({}, this.nodeOptions, {
recordInfo: {
context: this.record.getContext(this.recordParams),
@@ -193,6 +191,7 @@ var FieldHtml = basic_fields.DebouncedField.extend(TranslatableFieldMixin, {
snippets: this.nodeOptions.snippets,
tabsize: 0,
height: 180,
generateOptions: function (options) {
var para = _.find(options.toolbar, function (item) {
return item[0] === 'para';
@@ -6,6 +6,7 @@ var Widget = require('web.Widget');
var core = require('web.core');
var rte = require('web_editor.rte');
var snippetsEditor = require('web_editor.snippet.editor');
var summernoteCustomColors = require('web_editor.rte.summernote_custom_colors');
var _t = core._t;
@@ -26,7 +27,7 @@ var EditorMenuBar = Widget.extend({
*
* @constructor
*/
init: function (parent) {
init: function (parent, options) {
var self = this;
var res = this._super.apply(this, arguments);
var Editor = options.Editor || rte.Class;
@@ -48,8 +49,6 @@ var EditorMenuBar = Widget.extend({
var $editable = this.rte.editable();
window.__EditorMenuBar_$editable = $editable; // TODO remove this hack asap
var options = this.getParent().params;
if (options.snippets) {
this.snippetsMenu = new snippetsEditor.Class(this, Object.assign({
$el: $editable,
@@ -185,6 +184,32 @@ var EditorMenuBar = Widget.extend({
// Private
//--------------------------------------------------------------------------
/**
* @private
*/
_getDefaultConfig: function ($editable) {
return {
'airMode' : true,
'focus': false,
'airPopover': [
['style', ['style']],
['font', ['bold', 'italic', 'underline', 'clear']],
['fontsize', ['fontsize']],
['color', ['color']],
['para', ['ul', 'ol', 'paragraph']],
['table', ['table']],
['insert', ['link', 'picture']],
['history', ['undo', 'redo']],
],
'styleWithSpan': false,
'inlinemedia' : ['p'],
'lang': 'odoo',
'onChange': function (html, $editable) {
$editable.trigger('content_changed');
},
'colors': summernoteCustomColors,
};
},
/**
* Reloads the page in non-editable mode, with the right scrolling.
*
@@ -67,7 +67,7 @@ var SnippetEditor = Widget.extend({
defs.push(this._initializeOptions());
// Initialize move/clone/remove buttons
if (!this.options.isEditableNode(this.$target[0])) {
if (!this.$target.is(':o_editable')) {
this.$el.find('.oe_snippet_move, .oe_snippet_clone, .oe_snippet_remove').remove();
} else {
this.dropped = false;
@@ -1249,6 +1249,7 @@ var SnippetsMenu = Widget.extend({
this.$el.addClass('o_loaded');
$('body.editor_enable').addClass('editor_has_snippets');
this.trigger_up('snippets_loaded', self.$el);
},
/**
* Creates a snippet editor to associated to the given snippet. If the given
@@ -47,7 +47,7 @@ var WysiwygRoot = Widget.extend({
});
return instance.attachTo($target).then(function () {
self.$editor = instance.$el;
self.$editor = instance.$editor || instance.$el;
});
});
},
@@ -2,7 +2,6 @@ odoo.define('web_editor.wysiwyg', function (require) {
'use strict';
var Widget = require('web.Widget');
var SummernoteManager = require('web_editor.rte.summernote');
var transcoder = require('web_editor.transcoder');
var summernoteCustomColors = require('web_editor.rte.summernote_custom_colors');
var id = 0;
@@ -32,28 +31,26 @@ var Wysiwyg = Widget.extend({
},
},
/**
* @params {Object} params
* @params {Object} params.recordInfo
* @params {Object} params.recordInfo.context
* @params {String} [params.recordInfo.context]
* @params {integer} [params.recordInfo.res_id]
* @params {String} [params.recordInfo.data_res_model]
* @params {integer} [params.recordInfo.data_res_id]
* @options {Object} options
* @options {Object} options.recordInfo
* @options {Object} options.recordInfo.context
* @options {String} [options.recordInfo.context]
* @options {integer} [options.recordInfo.res_id]
* @options {String} [options.recordInfo.data_res_model]
* @options {integer} [options.recordInfo.data_res_id]
* @see _onGetRecordInfo
* @see _getAttachmentsDomain in /wysiwyg/widgets/media.js
* @params {Object} params.attachments
* @options {Object} options.attachments
* @see _onGetRecordInfo
* @see _getAttachmentsDomain in /wysiwyg/widgets/media.js (for attachmentIDs)
* @params {function} params.generateOptions
* @options {function} options.generateOptions
* called with the summernote configuration object used before sending to summernote
* @see _editorOptions
**/
init: function (parent, params) {
init: function (parent, options) {
this._super.apply(this, arguments);
this.params = params;
this.params.isEditableNode = function (node) {
return $(node).is(':o_editable');
};
this.id = ++id;
this.options = options;
},
/**
* Load assets and color picker template then call summernote API
@@ -64,14 +61,13 @@ var Wysiwyg = Widget.extend({
willStart: function () {
new SummernoteManager(this);
this.$target = this.$el;
return this._super.apply(this, arguments);
return this._super();
},
/**
*
* @override
*/
start: function () {
var self = this;
this.$target.wrap('<odoo-wysiwyg-container>');
this.$el = this.$target.parent();
var options = this._editorOptions();
@@ -80,7 +76,6 @@ var Wysiwyg = Widget.extend({
this.$editor.data('wysiwyg', this);
this.$editor.data('oe-model', options.recordInfo.res_model);
this.$editor.data('oe-id', options.recordInfo.res_id);
var $wysiwyg = this.$editor.closest('odoo-wysiwyg-container');
$(document).on('mousedown', this._blur);
this._value = this.$target.html() || this.$target.val();
return this._super.apply(this, arguments).then(() => {
@@ -106,7 +101,7 @@ var Wysiwyg = Widget.extend({
* @returns {jQuery}
*/
getEditable: function () {
console.log('getEditable');
return this.$editor;
},
/**
* Return true if the content has changed.
@@ -116,27 +111,6 @@ var Wysiwyg = Widget.extend({
isDirty: function () {
return this._value !== (this.$editor.html() || this.$editor.val());
},
/**
* Return true if the current node is unbreakable.
* An unbreakable node can be removed or added but can't by split into
* different nodes (for keypress and selection).
* An unbreakable node can contain nodes that can be edited.
*
* @param {Node} node
* @returns {Boolean}
*/
isUnbreakableNode: function (node) {
console.log('isUnbreakableNode');
},
/**
* Return true if the current node is editable (for keypress and selection).
*
* @param {Node} node
* @returns {Boolean}
*/
isEditableNode: function (node) {
console.log('isEditableNode');
},
/**
* Set the focus on the element.
*/
@@ -193,12 +167,6 @@ var Wysiwyg = Widget.extend({
this.$target.html(value);
}
this.$editor.html(value);
if (this.params['style-inline']) {
transcoder.styleToClass(this.$editor);
transcoder.imgToFont(this.$editor);
transcoder.linkImgToAttachmentThumbnail(this.$editor);
}
this._value = (this.$target.html() || this.$target.val());
},
//--------------------------------------------------------------------------
// Private
@@ -246,8 +214,21 @@ Wysiwyg.getRange = function (node) {
eo: range.eo,
};
};
Wysiwyg.setRange = function () {
console.log('setRange');
/**
* @param {Node} startNode
* @param {Number} startOffset
* @param {Node} endNode
* @param {Number} endOffset
*/
Wysiwyg.setRange = function (startNode, startOffset, endNode, endOffset) {
$(startNode).focus();
if (endNode) {
$.summernote.core.range.create(startNode, startOffset, endNode, endOffset).select();
} else {
$.summernote.core.range.create(startNode, startOffset).select();
}
// trigger for Unbreakable
$(startNode.tagName ? startNode : startNode.parentNode).trigger('wysiwyg.range');
};
/**
* @param {Node} node - dom node
@@ -0,0 +1,136 @@
odoo.define('web_editor.wysiwyg.iframe', function (require) {
'use strict';
var Wysiwyg = require('web_editor.wysiwyg');
var ajax = require('web.ajax');
var core = require('web.core');
var qweb = core.qweb;
var promiseCommon;
var promiseWysiwyg;
/**
* Add option (inIframe) to load Wysiwyg in an iframe.
**/
Wysiwyg.include({
/**
* Add options to load Wysiwyg in an iframe.
*
* @override
* @param {boolean} options.inIframe
**/
init: function (parent, options) {
this._super.apply(this, arguments);
if (this.options.inIframe) {
if (!this.options.iframeCssAssets) {
this.options.iframeCssAssets = 'web_editor.wysiwyg_iframe_css_assets';
}
this._onUpdateIframeId = 'onLoad_' + this.id;
}
},
/**
* Load assets to inject into iframe.
*
* @override
**/
willStart: function () {
if (!this.options.inIframe) {
return this._super();
}
var debug = odoo.debug;
odoo.debug = false;
var defAsset;
if (this.options.iframeCssAssets) {
defAsset = ajax.loadAsset(this.options.iframeCssAssets);
} else {
defAsset = Promise.resolve({
cssLibs: [],
cssContents: []
});
}
promiseWysiwyg = promiseWysiwyg || ajax.loadAsset('web_editor.wysiwyg_iframe_editor_assets');
odoo.debug = debug;
this.defAsset = Promise.all([promiseWysiwyg, defAsset]);
this.$target = this.$el;
return this.defAsset
.then(this._loadIframe.bind(this))
.then(this._super.bind(this));
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* Create iframe, inject css and create a link with the content,
* then inject the target inside.
*
* @private
* @returns {Promise}
*/
_loadIframe: function () {
var self = this;
this.$iframe = $('<iframe class="wysiwyg_iframe">').css({
'min-height': '400px',
width: '100%'
});
var avoidDoubleLoad = 0; // this bug only appears on some configurations.
// resolve promise on load
var def = new Promise(function (resolve) {
window.top[self._onUpdateIframeId] = function (Editor, _avoidDoubleLoad) {
if (_avoidDoubleLoad !== avoidDoubleLoad) {
console.warn('Wysiwyg iframe double load detected');
return;
}
delete window.top[self._onUpdateIframeId];
var $iframeTarget = self.$iframe.contents().find('#iframe_target');
$iframeTarget.find('.o_editable').html(self.$target.val());
self.options.toolbarHandler = $('#web_editor-top-edit', self.$iframe[0].contentWindow.document);
$(qweb.render('web_editor.FieldTextHtml.fullscreen'))
.appendTo(self.options.toolbarHandler)
.on('click', '.o_fullscreen', function () {
$("body").toggleClass("o_field_widgetTextHtml_fullscreen");
var full = $("body").hasClass("o_field_widgetTextHtml_fullscreen");
self.$iframe.parents().toggleClass('o_form_fullscreen_ancestor', full);
$(window).trigger("resize"); // induce a resize() call and let other backend elements know (the navbar extra items management relies on this)
});
self.Editor = Editor;
resolve();
};
});
this.$iframe.data('loadDef', def); // for unit test
// inject content in iframe
this.$iframe.on('load', function onLoad (ev) {
var _avoidDoubleLoad = ++avoidDoubleLoad;
self.defAsset.then(function (assets) {
if (_avoidDoubleLoad !== avoidDoubleLoad) {
console.warn('Wysiwyg immediate iframe double load detected');
return;
}
var iframeContent = qweb.render('wysiwyg.iframeContent', {
assets: assets,
updateIframeId: self._onUpdateIframeId,
avoidDoubleLoad: _avoidDoubleLoad
});
self.$iframe[0].contentWindow.document
.open("text/html", "replace")
.write(iframeContent);
});
});
this.$iframe.insertAfter(this.$target);
return def;
},
});
});
@@ -0,0 +1,33 @@
odoo.define('web_editor.wysiwyg.snippets', function (require) {
'use strict';
var editor = require('web_editor.editor');
var Wysiwyg = require('web_editor.wysiwyg');
Wysiwyg.include({
init: function (parent, options) {
this._super.apply(this, arguments);
this.Editor = editor.Class;
if (!this.options.toolbarHandler) {
this.options.toolbarHandler = $('#web_editor-top-edit');
}
},
start: async function () {
if (this.options.snippets) {
var self = this;
this.editor = new (this.Editor)(this, this.options);
this.$editor = this.editor.rte.editable();
await this.editor.prependTo(this.$editor[0].ownerDocument.body);
this.options.toolbarHandler.append(this.editor.$el);
this.$el.on('content_changed', function (e) {
var $editable = $(e.target).closest('[data-oe-model], .o_editable');
$editable.addClass('o_dirty');
self.trigger_up('wysiwyg_change');
});
} else {
return this._super();
}
},
});
});
+60 -20
View File
@@ -1,5 +1,37 @@
// NOTE EDITOR
body.editor_enable {
padding-top: $o-wysiwyg-toolbar-height !important;
}
#wrapwrap {
height: calc(100vh - #{$o-wysiwyg-toolbar-height});
border: 1px solid $o-wysiwyg-border-color;
border-left: 0;
border-top: 0;
.btn {
-webkit-user-select: none;
}
}
// TOP BAR (EDIT)
#web_editor-top-edit {
height: $o-wysiwyg-toolbar-height;
background-color: $o-wysiwyg-bg-color;
z-index: $o-wysiwyg-zindex - 2;
position: fixed;
top: 0px;
right: 0px;
left: 0px;
white-space: nowrap;
border: 1px solid $o-wysiwyg-border-color;
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
transition: background-color 400ms $o-we-md-ease 0s;
font-family: $o-we-font-family;
}
.note-editor.note-frame {
background-color: inherit;
@@ -15,25 +47,36 @@
}
}
.note-popover.popover {
z-index: $o-wysiwyg-zindex;
height: auto;
background-color: $o-wysiwyg-bg-color;
.popover-content {
padding-bottom: 0;
max-width: 334px;
>note-btn-group {
white-space: nowrap;
}
}
.note-editor:not(.note-frame) .note-editing-area {
overflow: visible;
}
.note-editor .note-toolbar {
white-space: nowrap;
z-index: $o-wysiwyg-zindex + 10;
height: $o-wysiwyg-toolbar-height;
#web_editor-top-edit .note-popover .popover {
height: $o-wysiwyg-toolbar-height - 2;
top: 0 !important;
left: 0 !important;
padding: 0;
margin: 0;
border: 0;
width: auto;
text-align: center;
box-shadow: none;
background-color: $o-wysiwyg-bg-color;
z-index: $o-wysiwyg-zindex;
.popover-body {
height: $o-wysiwyg-toolbar-height - 2;
> .btn-group {
height: $o-wysiwyg-toolbar-height - 2;
margin-top: 0;
}
.btn {
height: $o-wysiwyg-toolbar-height - 2;
border-top: 0;
}
}
}
#web_editor-toolbars .popover,
@@ -57,12 +100,10 @@
}
.btn {
height: $o-wysiwyg-toolbar-height;
border: none;
border-radius: 0;
background-color: inherit;
color: inherit;
border-top: 2px solid transparent;
transition: all 0.3s ease 0s;
>.fa {
@@ -84,7 +125,6 @@
}
&.active {
border-top: 2px solid white;
background-color: inherit;
color: inherit;
box-shadow: inset 0 3px 10px rgba(0, 0, 0, 0.5);
@@ -16,7 +16,7 @@ iframe.wysiwyg_iframe.o_fullscreen {
body.o_in_iframe {
background-color: $o-view-background-color;
overflow: hidden;
overflow: auto;
.note-editable {
height: calc(100vh - #{$o-wysiwyg-toolbar-height} - 2px);
@@ -25,4 +25,12 @@ body.o_in_iframe {
.note-statusbar {
display: none;
}
#web_editor-toolbars .popover .btn {
font-size: 1rem;
}
.o_mail_theme_selector .btn {
font-size: 1rem;
}
}
@@ -13,6 +13,14 @@ $o-wysiwyg-handle-border-width: 1px !default;
$o-wysiwyg-handles-offset-to-hide: 10000px !default;
// OVERRIDES FOR EDITOR WITH SNIPPETS
body.editor_enable.editor_has_snippets {
padding-left: $o-we-sidebar-width !important;
#web_editor-top-edit .note-popover .popover {
margin: 0 0 0 $o-we-sidebar-width;
}
}
// NOTE EDITOR
.note-editor.o_snippets_loaded {
@@ -38,8 +46,8 @@ $o-wysiwyg-handles-offset-to-hide: 10000px !default;
display: flex;
flex-flow: column nowrap;
@include o-position-absolute(0, auto, 0, -$o-wysiwyg-sidebar-width);
position: absolute;
position: fixed;
top: 0;
width: $o-wysiwyg-sidebar-width;
z-index: $o-wysiwyg-zindex - 1;
@@ -52,7 +60,7 @@ $o-wysiwyg-handles-offset-to-hide: 10000px !default;
#snippets_menu {
flex: 0 0 auto;
height: $o-wysiwyg-sidebar-height;
height: $o-wysiwyg-toolbar-height;
line-height: $o-wysiwyg-sidebar-height;
margin: 0;
padding: 0;
@@ -341,6 +349,7 @@ $o-wysiwyg-handles-offset-to-hide: 10000px !default;
// MANIPULATORS
#oe_manipulators {
position: relative;
z-index: 1;
// SNIPPET MANIPULATORS
.oe_overlay {
+46 -22
View File
@@ -9,32 +9,56 @@
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"/>
<t t-foreach="asset.cssLibs || []" t-as="cssLib">
<link type="text/css" rel="stylesheet" t-att-href="cssLib"/>
<t t-foreach="assets || []" t-as="asset">
<t t-foreach="asset.cssLibs || []" t-as="cssLib">
<link type="text/css" rel="stylesheet" t-att-href="cssLib"/>
</t>
<t t-foreach="asset.cssContents || []" t-as="cssContent">
<style type="text/css" t-raw="cssContent"/>
</t>
<t t-foreach="asset.jsLibs || []" t-as="jsLib">
<script type="text/javascript" t-att-src="jsLib"/>
</t>
<t t-foreach="asset.jsContents || []" t-as="jsContent" t-if="jsContent.indexOf('inline asset') !== -1">
<script type="text/javascript" t-raw="jsContent"/>
</t>
</t>
<t t-foreach="asset.cssContents || []" t-as="cssContent">
<style type="text/css" t-raw="cssContent"/>
</t>
<t t-foreach="asset.jsContents || []" t-as="jsContent" t-if="jsContent.indexOf('inline asset') !== -1">
<script type="text/javascript" t-raw="jsContent"/>
</t>
</head>
<body class="o_in_iframe">
<div id="iframe_target" style="height: calc(100vh - 6px);"/>
<script type="text/javascript">
window.$ = window.jQuery = window.top.jQuery;
odoo.define('web.session', function () {
return window.top.odoo.__DEBUG__.services['web.session'];
});
odoo.define('root.widget', function (require) {
'use strict';
var Widget = require('web.Widget');
var widget = new Widget();
widget.appendTo(document.body);
return widget;
});
odoo.define('web.core.top', function (require) {
var core = require('web.core');
core.qweb.templates = window.top.odoo.__DEBUG__.services['web.core'].qweb.templates;
});
</script>
<script type="text/javascript" src="/web_editor/static/lib/summernote/summernote.js"/>
</head>
<body id="iframe_target" class="o_in_iframe">
<div id="web_editor-top-edit"></div>
<div id="wrapwrap">
<div data-oe-model="model" data-oe-type="html" class="o_editable oe_structure"></div>
</div>
<script type="text/javascript">
window._summernoteSlave = $.summernote;
window._summernoteSlave.iframe = true;
window._summernoteSlave.lang = window.top._summernoteMaster.lang;
window._fnSummenoteSlave = $.fn.summernote;
$.summernote = window.top._summernoteMaster;
$.fn.summernote = window.top._fnSummernoteMaster;
if (window.top.<t t-esc="updateIframeId"/>) {
window.top.<t t-esc="updateIframeId"/>(<t t-esc="avoidDoubleLoad"/>);
}
odoo.define('web_editor.wysiwyg.iniframe', function (require) {
'use strict';
var editor = require('web_editor.editor');
window._summernoteSlave = $.summernote;
window._summernoteSlave.iframe = true;
window._summernoteSlave.lang = window.top.$.summernote.lang;
if (window.top.<t t-esc="updateIframeId"/>) {
window.top.<t t-esc="updateIframeId"/>(editor.Class, <t t-esc="avoidDoubleLoad"/>);
}
});
</script>
</body>
</t>
+9 -2
View File
@@ -2,8 +2,12 @@
<odoo>
<template id="wysiwyg_iframe_css_assets" name="CSS assets for wysiwyg iframe content">
<t t-call-assets="web.assets_common" t-js="false"/>
<t t-call-assets="web.assets_backend" t-js="false"/>
</template>
<template id="wysiwyg_iframe_editor_assets" name="Editor assets for wysiwyg iframe content">
<t t-call-assets="web.assets_common"/>
<t t-call-assets="web.assets_frontend" t-js="false"/>
<t t-call-assets="web_editor.assets_wysiwyg"/>
</template>
<template id="compiled_assets_wysiwyg" name="Wysiwyg Editor">
@@ -53,6 +57,8 @@
<!-- Launcher -->
<script type="text/javascript" src="/web_editor/static/src/js/wysiwyg/wysiwyg.js"/>
<script type="text/javascript" src="/web_editor/static/src/js/wysiwyg/wysiwyg_snippets.js"/>
<script type="text/javascript" src="/web_editor/static/src/js/wysiwyg/wysiwyg_iframe.js"/>
</template>
<template id="summernote" name="Summernote">
@@ -152,6 +158,7 @@
</xpath>
<xpath expr="script[last()]" position="after">
<script type="text/javascript" src="/web_editor/static/src/js/backend/field_html.js" />
<script type="text/javascript" src="/web_editor/static/src/js/backend/convert_inline.js" />
</xpath>
</template>