[FIX] web_editor, website: review widgets stop on editor save

Original 14.0 commit title:
Prevent deadlock on editor save when an option is focused
See [1].

In 15.0, the code worsened... but, by chance, fixed the deadlock bug.
See adapted version via the X-original-commit.

With this master version of the original commit, it is tried to clean
the code a bit... at least around the code that was originally touched,
without starting a big refactoring that is again needed. The deprecated
(and added by mistake) 'edition_will_stopped' and 'edition_was_stopped'
events are now gone.
Hopefully, we will continue to improve that editor hierarchy (and
ultimately converting to OWL?) in future updates.

[1]: https://github.com/odoo/odoo/commit/53a08b3470d7d18488abf93b7ea6551700650460

Related to opw-2767903

closes odoo/odoo#92321

X-original-commit: 483a2d6cac1be3b40a4d1878536baea9e6098ec1
Signed-off-by: Romain Derie (rde) <rde@odoo.com>
Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com>
This commit is contained in:
qsm-odoo
2022-05-27 12:47:45 +02:00
parent 95ed4ce98a
commit 8a01b40d29
2 changed files with 80 additions and 73 deletions
@@ -796,25 +796,20 @@ const Wysiwyg = Widget.extend({
}
return Promise.resolve({isDirty: isDirty, html: html});
},
/**
* Save the content for the normal mode or the translation mode.
*/
saveContent: async function (reload = true) {
await this.saveToServer(reload);
},
/**
* Reset the history.
*/
historyReset: function () {
this.odooEditor.historyReset();
},
/**
* Save the content to the server for the normal mode.
* Saves the content.
*
* @param {boolean} [reload=true]
* @returns {Promise}
*/
saveToServer: async function (reload = true) {
saveContent: async function (reload = true) {
const defs = [];
this.trigger_up('edition_will_stopped');
this.trigger_up('ready_to_save', {defs: defs});
await Promise.all(defs);
@@ -824,7 +819,6 @@ const Wysiwyg = Widget.extend({
await this.saveModifiedImages(editables.length ? $(editables) : this.$editable);
await this._saveViewBlocks();
this.trigger_up('edition_was_stopped');
window.removeEventListener('beforeunload', this._onBeforeUnload);
if (reload) {
window.location.reload();
+75 -62
View File
@@ -16,7 +16,6 @@ var _t = core._t;
*/
var EditPageMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({
assetLibs: ['web_editor.compiled_assets_wysiwyg', 'website.compiled_assets_wysiwyg'],
xmlDependencies: ['/website/static/src/xml/website.editor.xml'],
actions: _.extend({}, websiteNavbarData.WebsiteNavbarActionWidget.prototype.actions, {
edit: '_startEditMode',
@@ -29,8 +28,6 @@ var EditPageMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({
snippet_cloned: '_onSnippetCloned',
snippet_dropped: '_onSnippetDropped',
snippet_removed: '_onSnippetRemoved',
edition_will_stopped: '_onEditionWillStop',
edition_was_stopped: '_onEditionWasStopped',
request_save: '_onSnippetRequestSave',
request_cancel: '_onSnippetRequestCancel',
}),
@@ -90,7 +87,6 @@ var EditPageMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({
return def;
},
/**
* Asks the snippets to clean themself, then saves the page, then reloads it
* if asked to.
@@ -103,35 +99,34 @@ var EditPageMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({
if (this._saving) {
return false;
}
if (this.observer) {
this.observer.disconnect();
this.observer = undefined;
}
var self = this;
this._saving = true;
this.trigger_up('edition_will_stopped');
this._prepareLeavingEditMode();
const destroy = () => {
self.wysiwyg.destroy();
self.trigger_up('edition_was_stopped');
self.destroy();
this.wysiwyg.destroy();
this.destroy();
};
if (!this.wysiwyg.isDirty()) {
destroy();
window.location.reload();
return;
}
return this.wysiwyg.saveContent(false).then((result) => {
var $wrapwrap = $('#wrapwrap');
self.editableFromEditorMenu($wrapwrap).removeClass('o_editable');
this.editableFromEditorMenu($wrapwrap).removeClass('o_editable');
if (reload) {
// remove top padding because the connected bar is not visible
$('body').removeClass('o_connected_user');
return self._reload();
return this._reload();
} else {
destroy();
this.editModeEnabled = false;
}
return true;
}).guardedCatch(() => {
this._unprepareLeavingEditMode();
this._saving = false;
});
},
@@ -145,31 +140,32 @@ var EditPageMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({
* @returns {Deferred}
*/
cancel: function (reload = true) {
var self = this;
var def = new Promise(function (resolve, reject) {
if (!self.wysiwyg.isDirty()) {
const prom = new Promise((resolve, reject) => {
if (!this.wysiwyg.isDirty()) {
resolve();
} else {
var confirm = Dialog.confirm(self, _t("If you discard the current edits, all unsaved changes will be lost. You can cancel to return to edit mode."), {
var confirm = Dialog.confirm(this, _t("If you discard the current edits, all unsaved changes will be lost. You can cancel to return to edit mode."), {
confirm_callback: resolve,
});
confirm.on('closed', self, reject);
confirm.on('closed', this, reject);
}
});
return def.then(function () {
self.trigger_up('edition_will_stopped');
return prom.then(() => {
this._prepareLeavingEditMode();
var $wrapwrap = $('#wrapwrap');
self.editableFromEditorMenu($wrapwrap).removeClass('o_editable');
this.editableFromEditorMenu($wrapwrap).removeClass('o_editable');
if (reload) {
window.onbeforeunload = null;
self.wysiwyg.destroy();
return self._reload();
this.wysiwyg.destroy();
return this._reload();
} else {
self.wysiwyg.destroy();
self.trigger_up('readonly_mode');
self.trigger_up('edition_was_stopped');
self.destroy();
this.wysiwyg.destroy();
this.trigger_up('readonly_mode');
this.destroy();
this.editModeEnabled = false;
}
});
},
@@ -207,9 +203,10 @@ var EditPageMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({
*/
_startEditMode: async function () {
var self = this;
if (this.editModeEnable) {
if (this.editModeEnabled) {
return;
}
this.editModeEnabled = true;
$.blockUI({overlayCSS: {
backgroundColor: '#000',
@@ -223,7 +220,6 @@ var EditPageMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({
if (this.$welcomeMessage) {
this.$welcomeMessage.detach(); // detach from the readonly rendering before the clone by wysiwyg.
}
this.editModeEnable = true;
await this._createWysiwyg();
@@ -305,18 +301,7 @@ var EditPageMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({
}
};
this.observer = new MutationObserver(processRecords);
const observe = () => {
if (this.observer) {
this.observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeOldValue: true,
characterData: true,
});
}
};
observe();
this._observe();
this.wysiwyg.odooEditor.addEventListener('observerUnactive', () => {
if (this.observer) {
@@ -324,7 +309,9 @@ var EditPageMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({
this.observer.disconnect();
}
});
this.wysiwyg.odooEditor.addEventListener('observerActive', observe);
this.wysiwyg.odooEditor.addEventListener('observerActive', () => {
this._observe();
});
$('body').addClass('editor_started');
},
@@ -338,6 +325,49 @@ var EditPageMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({
_getReadOnlyAreas () {
return [];
},
/**
* @private
*/
_observe() {
if (this.observer) {
this.observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeOldValue: true,
characterData: true,
});
}
},
/**
* Cleans what must be cleaned for both saving and discarding the changes
* that were made during edition.
*
* @private
*/
_prepareLeavingEditMode() {
if (this.observer) {
this.observer.disconnect();
}
if (this.$editorMessageElements) {
this.$editorMessageElements.removeAttr('data-editor-message');
}
},
/**
* Restores what was done in preparation of leaving the edit mode (e.g.
* in case leaving failed for example). @see _prepareLeavingEditMode.
*
* FIXME this method was created to make the existing code readable and
* logical but it cannot actually be called and have an editor that is still
* usable afterwards (if the save/cancel failed for some reason, the editor
* seems dead no matter what).
*
* @private
*/
_unprepareLeavingEditMode() {
this._addEditorMessages();
this._observe();
},
/**
* Call preventDefault of an event.
*
@@ -564,23 +594,6 @@ var EditPageMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({
$target: ev.data.$target,
});
},
/**
* Called when edition will stop. Notifies the
* WebsiteRoot that is should stop the public widgets.
*
* @private
* @param {OdooEvent} ev
*/
_onEditionWillStop: function (ev) {
this.$editorMessageElements && this.$editorMessageElements.removeAttr('data-editor-message');
this.trigger_up('widgets_stop_request', {
$target: this._targetForEdition(),
});
if (this.observer) {
this.observer.disconnect();
this.observer = undefined;
}
},
/**
* Called when edition was stopped. Notifies the
* WebsiteRoot that is should start the public widgets.
@@ -589,7 +602,7 @@ var EditPageMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({
* @param {OdooEvent} ev
*/
_onEditionWasStopped: function (ev) {
this.editModeEnable = false;
this.editModeEnabled = false;
},
/**
* Called when a snippet is about to be cloned in the page. Notifies the