diff options
| author | Christophe Simonis <chs@odoo.com> | 2018-06-29 12:46:00 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2018-06-29 12:46:00 +0200 |
| commit | 49edee1f4bd74bc342863d8ef50eb86de0b4c55f (patch) | |
| tree | 20f449f08ba1f4a548c9cb2d82f67f2545048419 /addons/web/static/src/js | |
| parent | 4a08388492c6667b7f4a11f1397b8557c355b208 (diff) | |
| parent | d54a3726eca6ad48d969d5b4a6e559f783809832 (diff) | |
[MERGE] forward port branch saas-11.4 up to d54a3726eca
Diffstat (limited to 'addons/web/static/src/js')
| -rw-r--r-- | addons/web/static/src/js/chrome/abstract_web_client.js | 2 | ||||
| -rw-r--r-- | addons/web/static/src/js/chrome/action_manager.js | 10 | ||||
| -rw-r--r-- | addons/web/static/src/js/chrome/loading.js | 6 | ||||
| -rw-r--r-- | addons/web/static/src/js/core/ajax.js | 62 | ||||
| -rw-r--r-- | addons/web/static/src/js/core/session.js | 67 | ||||
| -rw-r--r-- | addons/web/static/src/js/fields/basic_fields.js | 16 | ||||
| -rw-r--r-- | addons/web/static/src/js/services/crash_manager.js | 3 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/basic/basic_model.js | 44 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/form/form_controller.js | 6 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/form/form_view.js | 1 |
10 files changed, 153 insertions, 64 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 7d3f19c1639..bce77a352ea 100644 --- a/addons/web/static/src/js/chrome/abstract_web_client.js +++ b/addons/web/static/src/js/chrome/abstract_web_client.js @@ -170,7 +170,7 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi core.bus.on('connection_restored', this, this._onConnectionRestored); // crash manager integration - session.on('error', crash_manager, crash_manager.rpc_error); + core.bus.on('rpc_error', crash_manager, crash_manager.rpc_error); window.onerror = function (message, file, line, col, error) { // Scripts injected in DOM (eg: google API's js files) won't return a clean error on window.onerror. // The browser will just give you a 'Script error.' as message and nothing else for security issue. diff --git a/addons/web/static/src/js/chrome/action_manager.js b/addons/web/static/src/js/chrome/action_manager.js index 0fe45f011e8..f98447c8983 100644 --- a/addons/web/static/src/js/chrome/action_manager.js +++ b/addons/web/static/src/js/chrome/action_manager.js @@ -391,10 +391,13 @@ var ActionManager = Widget.extend({ } return this._startController(controller).then(function (controller) { + var prevDialogOnClose; if (self.currentDialogController) { + prevDialogOnClose = self.currentDialogController.onClose; self._closeDialog(true); } + controller.onClose = prevDialogOnClose || options.on_close; var dialog = new Dialog(self, _.defaults({}, options, { buttons: [], dialogClass: controller.className, @@ -405,7 +408,7 @@ var ActionManager = Widget.extend({ self._removeAction(action.jsID); self.currentDialogController = null; if (silent !== true) { - options.on_close(); + controller.onClose(); } }); controller.dialog = dialog; @@ -488,8 +491,9 @@ var ActionManager = Widget.extend({ * @returns {Deferred} resolved immediately */ _executeCloseAction: function (action, options) { + var result; if (!this.currentDialogController) { - options.on_close(); + result = options.on_close(); } this._closeDialog(); @@ -499,7 +503,7 @@ var ActionManager = Widget.extend({ this.trigger_up('show_effect', action.effect); } - return $.when(); + return $.when(result); }, /** * Executes actions of type 'ir.actions.server'. diff --git a/addons/web/static/src/js/chrome/loading.js b/addons/web/static/src/js/chrome/loading.js index 0c1b5e5025f..6c7f102231b 100644 --- a/addons/web/static/src/js/chrome/loading.js +++ b/addons/web/static/src/js/chrome/loading.js @@ -26,9 +26,9 @@ var Loading = Widget.extend({ this._super(parent); this.count = 0; this.blocked_ui = false; - session.on("request", this, this.request_call); - session.on("response", this, this.response_call); - session.on("response_failed", this, this.response_call); + core.bus.on('rpc_request', this, this.request_call); + core.bus.on("rpc_response", this, this.response_call); + core.bus.on("rpc_response_failed", this, this.response_call); }, destroy: function() { this.on_rpc_event(-this.count); diff --git a/addons/web/static/src/js/core/ajax.js b/addons/web/static/src/js/core/ajax.js index d6c67eacaec..fa79c65ef45 100644 --- a/addons/web/static/src/js/core/ajax.js +++ b/addons/web/static/src/js/core/ajax.js @@ -5,7 +5,15 @@ var core = require('web.core'); var utils = require('web.utils'); var time = require('web.time'); -function genericJsonRpc (fct_name, params, fct) { +function genericJsonRpc (fct_name, params, settings, fct) { + var shadow = settings.shadow || false; + delete settings.shadow; + if (!shadow) { + core.bus.trigger('rpc_request'); + } + + var deferred = $.Deferred(); + var data = { jsonrpc: "2.0", method: fct_name, @@ -29,12 +37,56 @@ function genericJsonRpc (fct_name, params, fct) { return def.reject.apply(def, ["communication"].concat(_.toArray(arguments))); }); // FIXME: jsonp? - result.abort = function () { if (xhr.abort) xhr.abort(); }; - return result; + deferred.abort = function () { + deferred.reject('communication', $.Event(), 'abort', 'abort'); + if (xhr.abort) { + xhr.abort(); + } + }; + + result.then(function (result) { + if (!shadow) { + core.bus.trigger('rpc_response'); + } + deferred.resolve(result); + }, function (type, error, textStatus, errorThrown) { + if (type === "server") { + if (!shadow) { + core.bus.trigger('rpc_response'); + } + if (error.code === 100) { + core.bus.trigger('invalidate_session'); + } + deferred.reject(error, $.Event()); + } else { + if (!shadow) { + core.bus.trigger('rpc_response_failed'); + } + var nerror = { + code: -32098, + message: "XmlHttpRequestError " + errorThrown, + data: { + type: "xhr"+textStatus, + debug: error.responseText, + objects: [error, errorThrown] + }, + }; + deferred.reject(nerror, $.Event()); + } + }); + deferred.fail(function () { // Allow deferred user to disable rpc_error call in fail + deferred.fail(function (error, event) { + if (!event.isDefaultPrevented()) { + core.bus.trigger('rpc_error', error, event); + } + }); + }); + return deferred; } function jsonRpc(url, fct_name, params, settings) { - return genericJsonRpc(fct_name, params, function(data) { + settings = settings || {}; + return genericJsonRpc(fct_name, params, settings, function(data) { return $.ajax(url, _.extend({}, settings, { url: url, dataType: 'json', @@ -47,7 +99,7 @@ function jsonRpc(url, fct_name, params, settings) { function jsonpRpc(url, fct_name, params, settings) { settings = settings || {}; - return genericJsonRpc(fct_name, params, function(data) { + return genericJsonRpc(fct_name, params, settings, function(data) { var payload_str = JSON.stringify(data, time.date_to_utc); var payload_url = $.param({r:payload_str}); var force2step = settings.force2step || false; diff --git a/addons/web/static/src/js/core/session.js b/addons/web/static/src/js/core/session.js index 11423eec909..09b65357404 100644 --- a/addons/web/static/src/js/core/session.js +++ b/addons/web/static/src/js/core/session.js @@ -49,6 +49,7 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { this.qweb_mutex = new concurrency.Mutex(); this.currencies = {}; this._groups_def = {}; + core.bus.on('invalidate_session', this, this._onInvalidateSession); }, setup: function (origin, options) { // must be able to customize server @@ -327,31 +328,31 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { rpc: function (url, params, options) { var self = this; options = _.clone(options || {}); - var shadow = options.shadow || false; options.headers = _.extend({}, options.headers); if (odoo.debug) { options.headers["X-Debug-Mode"] = $.deparam($.param.querystring()).debug; } - delete options.shadow; - - var deferred = $.Deferred(); + var deferred = self.check_session_id(); var aborted = false; + var xhrDef; deferred.abort = function () { - aborted = true; + if (xhrDef) { + xhrDef.abort(); + } else { + aborted = true; + } }; - self.check_session_id().then(function () { + + return deferred.then(function () { if (aborted) { - return deferred.reject('communication', $.Event(), 'abort', 'abort'); + return $.Deferred().reject('communication', $.Event(), 'abort', 'abort'); } // TODO: remove if (! _.isString(url)) { _.extend(options, url); url = url.url; } - // TODO correct handling of timeouts - if (! shadow) - self.trigger('request'); var fct; if (self.origin_server) { fct = ajax.jsonRpc; @@ -370,40 +371,9 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { url = self.url(url, null); options.session_id = self.session_id || ''; } - var p = fct(url, "call", params, options); - deferred.abort = p.abort.bind(p); // Allow to abort the rpc call - p = p.then(function (result) { - if (! shadow) - self.trigger('response'); - return deferred.resolve(result); - }, function (type, error, textStatus, errorThrown) { - if (type === "server") { - if (! shadow) - self.trigger('response'); - if (error.code === 100) { - self.uid = false; - } - return deferred.reject(error, $.Event()); - } else { - if (! shadow) - self.trigger('response_failed'); - var nerror = { - code: -32098, - message: "XmlHttpRequestError " + errorThrown, - data: {type: "xhr"+textStatus, debug: error.responseText, objects: [error, errorThrown] } - }; - return deferred.reject(nerror, $.Event()); - } - }); - return deferred.fail(function () { // Allow deferred user to disable rpc_error call in fail - deferred.fail(function (error, event) { - if (!event.isDefaultPrevented()) { - self.trigger('error', error, event); - } - }); - }); + xhrDef = fct(url, "call", params, options); + return xhrDef; }); - return deferred; }, url: function (path, params) { params = _.extend(params || {}); @@ -428,6 +398,17 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { getTZOffset: function (date) { return -new Date(date).getTimezoneOffset(); }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @private + */ + _onInvalidateSession: function () { + this.uid = false; + }, }); return Session; diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index aa66439143d..cb676871fc5 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -2597,6 +2597,21 @@ var AceEditor = DebouncedField.extend({ //-------------------------------------------------------------------------- /** + * Format value + * + * Note: We have to overwrite this method to always return a string. + * AceEditor works with string and not boolean value. + * + * @override + * @private + * @param {boolean|string} value + * @returns {string} + */ + _formatValue: function (value) { + return this._super.apply(this, arguments) || ''; + }, + + /** * @override * @private */ @@ -2618,6 +2633,7 @@ var AceEditor = DebouncedField.extend({ this.aceSession.setValue(newValue); } }, + /** * Starts the ace library on the given DOM element. This initializes the * ace editor option according to the edit/readonly mode and binds ace diff --git a/addons/web/static/src/js/services/crash_manager.js b/addons/web/static/src/js/services/crash_manager.js index 8d15efda686..f3bfa87341c 100644 --- a/addons/web/static/src/js/services/crash_manager.js +++ b/addons/web/static/src/js/services/crash_manager.js @@ -41,7 +41,8 @@ var CrashManager = core.Class.extend({ core.bus.trigger('connection_lost'); this.connection_lost = true; var timeinterval = setInterval(function() { - ajax.jsonRpc('/web/webclient/version_info').then(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; diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js index d8ebddc101c..6ad520f9eb0 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -683,13 +683,16 @@ var BasicModel = AbstractModel.extend({ /** * Returns true if a record can be abandoned. * - * A record can be abandonned if it is a new record, except if - * this datapoint has been specifically tagged as "do not abandon". + * Case for not abandoning the record: * - * Example: + * 1. flagged as 'no abandon' (i.e. during a `default_get`, including any + * `onchange` from a `default_get`) + * 2. registered in a list on addition + * 2.1. registered as non-new addition + * 2.2. registered as new additon on update + * 3. record is not new * - * - Discard record from "Add an item" => "New" record => abandon - * - Discard record from `default_get`/`onchange` => do not abandon + * Otherwise, the record can be abandoned. * * This is useful when discarding changes on this record, as it means that * we must keep the record even if some fields are invalids (e.g. required @@ -699,7 +702,29 @@ var BasicModel = AbstractModel.extend({ * @returns {boolean} */ canBeAbandoned: function (id) { - return !this.localData[id]._noAbandon && this.isNew(id); + // 1. no drop if flagged + if (this.localData[id]._noAbandon) { + return false; + } + // 2. no drop in a list on "ADD in some cases + var record = this.localData[id]; + var parent = this.localData[record.parentID]; + if (parent) { + var entry = _.findWhere(parent._savePoint, {operation: 'ADD', id: id}); + if (entry) { + // 2.1. no drop on non-new addition in list + if (!entry.isNew) { + return false; + } + // 2.2. no drop on new addition on "UPDATE" + var lastEntry = _.last(parent._savePoint); + if (lastEntry.operation === 'UPDATE' && lastEntry.id === id) { + return false; + } + } + } + // 3. drop new records + return this.isNew(id); }, /** * Returns true if a record is dirty. A record is considered dirty if it has @@ -2673,7 +2698,7 @@ var BasicModel = AbstractModel.extend({ count: ids.length, context: record.context, fieldsInfo: fieldsInfo, - fields: view ? view.viewFields : fieldInfo.relatedFields, + fields: view ? view.fields : fieldInfo.relatedFields, limit: fieldInfo.limit, modelName: field.relation, res_ids: ids, @@ -2685,6 +2710,11 @@ var BasicModel = AbstractModel.extend({ relationField: field.relation_field, viewType: view ? view.type : fieldInfo.viewType, }); + // set existing changes to the list + if (record._changes && record._changes[fieldName]) { + list._changes = self.localData[record._changes[fieldName]]._changes; + record._changes[fieldName] = list.id; + } record.data[fieldName] = list.id; if (!fieldInfo.__no_fetch) { var def = self._readUngroupedList(list).then(function () { diff --git a/addons/web/static/src/js/views/form/form_controller.js b/addons/web/static/src/js/views/form/form_controller.js index be49c12e768..d0ca5c3d09f 100644 --- a/addons/web/static/src/js/views/form/form_controller.js +++ b/addons/web/static/src/js/views/form/form_controller.js @@ -634,7 +634,11 @@ var FormController = BasicController.extend({ */ _onSave: function (ev) { ev.stopPropagation(); // Prevent x2m lines to be auto-saved - this.saveRecord(); + var self = this; + this._disableButtons(); + this.saveRecord().always(function () { + self._enableButtons(); + }); }, /** * This method is called when someone tries to sort a column, most likely diff --git a/addons/web/static/src/js/views/form/form_view.js b/addons/web/static/src/js/views/form/form_view.js index f0ea360fed0..c688a997581 100644 --- a/addons/web/static/src/js/views/form/form_view.js +++ b/addons/web/static/src/js/views/form/form_view.js @@ -100,6 +100,7 @@ var FormView = BasicView.extend({ for (var viewName in views) { // clone to make runbot green? attrs.views[viewName] = self._processFieldsView(views[viewName], viewName); + attrs.views[viewName].fields = attrs.views[viewName].viewFields; self._processSubViewAttrs(attrs.views[viewName], attrs); } self._setSubViewLimit(attrs); |
