summaryrefslogtreecommitdiff
path: root/addons/web/static/src
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2019-01-18 14:26:33 +0100
committerChristophe Simonis <chs@odoo.com>2019-01-18 14:26:33 +0100
commita337b9ec92943017089ff24ea35ca592557121aa (patch)
treed8b111991257d17aa7d9325ef15bf11730b988e6 /addons/web/static/src
parentf8e37d6a808a302cda973bc0c6f2b275355ef74c (diff)
parentf854e01a983334e214357b4e05083b9a96a61be7 (diff)
[MERGE] forward port branch 12.0 up to f854e01a983
Diffstat (limited to 'addons/web/static/src')
-rw-r--r--addons/web/static/src/js/chrome/abstract_web_client.js4
-rw-r--r--addons/web/static/src/js/chrome/apps_menu.js3
-rw-r--r--addons/web/static/src/js/fields/special_fields.js3
-rw-r--r--addons/web/static/src/js/services/crash_manager.js35
-rw-r--r--addons/web/static/src/js/views/basic/basic_controller.js12
-rw-r--r--addons/web/static/src/js/views/field_manager_mixin.js6
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_controller.js29
-rw-r--r--addons/web/static/src/js/views/pivot/pivot_model.js35
8 files changed, 85 insertions, 42 deletions
diff --git a/addons/web/static/src/js/chrome/abstract_web_client.js b/addons/web/static/src/js/chrome/abstract_web_client.js
index ea8fdc24a18..7082f26e69d 100644
--- a/addons/web/static/src/js/chrome/abstract_web_client.js
+++ b/addons/web/static/src/js/chrome/abstract_web_client.js
@@ -205,6 +205,10 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
});
}
} else {
+ // ignore Chrome video internal error: https://crbug.com/809574
+ if (!error && message === 'ResizeObserver loop limit exceeded') {
+ return;
+ }
var traceback = error ? error.stack : '';
crash_manager.show_error({
type: _t("Odoo Client Error"),
diff --git a/addons/web/static/src/js/chrome/apps_menu.js b/addons/web/static/src/js/chrome/apps_menu.js
index f6535ed764c..eb7faaa971b 100644
--- a/addons/web/static/src/js/chrome/apps_menu.js
+++ b/addons/web/static/src/js/chrome/apps_menu.js
@@ -41,6 +41,9 @@ var AppsMenu = Widget.extend({
* Open the first app in the list of apps
*/
openFirstApp: function () {
+ if (!this._apps.length) {
+ return
+ }
var firstApp = this._apps[0];
this._openApp(firstApp);
},
diff --git a/addons/web/static/src/js/fields/special_fields.js b/addons/web/static/src/js/fields/special_fields.js
index 5057b9de33f..e9dcbc592a3 100644
--- a/addons/web/static/src/js/fields/special_fields.js
+++ b/addons/web/static/src/js/fields/special_fields.js
@@ -122,7 +122,8 @@ var FieldTimezoneMismatch = FieldSelection.extend({
});
var FieldReportLayout = relational_fields.FieldMany2One.extend({
- supportedFieldTypes: ['many2one', 'selection'],
+ // this widget is not generic, so we disable its studio use
+ // supportedFieldTypes: ['many2one', 'selection'],
events: _.extend({}, relational_fields.FieldMany2One.prototype.events, {
'click img': '_onImgClicked',
}),
diff --git a/addons/web/static/src/js/services/crash_manager.js b/addons/web/static/src/js/services/crash_manager.js
index e1da816c8e5..31aa178dc1b 100644
--- a/addons/web/static/src/js/services/crash_manager.js
+++ b/addons/web/static/src/js/services/crash_manager.js
@@ -22,6 +22,7 @@ var map_title ={
var CrashManager = core.Class.extend({
init: function() {
this.active = true;
+ this.isConnected = true;
},
enable: function () {
this.active = true;
@@ -29,8 +30,29 @@ var CrashManager = core.Class.extend({
disable: function () {
this.active = false;
},
- rpc_error: function(error) {
+ handleLostConnection: function () {
var self = this;
+ if (!this.isConnected) {
+ // already handled, nothing to do. This can happen when several
+ // rpcs are done in parallel and fail because of a lost connection.
+ return;
+ }
+ this.isConnected = false;
+ var delay = 2000;
+ core.bus.trigger('connection_lost');
+
+ setTimeout(function checkConnection() {
+ ajax.jsonRpc('/web/webclient/version_info', 'call', {}, {shadow:true}).then(function () {
+ core.bus.trigger('connection_restored');
+ self.isConnected = true;
+ }).fail(function () {
+ // exponential backoff, with some jitter
+ delay = (delay * 1.5) + 500*Math.random();
+ setTimeout(checkConnection, delay);
+ });
+ }, delay);
+ },
+ rpc_error: function(error) {
if (!this.active) {
return;
}
@@ -38,16 +60,7 @@ var CrashManager = core.Class.extend({
return;
}
if (error.code === -32098) {
- core.bus.trigger('connection_lost');
- this.connection_lost = true;
- var timeinterval = setInterval(function() {
- var options = {shadow: true};
- ajax.jsonRpc('/web/webclient/version_info', 'call', {}, options).then(function () {
- clearInterval(timeinterval);
- core.bus.trigger('connection_restored');
- self.connection_lost = false;
- });
- }, 2000);
+ this.handleLostConnection();
return;
}
var handler = core.crash_registry.get(error.data.name, true);
diff --git a/addons/web/static/src/js/views/basic/basic_controller.js b/addons/web/static/src/js/views/basic/basic_controller.js
index 7f578da077d..f26a9cbe737 100644
--- a/addons/web/static/src/js/views/basic/basic_controller.js
+++ b/addons/web/static/src/js/views/basic/basic_controller.js
@@ -38,6 +38,10 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
FieldManagerMixin.init.call(this, this.model);
this.mode = params.mode || 'readonly';
this.handle = this.initialState.id;
+ // savingDef is used to ensure that we always wait for pending save
+ // operations to complete before checking if there are changes to
+ // discard when discardChanges is called
+ this.savingDef = $.when();
},
/**
* @override
@@ -102,14 +106,15 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
return true;
},
/**
- * Waits for the mutex to be unlocked and then calls _.discardChanges.
+ * Waits for the mutex to be unlocked and for changes to be saved, then
+ * calls _.discardChanges.
* This ensures that the confirm dialog isn't displayed directly if there is
* a pending 'write' rpc.
*
* @see _.discardChanges
*/
discardChanges: function (recordID, options) {
- return this.mutex.exec(function () {})
+ return $.when(this.mutex.getUnlockedDef(), this.savingDef)
.then(this._discardChanges.bind(this, recordID || this.handle, options));
},
/**
@@ -183,11 +188,12 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
// mutex-protected as commitChanges function of x2m has to be aware of
// all final changes made to a row.
var self = this;
- return this.mutex.getUnlockedDef().then(function () {
+ this.savingDef = this.mutex.getUnlockedDef().then(function () {
return self.renderer.commitChanges(recordID || self.handle).then(function () {
return self.mutex.exec(self._saveRecord.bind(self, recordID, options));
});
});
+ return this.savingDef;
},
/**
* @override
diff --git a/addons/web/static/src/js/views/field_manager_mixin.js b/addons/web/static/src/js/views/field_manager_mixin.js
index 343730cc7a4..bc61e92a89a 100644
--- a/addons/web/static/src/js/views/field_manager_mixin.js
+++ b/addons/web/static/src/js/views/field_manager_mixin.js
@@ -130,8 +130,10 @@ var FieldManagerMixin = {
if ('offset' in data) {
params.offset = data.offset;
}
- this.model.reload(data.id, params).then(function (db_id) {
- data.on_success(self.model.get(db_id));
+ this.mutex.exec(function () {
+ return self.model.reload(data.id, params).then(function (db_id) {
+ data.on_success(self.model.get(db_id));
+ });
});
},
/**
diff --git a/addons/web/static/src/js/views/kanban/kanban_controller.js b/addons/web/static/src/js/views/kanban/kanban_controller.js
index 2383a0ac730..8c43857277c 100644
--- a/addons/web/static/src/js/views/kanban/kanban_controller.js
+++ b/addons/web/static/src/js/views/kanban/kanban_controller.js
@@ -210,15 +210,17 @@ var KanbanController = BasicController.extend({
*/
_onAddColumn: function (ev) {
var self = this;
- this.model.createGroup(ev.data.value, this.handle).then(function () {
- var state = self.model.get(self.handle, {raw: true});
- var ids = _.pluck(state.data, 'res_id').filter(_.isNumber);
- return self._resequenceColumns(ids);
- }).then(function () {
- return self.update({}, {reload: false});
- }).then(function () {
- self._updateButtons();
- self.renderer.quickCreateToggleFold();
+ this.mutex.exec(function () {
+ return self.model.createGroup(ev.data.value, this.handle).then(function () {
+ var state = self.model.get(self.handle, {raw: true});
+ var ids = _.pluck(state.data, 'res_id').filter(_.isNumber);
+ return self._resequenceColumns(ids);
+ }).then(function () {
+ return self.update({}, {reload: false});
+ }).then(function () {
+ self._updateButtons();
+ self.renderer.quickCreateToggleFold();
+ });
});
},
/**
@@ -271,11 +273,16 @@ var KanbanController = BasicController.extend({
* @private
*/
_onButtonNew: function () {
+ var self = this;
var state = this.model.get(this.handle, {raw: true});
var quickCreateEnabled = this.quickCreateEnabled && viewUtils.isQuickCreateEnabled(state);
if (this.on_create === 'quick_create' && quickCreateEnabled && state.data.length) {
- // Activate the quick create in the first column
- this.renderer.addQuickCreate();
+ // activate the quick create in the first column when the mutex is
+ // unlocked, to ensure that there is no pending re-rendering that
+ // would remove it (e.g. if we are currently adding a new column)
+ this.mutex.getUnlockedDef().then(function () {
+ self.renderer.addQuickCreate();
+ });
} else if (this.on_create && this.on_create !== 'quick_create') {
// Execute the given action
this.do_action(this.on_create, {
diff --git a/addons/web/static/src/js/views/pivot/pivot_model.js b/addons/web/static/src/js/views/pivot/pivot_model.js
index 5a6423fbe34..607cf2d7b2f 100644
--- a/addons/web/static/src/js/views/pivot/pivot_model.js
+++ b/addons/web/static/src/js/views/pivot/pivot_model.js
@@ -321,7 +321,7 @@ var PivotModel = AbstractModel.extend({
};
}
return {
- colGroupBys: this.data.main_col.groupbys,
+ colGroupBys: this.data.main_col.root.groupbys,
context: this.data.context,
domain: this.data.domain,
compare: this.data.compare,
@@ -331,7 +331,7 @@ var PivotModel = AbstractModel.extend({
mainColWidth: this.data.main_col.width,
measures: this.data.measures,
rows: !isRaw && this._computeRows(),
- rowGroupBys: this.data.main_row.groupbys,
+ rowGroupBys: this.data.main_row.root.groupbys,
sortedColumn: this.data.sorted_column,
};
},
@@ -432,22 +432,19 @@ var PivotModel = AbstractModel.extend({
return this._loadData();
}
- var old_row_root = this.data.main_row.root;
- var old_col_root = this.data.main_col.root;
+ var old_row = this.data.main_row;
+ var old_col = this.data.main_col;
return this._loadData().then(function () {
- var new_groupby_length;
if (!('groupBy' in params) && !('pivot_row_groupby' in (params.context || {}))) {
// we only update the row groupbys according to the old groupbys
// if we don't have the key 'groupBy' in params. In that case,
// we want to have the full open state for the groupbys.
- self._updateTree(old_row_root, self.data.main_row.root);
- new_groupby_length = self._getHeaderDepth(self.data.main_row.root) - 1;
- self.data.main_row.groupbys = old_row_root.groupbys.slice(0, new_groupby_length);
+ self._updateTree(old_row.root, self.data.main_row.root);
+ self._updateMainGroupBys(old_row, self.data.main_row);
}
- self._updateTree(old_col_root, self.data.main_col.root);
- new_groupby_length = self._getHeaderDepth(self.data.main_col.root) - 1;
- self.data.main_row.groupbys = old_row_root.groupbys.slice(0, new_groupby_length);
+ self._updateTree(old_col.root, self.data.main_col.root);
+ self._updateMainGroupBys(old_col, self.data.main_col);
});
},
/**
@@ -1052,9 +1049,6 @@ var PivotModel = AbstractModel.extend({
}
}
- this.data.main_row.groupbys = rowGroupBys;
- this.data.main_col.groupbys = colGroupBys;
-
main_row_header.other_root = main_col_header;
main_col_header.other_root = main_row_header;
@@ -1124,6 +1118,19 @@ var PivotModel = AbstractModel.extend({
}
},
/**
+ * Updates the groupBys that the main[row | col] holds
+ *
+ * @private
+ * @param {Object} old: The main abstract header before reload
+ * @param {Object} main: The main abstract header after reload
+ *
+ */
+ _updateMainGroupBys: function (old, main) {
+ var new_groupby_length = this._getHeaderDepth(main.root) - 1;
+ var new_groupby_list = old.root.groupbys.slice(0, new_groupby_length);
+ main.root.groupbys = new_groupby_list;
+ },
+ /**
* @param {Object} old_tree
* @param {Object} new_tree
*/