From 160ddc8567c60493ea077033aa44e1f4139fec41 Mon Sep 17 00:00:00 2001 From: remi-filament <30716308+remi-filament@users.noreply.github.com> Date: Mon, 5 Feb 2018 20:21:45 +0100 Subject: [FIX] web_editor: correct check of property 'rules' Port of PR #22603 from v9 to v10 See Issue #22517 (#22679) --- addons/web_editor/static/src/js/transcoder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'addons/web_editor/static/src') diff --git a/addons/web_editor/static/src/js/transcoder.js b/addons/web_editor/static/src/js/transcoder.js index 6b42e028b3a..265ca5ab3f0 100644 --- a/addons/web_editor/static/src/js/transcoder.js +++ b/addons/web_editor/static/src/js/transcoder.js @@ -9,7 +9,7 @@ var getMatchedCSSRules = function (a) { var sheets = document.styleSheets; for(var i = sheets.length-1; i >= 0 ; i--) { var rules; - if (sheets[i].rules) { + if (sheets[i].hasOwnProperty('rules')) { rules = sheets[i].rules; } else { //try...catch because Firefox not able to enumerate document.styleSheets[].cssRules[] for cross-domain stylesheets. -- cgit v1.2.3 From a98beb13db82b692d32aa0e6e0b0befabbbaa386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Mon, 5 Feb 2018 20:24:23 +0100 Subject: [FIX] web_editor: correct check of 'rules' property see issue #22517 --- addons/web_editor/static/src/js/transcoder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'addons/web_editor/static/src') diff --git a/addons/web_editor/static/src/js/transcoder.js b/addons/web_editor/static/src/js/transcoder.js index ca6f751f954..b0831832dd6 100644 --- a/addons/web_editor/static/src/js/transcoder.js +++ b/addons/web_editor/static/src/js/transcoder.js @@ -13,7 +13,7 @@ var getMatchedCSSRules = function (a) { var sheets = document.styleSheets; for(var i = sheets.length-1; i >= 0 ; i--) { var rules; - if (sheets[i].rules) { + if (sheets[i].hasOwnProperty('rules')) { rules = sheets[i].rules; } else { //try...catch because Firefox not able to enumerate document.styleSheets[].cssRules[] for cross-domain stylesheets. -- cgit v1.2.3 From 8dfd6a2507c2b59016d466d818c0d42afbfdb027 Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Mon, 5 Feb 2018 15:00:44 +0100 Subject: [FIX] web_editor: fix image deletion and replacement Deleting/replacing an image through summernote gave unexpected results. As modifiying the related function may be tricky, this commit instead wrap them to solve the specific erratic behavior: if the range is in/ matches an image, then simply remove the image and move the cursor to its previous location. --- addons/web_editor/static/src/js/summernote.js | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'addons/web_editor/static/src') diff --git a/addons/web_editor/static/src/js/summernote.js b/addons/web_editor/static/src/js/summernote.js index da837d67354..9c5ef38d43d 100644 --- a/addons/web_editor/static/src/js/summernote.js +++ b/addons/web_editor/static/src/js/summernote.js @@ -953,15 +953,21 @@ range.WrappedRange.prototype.deleteContents = function (towrite) { return this; } - var prevBP = dom.removeBetween(this.sc, this.so, this.ec, this.eo, towrite); - - $(dom.node(prevBP.sc)).trigger("click"); // trigger click to disable and reanable editor and image handler - return new range.WrappedRange( - prevBP.sc, - prevBP.so, - prevBP.ec, - prevBP.eo - ); + var r; + var image = this.isOnImg(); + if (image) { + // If the range matches/is in an image, then the image is to be removed + // and the cursor moved to its previous position + var parentNode = image.parentNode; + var index = _.indexOf(parentNode.childNodes, image); + parentNode.removeChild(image); + r = new range.WrappedRange(parentNode, index, parentNode, index); + } else { + r = dom.removeBetween(this.sc, this.so, this.ec, this.eo, towrite); + } + + $(dom.node(r.sc)).trigger("click"); // trigger click to disable and reanable editor and image handler + return new range.WrappedRange(r.sc, r.so, r.ec, r.eo); }; range.WrappedRange.prototype.clean = function (mergeFilter, all) { var node = dom.node(this.sc === this.ec ? this.sc : this.commonAncestor()); -- cgit v1.2.3 From ce51ad7e69dbc1415eaf5f2ae846bbe89a37f69b Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Wed, 7 Feb 2018 15:55:20 +0100 Subject: [FIX] web_editor: allow deleting all images in table cells Before this commit, it was impossible to delete an image which was a direct child of a table cell using the trash-icon button in the toolbar. This commit fixes the behavior by changing the logic quite a lot, so it is kinda tricky (see https://github.com/odoo/odoo/commit/25849e918651eba080dac3af4e4fcdc8f1280241 for more explaination). Closes https://github.com/odoo/odoo/issues/22523 --- addons/web_editor/static/src/js/summernote.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'addons/web_editor/static/src') diff --git a/addons/web_editor/static/src/js/summernote.js b/addons/web_editor/static/src/js/summernote.js index 9c5ef38d43d..574836d0180 100644 --- a/addons/web_editor/static/src/js/summernote.js +++ b/addons/web_editor/static/src/js/summernote.js @@ -1313,7 +1313,7 @@ function summernote_keydown_clean (field) { function remove_table_content(r) { var ancestor = r.commonAncestor(); - var nodes = dom.listBetween(r.sc, r.ec); + var nodes = dom.listBetween(r.sc, r.ec, r.so, r.eo); if (dom.isText(r.sc)) { r.sc.textContent = r.sc.textContent.slice(0, r.so); } -- cgit v1.2.3 From 152e3f8686ec4f48ba22e5c13f92f72c2640a3b8 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Wed, 7 Feb 2018 14:13:00 +0100 Subject: [FIX] web_editor: encoding of the attachment name is missing in image src --- addons/web_editor/static/src/js/widgets/widgets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'addons/web_editor/static/src') diff --git a/addons/web_editor/static/src/js/widgets/widgets.js b/addons/web_editor/static/src/js/widgets/widgets.js index e499e83843d..1f150b16862 100644 --- a/addons/web_editor/static/src/js/widgets/widgets.js +++ b/addons/web_editor/static/src/js/widgets/widgets.js @@ -305,7 +305,7 @@ var ImageDialog = Widget.extend({ return (r.url || r.id); }); _.each(this.records, function (record) { - record.src = record.url || _.str.sprintf('/web/image/%s/%s', record.id, record.name); // Name is added for SEO purposes + record.src = record.url || _.str.sprintf('/web/image/%s/%s', record.id, encodeURI(record.name)); // Name is added for SEO purposes record.is_document = !(/gif|jpe|jpg|png/.test(record.mimetype)); }); this.display_attachments(); -- cgit v1.2.3 From 6494f511718893eec3573c60c0a62e04d386359d Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Wed, 7 Feb 2018 14:11:46 +0100 Subject: [FIX] base/mail: Added an access_token on the attachments url The access_token is automatically added by the media editor. It's added on the website and by the html editor to send by email. The widget display the attachments from 'ir.ui.view' (public attachment) and the attachment from the current record. Issue: can't load image in gmail or mobile because the client mail use a proxy who avoid ours odoo access. --- addons/web_editor/static/src/js/backend/fields.js | 2 + .../static/src/js/editor/rte.summernote.js | 6 +- .../static/src/js/editor/snippets.options.js | 7 +- addons/web_editor/static/src/js/widgets/widgets.js | 135 +++++++++++++-------- addons/web_editor/static/src/xml/editor.xml | 7 +- 5 files changed, 101 insertions(+), 56 deletions(-) (limited to 'addons/web_editor/static/src') diff --git a/addons/web_editor/static/src/js/backend/fields.js b/addons/web_editor/static/src/js/backend/fields.js index b84fcd21c91..ae7ee94e964 100644 --- a/addons/web_editor/static/src/js/backend/fields.js +++ b/addons/web_editor/static/src/js/backend/fields.js @@ -133,6 +133,8 @@ var FieldTextHtmlSimple = basic_fields.DebouncedField.extend(TranslatableFieldMi this.$textarea.summernote(this._getSummernoteConfig()); this.$content = this.$('.note-editable:first'); this.$content.html(this._textToHtml(this.value)); + this.$content.data('oe-id', this.recordData.res_id || this.res_id); + this.$content.data('oe-model', this.recordData.model || this.model); // trigger a mouseup to refresh the editor toolbar this.$content.trigger('mouseup'); if (this.nodeOptions['style-inline']) { diff --git a/addons/web_editor/static/src/js/editor/rte.summernote.js b/addons/web_editor/static/src/js/editor/rte.summernote.js index 2ac9df2d693..71982f8137b 100644 --- a/addons/web_editor/static/src/js/editor/rte.summernote.js +++ b/addons/web_editor/static/src/js/editor/rte.summernote.js @@ -1080,8 +1080,12 @@ var SummernoteManager = Class.extend(mixins.EventDispatcherMixin, { return; } data.__alreadyDone = true; + var mediaDialog = new weWidgets.MediaDialog(this, - data.options || {}, + _.extend({ + res_model: data.$editable.data('oe-model'), + res_id: data.$editable.data('oe-id'), + }, data.options), data.$editable, data.media ); diff --git a/addons/web_editor/static/src/js/editor/snippets.options.js b/addons/web_editor/static/src/js/editor/snippets.options.js index 38dcacf9e77..382bae0d4a7 100644 --- a/addons/web_editor/static/src/js/editor/snippets.options.js +++ b/addons/web_editor/static/src/js/editor/snippets.options.js @@ -872,7 +872,12 @@ registry.background = SnippetOption.extend({ // Put fake image in the DOM, edit it and use it as background-image var $image = $('', {class: 'hidden', src: value}).appendTo(this.$target); - var _editor = new widget.MediaDialog(this, {}, null, $image[0]).open(); + var $editable = this.$target.closest('.o_editable'); + var options = { + res_model: $editable.data('oe-model'), + res_id: $editable.data('oe-id'), + }; + var _editor = new widget.MediaDialog(this, options, null, $image[0]).open(); _editor.opened(function () { _editor.$('[href="#editor-media-video"], [href="#editor-media-icon"]').addClass('hidden'); }); diff --git a/addons/web_editor/static/src/js/widgets/widgets.js b/addons/web_editor/static/src/js/widgets/widgets.js index 1f150b16862..b93724c7a30 100644 --- a/addons/web_editor/static/src/js/widgets/widgets.js +++ b/addons/web_editor/static/src/js/widgets/widgets.js @@ -120,7 +120,13 @@ var ImageDialog = Widget.extend({ this._super.apply(this, arguments); this.options = options || {}; this.accept = this.options.accept || this.options.document ? "*/*" : "image/*"; - this.domain = this.options.domain || ['|', ['mimetype', '=', false], ['mimetype', this.options.document ? 'not in' : 'in', ['image/gif', 'image/jpe', 'image/jpeg', 'image/jpg', 'image/gif', 'image/png']]]; + if (this.options.res_id) { + this.domain = ['|', + '&', ['res_model', '=', this.options.res_model], ['res_id', '=', this.options.res_id], + ['res_model', '=', 'ir.ui.view']]; + } else { + this.domain = [['res_model', '=', 'ir.ui.view']]; + } this.parent = parent; this.old_media = media; this.media = media; @@ -171,6 +177,7 @@ var ImageDialog = Widget.extend({ } }, save: function () { + var self = this; if (this.options.select_images) { return this.images; } @@ -181,34 +188,55 @@ var ImageDialog = Widget.extend({ img = _.find(this.images, function (img) { return img.id === id;}); } - var media; - if (!img.is_document) { - if (this.media.tagName !== "IMG" || !this.old_media) { - this.add_class = "pull-left"; - this.style = {"width": "100%"}; - } - if (this.media.tagName !== "IMG") { - media = document.createElement('img'); - $(this.media).replaceWith(media); - this.media = media; - } - this.media.setAttribute('src', img.src); - } else { - if (this.media.tagName !== "A") { - $('.note-control-selection').hide(); - media = document.createElement('a'); - $(this.media).replaceWith(media); - this.media = media; - } - this.media.setAttribute('href', '/web/content/' + img.id + '?unique=' + img.checksum + '&download=true'); - $(this.media).addClass('o_image').attr('title', img.name).attr('data-mimetype', img.mimetype); + var def = $.when(); + if (!img.access_token) { + def = this._rpc({ + model: 'ir.attachment', + method: 'generate_access_token', + args: [[img.id]] + }).then(function (access_token) { + img.access_token = access_token[0]; + }); } - $(this.media).attr('alt', img.alt); - var style = this.style; - if (style) { $(this.media).css(style); } + return def.then(function () { + var media; + if (!img.is_document) { + if (img.access_token && self.options.res_model !== 'ir.ui.view') { + img.src += _.str.sprintf('?access_token=%s', img.access_token); + } + if (self.media.tagName !== "IMG" || !self.old_media) { + self.add_class = "pull-left"; + self.style = {"width": "100%"}; + } + if (self.media.tagName !== "IMG") { + media = document.createElement('img'); + $(self.media).replaceWith(media); + self.media = media; + } + self.media.setAttribute('src', img.src); + } else { + if (self.media.tagName !== "A") { + $('.note-control-selection').hide(); + media = document.createElement('a'); + $(self.media).replaceWith(media); + self.media = media; + } + var href = '/web/content/' + img.id + '?'; + if (img.access_token && self.options.res_model !== 'ir.ui.view') { + href += _.str.sprintf('access_token=%s&', img.access_token); + } + href += 'unique=' + img.checksum + '&download=true'; + self.media.setAttribute('href', href); + $(self.media).addClass('o_image').attr('title', img.name).attr('data-mimetype', img.mimetype); + } - return this.media; + $(self.media).attr('alt', img.alt); + var style = self.style; + if (style) { $(self.media).css(style); } + + return self.media; + }); }, clear: function () { this.media.className = this.media.className.replace(/(^|\s+)((img(\s|$)|img-(?!circle|rounded|thumbnail))[^\s]*)/g, ' '); @@ -282,7 +310,7 @@ var ImageDialog = Widget.extend({ } }, fetch_existing: function (needle) { - var domain = [['res_model', '=', 'ir.ui.view']].concat(this.domain); + var domain = this.domain.concat(['|', ['mimetype', '=', false], ['mimetype', this.options.document ? 'not in' : 'in', ['image/gif', 'image/jpe', 'image/jpeg', 'image/jpg', 'image/gif', 'image/png']]]); if (needle && needle.length) { domain.push('|', ['datas_fname', 'ilike', needle], ['name', 'ilike', needle]); } @@ -292,7 +320,7 @@ var ImageDialog = Widget.extend({ args: [], kwargs: { domain: domain, - fields: ['name', 'mimetype', 'checksum', 'url', 'type'], + fields: ['name', 'mimetype', 'checksum', 'url', 'type', 'res_id', 'res_model', 'access_token'], order: [{name: 'id', asc: false}], context: weContext.get(), } @@ -1001,18 +1029,21 @@ var MediaDialog = Dialog.extend({ return this._super.apply(this, arguments); }, save: function () { + var self = this; + var args = arguments; + var _super = this._super; if (this.options.select_images) { - this.final_data = this.active.save(); - this._super.apply(this, arguments); - return; + return $.when(this.active.save()).then(function (data) { + self.final_data = data; + return _super.apply(self, args); + }); } if (this.rte) { this.range.select(); this.rte.historyRecordUndo(this.media); } - var self = this; - if (self.media) { + if (this.media) { this.media.innerHTML = ""; if (this.active !== this.imageDialog && this.active !== this.documentDialog) { this.imageDialog.clear(); @@ -1029,27 +1060,27 @@ var MediaDialog = Dialog.extend({ this.range.insertNode(this.media, true); this.active.media = this.media; } - this.active.save(); - if (this.active.add_class) { - $(this.active.media).addClass(this.active.add_class); - } - var media = this.active.media; - - this.final_data = [media, self.old_media]; - $(document.body).trigger("media-saved", this.final_data); - $(self.old_media).trigger("save", this.final_data); - $(this.final_data).trigger('input'); - - // Update editor bar after image edition (in case the image change to icon or other) - _.defer(function () { - if (!media.parentNode) return; - range.createFromNode(media).select(); - click_event(media, "mousedown"); - click_event(media, "mouseup"); + return $.when(this.active.save()).then(function () { + if (self.active.add_class) { + $(self.active.media).addClass(self.active.add_class); + } + var media = self.active.media; + + self.final_data = [media, self.old_media]; + $(document.body).trigger("media-saved", self.final_data); + $(self.old_media).trigger("save", self.final_data); + $(self.final_data).trigger('input'); + + // Update editor bar after image edition (in case the image change to icon or other) + _.defer(function () { + if (!media.parentNode) return; + range.createFromNode(media).select(); + click_event(media, "mousedown"); + click_event(media, "mouseup"); + }); + return _super.apply(self, args); }); - - this._super.apply(this, arguments); }, searchTimer: null, search: function () { diff --git a/addons/web_editor/static/src/xml/editor.xml b/addons/web_editor/static/src/xml/editor.xml index e8e6374305e..8f975b0d784 100644 --- a/addons/web_editor/static/src/xml/editor.xml +++ b/addons/web_editor/static/src/xml/editor.xml @@ -86,6 +86,8 @@ target="fileframe" class="form-inline"> + +
@@ -145,8 +147,9 @@
- -
+ + +
-- cgit v1.2.3 From ae39a89a096c8dcb315425a23e4c6d7a70ef2f27 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Tue, 30 Jan 2018 14:22:07 +0100 Subject: [FIX] web_editor: add thumbnail image to send attachment --- addons/web_editor/static/src/js/backend/fields.js | 1 + .../web_editor/static/src/js/editor/transcoder.js | 30 ++++++++++++++++++++++ addons/web_editor/static/src/js/inline.js | 1 + 3 files changed, 32 insertions(+) (limited to 'addons/web_editor/static/src') diff --git a/addons/web_editor/static/src/js/backend/fields.js b/addons/web_editor/static/src/js/backend/fields.js index ae7ee94e964..d3d601bac73 100644 --- a/addons/web_editor/static/src/js/backend/fields.js +++ b/addons/web_editor/static/src/js/backend/fields.js @@ -118,6 +118,7 @@ var FieldTextHtmlSimple = basic_fields.DebouncedField.extend(TranslatableFieldMi */ _getValue: function () { if (this.nodeOptions['style-inline']) { + transcoder.linkImgToAttachmentThumbnail(this.$content); transcoder.classToStyle(this.$content); transcoder.fontToImg(this.$content); } diff --git a/addons/web_editor/static/src/js/editor/transcoder.js b/addons/web_editor/static/src/js/editor/transcoder.js index c90d5ebab1d..54bbc803645 100644 --- a/addons/web_editor/static/src/js/editor/transcoder.js +++ b/addons/web_editor/static/src/js/editor/transcoder.js @@ -259,10 +259,40 @@ function styleToClass($editable) { $c.remove(); } +/** + * Converts css display for attachment link to real image. + * Without this post process, the display depends on the css and the picture + * does not appear when we use the html without css (to send by email for e.g.) + * + * @param {jQuery} $editable + */ +function attachmentThumbnailToLinkImg($editable) { + $editable.find('a[href*="/web/content/"][data-mimetype]:empty').each(function () { + var $link = $(this); + var $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); + }); +} + +/** + * Revert attachmentThumbnailToLinkImg changes + * + * @see attachmentThumbnailToLinkImg + * @param {jQuery} $editable + */ +function linkImgToAttachmentThumbnail($editable) { + $editable.find('a[href*="/web/content/"][data-mimetype] > img').remove(); +} + return { fontToImg: fontToImg, imgToFont: imgToFont, classToStyle: classToStyle, styleToClass: styleToClass, + attachmentThumbnailToLinkImg: attachmentThumbnailToLinkImg, + linkImgToAttachmentThumbnail: linkImgToAttachmentThumbnail, }; }); diff --git a/addons/web_editor/static/src/js/inline.js b/addons/web_editor/static/src/js/inline.js index ac0dee435ae..8715b5b7f24 100644 --- a/addons/web_editor/static/src/js/inline.js +++ b/addons/web_editor/static/src/js/inline.js @@ -53,6 +53,7 @@ snippet_editor.Class.include({ this._super.apply(this, arguments); var $editable = $('#editable_area'); + transcoder.linkImgToAttachmentThumbnail($editable); transcoder.fontToImg($editable); transcoder.classToStyle($editable); -- cgit v1.2.3 From 1c5bb8564be91b5d3f6e64f133487cf55db4b1db Mon Sep 17 00:00:00 2001 From: Nicolas Lempereur Date: Mon, 12 Feb 2018 10:51:46 +0100 Subject: [FIX] web_editor: regression less editor 5af122387 In 5af12238 a change was made to have the correct hierarchy levels, but this can cause an issue when editing less files. This is because we now had "undefined + 1" which gives NaN which breaks Array constructor. opw-814579 opw-815397 opw-814322 opw-815265 opw-814021 closes #23006 --- addons/web_editor/static/src/js/widgets/ace.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'addons/web_editor/static/src') diff --git a/addons/web_editor/static/src/js/widgets/ace.js b/addons/web_editor/static/src/js/widgets/ace.js index 6c28c1e2332..0a89d0d8ce7 100644 --- a/addons/web_editor/static/src/js/widgets/ace.js +++ b/addons/web_editor/static/src/js/widgets/ace.js @@ -742,7 +742,7 @@ var ViewEditor = Widget.extend({ var text = data.text || ''; if (!isSelected) { - text = Array($elem.data('level') + 1).join('-') + ' ' + text; + text = Array(($elem.data('level') || 0) + 1).join('-') + ' ' + text; } var $div = $('
', { text: text, -- cgit v1.2.3 From 0de5c1f076d7e7d0e5361e08c810c727bd41d9e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20K=C3=BChn?= Date: Mon, 12 Feb 2018 00:03:22 +0100 Subject: [FIX] web{_site,_editor}: transparent root instance 'root widget' With this commit, any component that needs an instance of the root widget can simply require the module 'root.widget'. Based on the context (backend, frontend, iframe), the real root.widget will resolve to either the webclient, the website root instance, or the iframe root instance. This is desirable in order for the tour_manager to become a child of the root widget, as tours are enabled in any of the above contexts. This commit is a requirement in order to solve an issue with JS services, in which there should be only one service provider at any given time. --- addons/web_editor/static/src/js/iframe.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'addons/web_editor/static/src') diff --git a/addons/web_editor/static/src/js/iframe.js b/addons/web_editor/static/src/js/iframe.js index 949a141c333..f2c5e3be661 100644 --- a/addons/web_editor/static/src/js/iframe.js +++ b/addons/web_editor/static/src/js/iframe.js @@ -82,7 +82,7 @@ translator.Class.include({ //============================================================================== -odoo.define('web_editor.IframeRoot.instance', function (require) { +odoo.define('root.widget', function (require) { 'use strict'; require('web.dom_ready'); -- cgit v1.2.3