diff --git a/addons/test_website/static/tests/tours/custom_snippets.js b/addons/test_website/static/tests/tours/custom_snippets.js index 1b6b4a593ba..7fc066a1648 100644 --- a/addons/test_website/static/tests/tours/custom_snippets.js +++ b/addons/test_website/static/tests/tours/custom_snippets.js @@ -50,6 +50,10 @@ tour.register('test_custom_snippet', { content: "confirm save name", trigger: ".modal-dialog button span:contains('Save')", }, + { + content: "confirm reload", + trigger: ".modal-dialog button span:contains('Save and Reload')", + }, { content: "go to blocks", trigger: ".o_we_add_snippet_btn", 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 517f4e62d93..8b2f3b66794 100644 --- a/addons/web_editor/static/src/js/editor/snippets.editor.js +++ b/addons/web_editor/static/src/js/editor/snippets.editor.js @@ -2419,6 +2419,12 @@ var SnippetsMenu = Widget.extend({ }); }, /** + * UNUSED: used to be called when saving a custom snippet. We now save and + * reload the page when saving a custom snippet so that all the DOM cleanup + * mechanisms are run before saving. Kept for compatibility. + * + * TODO: remove in master / find a way to clean the DOM without save+reload + * * @private */ _onReloadSnippetTemplate: async function (ev) { 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 fe3f9118597..d36df85d1c8 100644 --- a/addons/web_editor/static/src/js/editor/snippets.options.js +++ b/addons/web_editor/static/src/js/editor/snippets.options.js @@ -4,6 +4,7 @@ odoo.define('web_editor.snippets.options', function (require) { var core = require('web.core'); const {ColorpickerWidget} = require('web.Colorpicker'); const Dialog = require('web.Dialog'); +const rpc = require('web.rpc'); const time = require('web.time'); var Widget = require('web.Widget'); var ColorPaletteWidget = require('web_editor.ColorPalette').ColorPaletteWidget; @@ -4695,27 +4696,54 @@ registry.SnippetSave = SnippetOptionWidget.extend({ classes: 'btn-primary', close: true, click: async () => { - const snippetName = dialog.el.querySelector('.o_we_snippet_name_input').value; - const targetCopyEl = this.$target[0].cloneNode(true); - delete targetCopyEl.dataset.name; + const save = await new Promise(resolve => { + Dialog.confirm(this, _t("To save a snippet, we need to save all your previous modifications and reload the page."), { + buttons: [ + { + text: _t("Save and Reload"), + classes: 'btn-primary', + close: true, + click: () => resolve(true), + }, { + text: _t("Cancel"), + close: true, + click: () => resolve(false), + } + ] + }); + }); + if (!save) { + return; + } const snippetKey = this.$target[0].dataset.snippet; let thumbnailURL; this.trigger_up('snippet_thumbnail_url_request', { key: snippetKey, onSuccess: url => thumbnailURL = url, }); - await this._rpc({ - model: 'ir.ui.view', - method: 'save_snippet', - kwargs: { - 'name': snippetName, - 'arch': targetCopyEl.outerHTML, - 'template_key': this.options.snippets, - 'snippet_key': snippetKey, - 'thumbnail_url': thumbnailURL, + this.trigger_up('request_save', { + reloadEditor: true, + onSuccess: async () => { + const snippetName = dialog.el.querySelector('.o_we_snippet_name_input').value; + const targetCopyEl = this.$target[0].cloneNode(true); + delete targetCopyEl.dataset.name; + // By the time onSuccess is called after request_save, the + // current widget has been destroyed and is orphaned, so this._rpc + // will not work as it can't trigger_up. For this reason, we need + // to bypass the service provider and use the global RPC directly + await rpc.query({ + model: 'ir.ui.view', + method: 'save_snippet', + kwargs: { + 'name': snippetName, + 'arch': targetCopyEl.outerHTML, + 'template_key': this.options.snippets, + 'snippet_key': snippetKey, + 'thumbnail_url': thumbnailURL, + }, + }); }, }); - this.trigger_up('reload_snippet_template'); }, }, { text: _t("Discard"),