From fc6630a2d74fd8024e9246f91b07cbffb15b8825 Mon Sep 17 00:00:00 2001 From: Guillaume-gdi Date: Tue, 14 Nov 2023 10:45:55 +0100 Subject: [PATCH] [FIX] web_editor, website: prevent drop elements next to editable media In the past, it was possible to drop elements in non-editable areas. Commit [1] fixed the issue but then commit [2] added a solution to be able to edit a media element that is in a `o_not_editable` environment. Unfortunately, the solution makes it possible to drop elements next to the `o_editable_media` elements ignoring the editable context (ignoring the first fix). Steps to reproduce the issue: - Enter website edit mode - Drop a Team block in a page - Click on the Team block => The user is able to drop a "Text highlight" block next to the team member pictures. This commit - reverts the commit [1] because in the meantime, the commit [3] fixed the issue. - fixes the Team block issue. - fixes the filterFunc for the drop-in. - adds a test to ensure the issue doesn't come back. [1]: https://github.com/odoo/odoo/commit/67924648676b37898aedd86bc32868299aee542c [2]: https://github.com/odoo/odoo/commit/580f1b77ce0b96b7efbf83a0ccdf6979bbf0e904 [3]: https://github.com/odoo/odoo/commit/ae7452f18dbd6ed56dbf883ae8495382b336aa78 opw-3536227 closes odoo/odoo#144806 X-original-commit: 9b4c2498b8fa2bc0aca604b6256e6c805b9b4c10 Signed-off-by: Quentin Smetz (qsm) Signed-off-by: Guillaume Dieleman (gdi) --- .../static/src/js/editor/snippets.editor.js | 26 +++++++++++--- .../tours/drag_and_drop_on_non_editable.js | 36 +++++++++++++++++++ addons/website/tests/test_snippets.py | 3 ++ 3 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 addons/website/static/tests/tours/drag_and_drop_on_non_editable.js diff --git a/addons/web_editor/static/src/js/editor/snippets.editor.js b/addons/web_editor/static/src/js/editor/snippets.editor.js index 60a43a2d294..cdbd7c9cbd4 100644 --- a/addons/web_editor/static/src/js/editor/snippets.editor.js +++ b/addons/web_editor/static/src/js/editor/snippets.editor.js @@ -2423,7 +2423,7 @@ var SnippetsMenu = Widget.extend({ $selectorSiblings = $(unique(($selectorSiblings || $()).add($selectorChildren.children()).get())); } - var noDropZonesSelector = '[data-invisible="1"], .o_we_no_overlay, :not(:visible), :not(:o_editable)'; + var noDropZonesSelector = '[data-invisible="1"], .o_we_no_overlay, :not(:visible)'; if ($selectorSiblings) { $selectorSiblings.not(`.oe_drop_zone, .oe_drop_clone, ${noDropZonesSelector}`).each(function () { var data; @@ -2835,6 +2835,13 @@ var SnippetsMenu = Widget.extend({ _computeSelectorFunctions: function (selector, exclude, target, noCheck, isChildren, excludeParent) { var self = this; + // TODO in master: FOR_DROP should be a param of the function. + const forDropID = 'FOR_DROP'; + const forDrop = exclude && exclude.startsWith(forDropID); + if (forDrop) { + exclude = exclude.substring(forDropID.length); + } + // The `:not(.o_editable_media)` part is handled outside of the selector // (see filterFunc). // Note: the `:not([contenteditable="true"])` part was there for that @@ -2856,10 +2863,19 @@ var SnippetsMenu = Widget.extend({ // When noCheck is true, we only check the exclude. return true; } - // `o_editable_media` bypasses the `o_not_editable` class. - if (this.classList.contains('o_editable_media')) { + // `o_editable_media` bypasses the `o_not_editable` class except for + // drag & drop. + if (!forDrop && this.classList.contains('o_editable_media')) { return weUtils.shouldEditableMediaBeEditable(this); } + if (forDrop && !isChildren) { + // it's a drop-in. + return !$(this) + .is('.o_not_editable :not([contenteditable="true"]), .o_not_editable'); + } + if (isChildren) { + return !$(this).is('.o_not_editable *'); + } return !$(this) .is('.o_not_editable:not(.s_social_media) :not([contenteditable="true"])'); }; @@ -2956,8 +2972,8 @@ var SnippetsMenu = Widget.extend({ 'base_target': target, 'selector': self._computeSelectorFunctions(selector, exclude, target, noCheck), '$el': $style, - 'drop-near': $style.data('drop-near') && self._computeSelectorFunctions($style.data('drop-near'), '', false, noCheck, true, excludeParent), - 'drop-in': $style.data('drop-in') && self._computeSelectorFunctions($style.data('drop-in'), '', false, noCheck), + 'drop-near': $style.data('drop-near') && self._computeSelectorFunctions($style.data('drop-near'), 'FOR_DROP', false, noCheck, true, excludeParent), + 'drop-in': $style.data('drop-in') && self._computeSelectorFunctions($style.data('drop-in'), 'FOR_DROP', false, noCheck), 'drop-exclude-ancestor': this.dataset.dropExcludeAncestor, 'drop-lock-within': this.dataset.dropLockWithin, 'data': Object.assign({string: $style.attr('string')}, $style.data()), diff --git a/addons/website/static/tests/tours/drag_and_drop_on_non_editable.js b/addons/website/static/tests/tours/drag_and_drop_on_non_editable.js new file mode 100644 index 00000000000..2c8d3146aee --- /dev/null +++ b/addons/website/static/tests/tours/drag_and_drop_on_non_editable.js @@ -0,0 +1,36 @@ +/** @odoo-module **/ + +import wTourUtils from '@website/js/tours/tour_utils'; + +wTourUtils.registerWebsitePreviewTour("test_drag_and_drop_on_non_editable", { + test: true, + url: "/", + edition: true, +}, () => [ + wTourUtils.dragNDrop({ + id: "s_company_team", + name: "Team", + }), + { + content: "Click on an editable media in non editable env.", + trigger: "iframe .s_company_team .o_not_editable > .o_editable_media", + }, + { + content: "Verify that the Replace options is in the editor.", + trigger: "we-button[data-replace-media]", + isCheck: true, + }, + wTourUtils.goBackToBlocks(), + Object.assign(wTourUtils.dragNDrop({ + id: "s_text_highlight", + name: "Text Highlight", + }), { + content: "Drag and drop the Text Highlight building block next to the Team block media.", + run: "drag_and_drop_native iframe .s_company_team .o_not_editable > .o_editable_media", + }), + { + content: "Verify that the Text Highlight building block isn't in a non editable element.", + trigger: "iframe .s_company_team :not(.o_not_editable) > .s_text_highlight", + isCheck: true, + }, +]); diff --git a/addons/website/tests/test_snippets.py b/addons/website/tests/test_snippets.py index 75a88b7a563..90fbd71577c 100644 --- a/addons/website/tests/test_snippets.py +++ b/addons/website/tests/test_snippets.py @@ -95,3 +95,6 @@ class TestSnippets(HttpCase): website.cookies_bar = True self.start_tour(self.env['website'].get_client_action_url('/'), 'snippet_popup_and_scrollbar', login='admin') self.start_tour(self.env['website'].get_client_action_url('/'), 'snippet_popup_and_animations', login='admin') + + def test_drag_and_drop_on_non_editable(self): + self.start_tour(self.env['website'].get_client_action_url('/'), 'test_drag_and_drop_on_non_editable', login='admin')