summaryrefslogtreecommitdiff
path: root/addons/web/static/src
diff options
context:
space:
mode:
authorAlexandre Kühn <aku@odoo.com>2018-02-12 16:10:52 +0100
committerAlexandre Kühn <aku@odoo.com>2018-02-12 16:16:06 +0100
commit83354c2b24bc6c9db1fb63440115d2d4db5bd4ef (patch)
tree0337dd42434200cb7279cc678dccd71cda61a17d /addons/web/static/src
parentdd610140bf272e21a5c3ba45202bcde8e417cfaa (diff)
parent7339ae978f5c3d374c43d59e9f75bc6d279d1c96 (diff)
[MERGE] mail,web,*: JS mail and services tracebacks
* account,bus, web_editor, website, website_slides, web_tour This merge fixes most issues where the webclient crashed with discuss installed in mobile. These issues occur due to the fact that the chat_manager, now a service, was loaded after the webclient is instantiated. The main idea of the fix is to let the webclient register services that are loaded after its instantiation. Despite appearances, the desktop mode had also this issue, but most of the bugs were hidden thanks to the availability of tours that are loaded after the module mail.
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/root_widget.js7
-rw-r--r--addons/web/static/src/js/core/abstract_service.js9
-rw-r--r--addons/web/static/src/js/core/registry.js14
-rw-r--r--addons/web/static/src/js/core/service_mixins.js103
-rw-r--r--addons/web/static/src/js/core/utils.js31
-rw-r--r--addons/web/static/src/js/services/ajax_service.js3
-rw-r--r--addons/web/static/src/js/services/core.js1
-rw-r--r--addons/web/static/src/js/services/local_storage_service.js3
-rw-r--r--addons/web/static/src/js/services/report_service.js3
10 files changed, 103 insertions, 75 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 2dd69d7426c..b4b192c1ea9 100644
--- a/addons/web/static/src/js/chrome/abstract_web_client.js
+++ b/addons/web/static/src/js/chrome/abstract_web_client.js
@@ -245,6 +245,10 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, {
* identified by strings. Each time a title part is changed, all parts
* are gathered, ordered by alphabetical order and displayed in the title
* of the window separated by ``-``.
+ *
+ * @private
+ * @param {string} part
+ * @param {string} title
*/
set_title_part: function (part, title) {
var tmp = _.clone(this.get("title_part"));
diff --git a/addons/web/static/src/js/chrome/root_widget.js b/addons/web/static/src/js/chrome/root_widget.js
new file mode 100644
index 00000000000..9cf0515b134
--- /dev/null
+++ b/addons/web/static/src/js/chrome/root_widget.js
@@ -0,0 +1,7 @@
+odoo.define('root.widget', function (require) {
+"use strict";
+
+var webClient = require('web.web_client');
+
+return webClient;
+});
diff --git a/addons/web/static/src/js/core/abstract_service.js b/addons/web/static/src/js/core/abstract_service.js
index cbd6c1322ca..14efef09b39 100644
--- a/addons/web/static/src/js/core/abstract_service.js
+++ b/addons/web/static/src/js/core/abstract_service.js
@@ -6,7 +6,6 @@ var Mixins = require('web.mixins');
var ServicesMixin = require('web.ServicesMixin');
var AbstractService = Class.extend(Mixins.EventDispatcherMixin, ServicesMixin, {
- Services: [],
dependencies: [],
name: null,
init: function (parent) {
@@ -15,13 +14,5 @@ var AbstractService = Class.extend(Mixins.EventDispatcherMixin, ServicesMixin, {
},
});
-var realExtend = AbstractService.extend;
-
-AbstractService.extend = function () {
- var Service = realExtend.apply(this, arguments);
- AbstractService.prototype.Services.push(Service);
- return Service;
-};
-
return AbstractService;
});
diff --git a/addons/web/static/src/js/core/registry.js b/addons/web/static/src/js/core/registry.js
index d70c09e4ae0..f925b0ea30a 100644
--- a/addons/web/static/src/js/core/registry.js
+++ b/addons/web/static/src/js/core/registry.js
@@ -25,6 +25,7 @@ var Registry = Class.extend({
*/
init: function (mapping) {
this.map = Object.create(mapping || null);
+ this.listeners = []; // listening callbacks on newly added items.
},
//--------------------------------------------------------------------------
@@ -34,12 +35,17 @@ var Registry = Class.extend({
/**
* Add a key (and a value) to the registry.
*
+ * Notify the listeners on newly added item in the registry.
+ *
* @param {string} key
* @param {any} value
* @returns {Registry} can be used to chain add calls.
*/
add: function (key, value) {
this.map[key] = value;
+ _.each(this.listeners, function (callback) {
+ callback(key, value);
+ });
return this;
},
/**
@@ -89,6 +95,14 @@ var Registry = Class.extend({
}
return null;
},
+ /**
+ * Register a callback to execute when items are added to the registry.
+ *
+ * @param {function} callback function with parameters (key, value).
+ */
+ onAdd: function (callback) {
+ this.listeners.push(callback);
+ },
});
return Registry;
diff --git a/addons/web/static/src/js/core/service_mixins.js b/addons/web/static/src/js/core/service_mixins.js
index d5abf727704..4ab0a361988 100644
--- a/addons/web/static/src/js/core/service_mixins.js
+++ b/addons/web/static/src/js/core/service_mixins.js
@@ -1,58 +1,86 @@
odoo.define('web.ServiceProviderMixin', function (require) {
"use strict";
-var AbstractService = require('web.AbstractService');
-var utils = require('web.utils');
+var core = require('web.core');
var ServiceProviderMixin = {
- services: {},
- init: function () {
+ services: {}, // dict containing deployed service instances
+ UndeployedServices: {}, // dict containing classes of undeployed services
+ /**
+ * @override
+ */
+ init: function (parent) {
+ var self = this;
// to properly instantiate services with this as parent, this mixin
// assumes that it is used along the EventDispatcherMixin, and that
// EventDispatchedMixin's init is called first
- var self = this;
- // as EventDispatcherMixin's init is already called, this handler has to
- // be bound manually
+ // as EventDispatcherMixin's init is already called, this handler has
+ // to be bound manually
this.on('call_service', this, this._call_service.bind(this));
- var sortedServices = this._sortServices(AbstractService.prototype.Services);
- _.each(sortedServices, function (Service) {
- var service = new Service(self);
- self.services[service.name] = service;
+ // add already registered services from the service registry
+ _.each(core.serviceRegistry.map, function (Service, serviceName) {
+ self.UndeployedServices[serviceName] = Service;
+ });
+ this._deployServices();
+
+ // listen on newly added services
+ core.serviceRegistry.onAdd(function (serviceName, Service) {
+ self.UndeployedServices[serviceName] = Service;
+ self._deployServices();
});
},
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ */
+ _deployServices: function () {
+ var self = this;
+ var done = false;
+ while (!done) {
+ var Service = _.find(this.UndeployedServices, function (Service) {
+ // no missing dependency
+ return !_.some(Service.prototype.dependencies, function (depName) {
+ return !self.services[depName];
+ });
+ });
+ if (Service) {
+ this.services[Service.prototype.name] = new Service(this);
+ delete this.UndeployedServices[Service.prototype.name];
+ } else {
+ done = true;
+ }
+ }
+ },
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Call the 'service', using data from the 'event' that
+ * has triggered the service call.
+ *
+ * For the ajax service, the arguments are extended with
+ * the target so that it can call back the caller.
+ *
+ * @private
+ * @param {OdooEvent} event
+ */
_call_service: function (event) {
- var service = this.services[event.data.service];
var args = event.data.args || [];
- // ajax service uses an extra 'target' argument for rpc
if (event.data.service === 'ajax' && event.data.method === 'rpc') {
+ // ajax service uses an extra 'target' argument for rpc
args = args.concat(event.target);
}
+ var service = this.services[event.data.service];
var result = service[event.data.method].apply(service, args);
event.data.callback(result);
},
- _sortServices: function (services) {
- var nodes = {};
- // Create nodes (services)
- _.each(services, function (Service) {
- nodes[Service.prototype.name] = Service.prototype.dependencies;
- });
- var sorted;
- try {
- sorted = utils.topologicalSort(nodes);
- } catch (err) {
- console.warn('topologicalSort Error:', err.message);
- sorted = nodes;
- }
- // Sort services based on sorted
- // Note: we convert sorted to an object key=>index for efficiency
- var sortedObj = _.invert(_.object(_.pairs(sorted)));
- sorted = _.sortBy(services, function (Service) {
- return sortedObj[Service.prototype.name];
- });
-
- return sorted;
- },
};
return ServiceProviderMixin;
@@ -69,6 +97,11 @@ var rpc = require('web.rpc');
* @name ServicesMixin
*/
var ServicesMixin = {
+ /**
+ * @param {string} service
+ * @param {string} method
+ * @return {any} result of the service called
+ */
call: function (service, method) {
var args = Array.prototype.slice.call(arguments, 2);
var result;
diff --git a/addons/web/static/src/js/core/utils.js b/addons/web/static/src/js/core/utils.js
index 0b4af722203..1bdd9e70a69 100644
--- a/addons/web/static/src/js/core/utils.js
+++ b/addons/web/static/src/js/core/utils.js
@@ -301,37 +301,6 @@ var utils = {
return fn(mod, Math.floor(x));
},
/**
- * Topological sort of nodes
- *
- * @param {Object} nodes where key is name of node, and value is list of
- * names of dependent nodes.
- * @param {string[]} nodes[name] list of named dependencies of node having
- * 'name' as its name.
- * @return {string[]} topological sort of nodes by their names.
- * @throws {Error} if there is at least one circular
- * dependency between nodes
- */
- topologicalSort: function (nodes) {
- var sorted = [];
- var unresolved = nodes;
- while (!_.isEmpty(unresolved)) {
- // Get all nodes that have no dependency
- var resolvable = _.pick(unresolved, function (dependencies, name) {
- return _.isEmpty(_.difference(dependencies, sorted));
- });
- if (_.isEmpty(resolvable)) {
- throw new Error("Circular dependency detected");
- }
- // Append them to sorted
- sorted = _.union(sorted, _.keys(resolvable));
- // Remove these nodes from unresolved
- _.each(resolvable, function (dependencies, name) {
- unresolved = _.omit(unresolved, name);
- });
- }
- return sorted;
- },
- /**
* performs a half up rounding with a fixed amount of decimals, correcting for float loss of precision
* See the corresponding float_round() in server/tools/float_utils.py for more info
* @param {Number} value the value to be rounded
diff --git a/addons/web/static/src/js/services/ajax_service.js b/addons/web/static/src/js/services/ajax_service.js
index 480549347d4..3446f651a59 100644
--- a/addons/web/static/src/js/services/ajax_service.js
+++ b/addons/web/static/src/js/services/ajax_service.js
@@ -2,6 +2,7 @@ odoo.define('web.AjaxService', function (require) {
"use strict";
var AbstractService = require('web.AbstractService');
+var core = require('web.core');
var session = require('web.session');
var AjaxService = AbstractService.extend({
@@ -21,6 +22,8 @@ var AjaxService = AbstractService.extend({
},
});
+core.serviceRegistry.add('ajax', AjaxService);
+
return AjaxService;
});
diff --git a/addons/web/static/src/js/services/core.js b/addons/web/static/src/js/services/core.js
index 73071a2a65d..522d6b07302 100644
--- a/addons/web/static/src/js/services/core.js
+++ b/addons/web/static/src/js/services/core.js
@@ -41,6 +41,7 @@ return {
crash_registry: new Registry(),
search_filters_registry: new Registry(),
search_widgets_registry: new Registry(),
+ serviceRegistry: new Registry(),
/**
* @type {String}
*/
diff --git a/addons/web/static/src/js/services/local_storage_service.js b/addons/web/static/src/js/services/local_storage_service.js
index a4527026238..7a8c803a0f9 100644
--- a/addons/web/static/src/js/services/local_storage_service.js
+++ b/addons/web/static/src/js/services/local_storage_service.js
@@ -2,6 +2,7 @@ odoo.define('web.LocalStorageService', function (require) {
'use strict';
var AbstractService = require('web.AbstractService');
+var core = require('web.core');
var localStorage = require('web.local_storage');
var LocalStorageService = AbstractService.extend({
@@ -20,6 +21,8 @@ var LocalStorageService = AbstractService.extend({
}
});
+core.serviceRegistry.add('local_storage', LocalStorageService);
+
return LocalStorageService;
});
diff --git a/addons/web/static/src/js/services/report_service.js b/addons/web/static/src/js/services/report_service.js
index a2bb8759a24..cfd62c0499f 100644
--- a/addons/web/static/src/js/services/report_service.js
+++ b/addons/web/static/src/js/services/report_service.js
@@ -6,6 +6,7 @@ odoo.define('web.ReportService', function (require) {
*/
var AbstractService = require('web.AbstractService');
+var core = require('web.core');
var ReportService = AbstractService.extend({
name: 'report',
@@ -28,6 +29,8 @@ var ReportService = AbstractService.extend({
},
});
+core.serviceRegistry.add('report', ReportService);
+
return ReportService;
});