[FIX] web_editor: DOM is not cleaned before saving custom snippets

Previously, when saving a custom snippet, the outerHTML of the snippet
was naively read directly and saved as a new custom snippet.
Unfortunately this completely bypasses all cleanup mechanisms that we
have before saving, such as stopping the public widgets, or creating
attachments for cropped or optimized images. This would result in weird
bugs, where public widgets would appear twice when dropping a custom
snippet containing one, as well as the creating a new attachment for any
image that was optimized or cropped before saving the custom snippet
every time it was dropped in the page again.

Unfortunately there is no good mechanism for an option to ask the editor
to clean the DOM without actually saving it, as a temporary measure, the
snippet-save option will instead save the whole page (which will clean
up the DOM) and save the custom snippet after the save, before
refreshing the page and reopening the editor, not unlike some theme
options.

task-2341612

closes odoo/odoo#64166

X-original-commit: 38569777935a7939182f49ca978d98f4079e8f2f
Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com>
This commit is contained in:
Samuel Degueldre
2021-01-06 15:49:34 +00:00
committed by qsm-odoo
parent c87dbecf54
commit fc089f32b0
3 changed files with 51 additions and 13 deletions
@@ -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",
@@ -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) {
@@ -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"),