summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src/js')
-rw-r--r--addons/web/static/src/js/chrome/abstract_web_client.js6
-rw-r--r--addons/web/static/src/js/chrome/action_manager_report.js1
-rw-r--r--addons/web/static/src/js/core/abstract_service.js11
-rw-r--r--addons/web/static/src/js/core/bus.js4
-rw-r--r--addons/web/static/src/js/core/mixins.js125
-rw-r--r--addons/web/static/src/js/core/service_mixins.js164
-rw-r--r--addons/web/static/src/js/core/utils.js31
-rw-r--r--addons/web/static/src/js/services/report_service.js14
8 files changed, 216 insertions, 140 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 58c068fecb1..2dd69d7426c 100644
--- a/addons/web/static/src/js/chrome/abstract_web_client.js
+++ b/addons/web/static/src/js/chrome/abstract_web_client.js
@@ -20,16 +20,16 @@ var data_manager = require('web.data_manager');
var Dialog = require('web.Dialog');
var dom = require('web.dom');
var Loading = require('web.Loading');
-var mixins = require('web.mixins');
var NotificationManager = require('web.NotificationManager');
var RainbowMan = require('web.RainbowMan');
+var ServiceProviderMixin = require('web.ServiceProviderMixin');
var session = require('web.session');
var Widget = require('web.Widget');
var _t = core._t;
var qweb = core.qweb;
-var AbstractWebClient = Widget.extend(mixins.ServiceProvider, {
+var AbstractWebClient = Widget.extend(ServiceProviderMixin, {
custom_events: {
clear_uncommitted_changes: function (e) {
this.clear_uncommitted_changes().then(e.data.callback);
@@ -90,8 +90,8 @@ var AbstractWebClient = Widget.extend(mixins.ServiceProvider, {
},
init: function (parent) {
this.client_options = {};
- mixins.ServiceProvider.init.call(this);
this._super(parent);
+ ServiceProviderMixin.init.call(this);
this.origin = undefined;
this._current_state = null;
this.menu_dm = new concurrency.DropMisordered();
diff --git a/addons/web/static/src/js/chrome/action_manager_report.js b/addons/web/static/src/js/chrome/action_manager_report.js
index bfdfa15f8b6..8975fea2628 100644
--- a/addons/web/static/src/js/chrome/action_manager_report.js
+++ b/addons/web/static/src/js/chrome/action_manager_report.js
@@ -12,6 +12,7 @@ var crash_manager = require('web.crash_manager');
var framework = require('web.framework');
var session = require('web.session');
+
var _t = core._t;
var _lt = core._lt;
diff --git a/addons/web/static/src/js/core/abstract_service.js b/addons/web/static/src/js/core/abstract_service.js
index 6ae0c3183da..cbd6c1322ca 100644
--- a/addons/web/static/src/js/core/abstract_service.js
+++ b/addons/web/static/src/js/core/abstract_service.js
@@ -2,15 +2,22 @@ odoo.define('web.AbstractService', function (require) {
"use strict";
var Class = require('web.Class');
+var Mixins = require('web.mixins');
+var ServicesMixin = require('web.ServicesMixin');
-var AbstractService = Class.extend({
+var AbstractService = Class.extend(Mixins.EventDispatcherMixin, ServicesMixin, {
Services: [],
+ dependencies: [],
name: null,
+ init: function (parent) {
+ Mixins.EventDispatcherMixin.init.call(this, arguments);
+ this.setParent(parent);
+ },
});
var realExtend = AbstractService.extend;
-AbstractService.extend = function() {
+AbstractService.extend = function () {
var Service = realExtend.apply(this, arguments);
AbstractService.prototype.Services.push(Service);
return Service;
diff --git a/addons/web/static/src/js/core/bus.js b/addons/web/static/src/js/core/bus.js
index 1f715da4df1..92278e0f1d6 100644
--- a/addons/web/static/src/js/core/bus.js
+++ b/addons/web/static/src/js/core/bus.js
@@ -10,8 +10,8 @@ var mixins = require('web.mixins');
* @class Bus
*/
return Class.extend(mixins.EventDispatcherMixin, {
- init: function() {
- mixins.EventDispatcherMixin.init.call(this, parent);
+ init: function () {
+ mixins.EventDispatcherMixin.init.call(this, arguments);
},
});
diff --git a/addons/web/static/src/js/core/mixins.js b/addons/web/static/src/js/core/mixins.js
index 23958d9e0f2..e4738fc2f43 100644
--- a/addons/web/static/src/js/core/mixins.js
+++ b/addons/web/static/src/js/core/mixins.js
@@ -3,7 +3,6 @@ odoo.define('web.mixins', function (require) {
var Class = require('web.Class');
var utils = require('web.utils');
-var AbstractService = require('web.AbstractService');
/**
* Mixin to structure objects' life-cycles folowing a parent-children
@@ -409,134 +408,10 @@ var PropertiesMixin = _.extend({}, EventDispatcherMixin, {
}
});
-var ServiceProvider = {
- services: {},
- init: function () {
- var self = this;
- _.each(AbstractService.prototype.Services, function (Service) {
- var service = new Service();
- self.services[service.name] = service;
- });
- this.custom_events = _.clone(this.custom_events);
- this.custom_events.call_service = this._call_service.bind(this);
- },
- _call_service: function (event) {
- var service = this.services[event.data.service];
- var args = (event.data.args || []).concat(event.target);
- var result = service[event.data.method].apply(service, args);
- event.data.callback(result);
- },
-};
-
return {
ParentedMixin: ParentedMixin,
EventDispatcherMixin: EventDispatcherMixin,
PropertiesMixin: PropertiesMixin,
- ServiceProvider: ServiceProvider,
};
});
-
-odoo.define('web.ServicesMixin', function (require) {
-"use strict";
-
-var rpc = require('web.rpc');
-
-/**
- * @mixin
- * @name ServicesMixin
- */
-var ServicesMixin = {
- call: function (service, method) {
- var args = Array.prototype.slice.call(arguments, 2);
- var result;
- this.trigger_up('call_service', {
- service: service,
- method: method,
- args: args,
- callback: function (r) {
- result = r;
- },
- });
- return result;
- },
- /**
- * Builds and executes RPC query. Returns a deferred's promise resolved with
- * the RPC result.
- *
- * @param {string} params either a route or a model
- * @param {string} options if a model is given, this argument is a method
- * @returns {Promise}
- */
- _rpc: function (params, options) {
- var query = rpc.buildQuery(params);
- var def = this.call('ajax', 'rpc', query.route, query.params, options);
- return def ? def.promise() : $.Deferred().promise();
- },
- loadFieldView: function (dataset, view_id, view_type, options) {
- return this.loadViews(dataset.model, dataset.get_context().eval(), [[view_id, view_type]], options).then(function (result) {
- return result[view_type];
- });
- },
- loadViews: function (modelName, context, views, options) {
- var def = $.Deferred();
- this.trigger_up('load_views', {
- modelName: modelName,
- context: context,
- views: views,
- options: options,
- on_success: def.resolve.bind(def),
- });
- return def;
- },
- loadFilters: function (dataset, action_id) {
- var def = $.Deferred();
- this.trigger_up('load_filters', {
- dataset: dataset,
- action_id: action_id,
- on_success: def.resolve.bind(def),
- });
- return def;
- },
- // Session stuff
- getSession: function () {
- var session;
- this.trigger_up('get_session', {
- callback: function (result) {
- session = result;
- }
- });
- return session;
- },
- /**
- * Informs the action manager to do an action. This supposes that the action
- * manager can be found amongst the ancestors of the current widget.
- * If that's not the case this method will simply return an unresolved
- * deferred.
- *
- * @param {any} action
- * @param {any} options
- * @returns {Deferred}
- */
- do_action: function (action, options) {
- var def = $.Deferred();
-
- this.trigger_up('do_action', {
- action: action,
- options: options,
- on_success: function (result) { def.resolve(result); },
- on_fail: function (result) { def.reject(result); },
- });
- return def;
- },
- do_notify: function (title, message, sticky, className) {
- this.trigger_up('notification', {title: title, message: message, sticky: sticky, className: className});
- },
- do_warn: function (title, message, sticky, className) {
- this.trigger_up('warning', {title: title, message: message, sticky: sticky, className: className});
- },
-};
-
-return ServicesMixin;
-
-});
diff --git a/addons/web/static/src/js/core/service_mixins.js b/addons/web/static/src/js/core/service_mixins.js
new file mode 100644
index 00000000000..d5abf727704
--- /dev/null
+++ b/addons/web/static/src/js/core/service_mixins.js
@@ -0,0 +1,164 @@
+odoo.define('web.ServiceProviderMixin', function (require) {
+"use strict";
+
+var AbstractService = require('web.AbstractService');
+var utils = require('web.utils');
+
+var ServiceProviderMixin = {
+ services: {},
+ init: function () {
+ // 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
+ 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;
+ });
+ },
+ _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') {
+ args = args.concat(event.target);
+ }
+ 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;
+
+});
+
+odoo.define('web.ServicesMixin', function (require) {
+"use strict";
+
+var rpc = require('web.rpc');
+
+/**
+ * @mixin
+ * @name ServicesMixin
+ */
+var ServicesMixin = {
+ call: function (service, method) {
+ var args = Array.prototype.slice.call(arguments, 2);
+ var result;
+ this.trigger_up('call_service', {
+ service: service,
+ method: method,
+ args: args,
+ callback: function (r) {
+ result = r;
+ },
+ });
+ return result;
+ },
+ /**
+ * Builds and executes RPC query. Returns a deferred's promise resolved with
+ * the RPC result.
+ *
+ * @param {string} params either a route or a model
+ * @param {string} options if a model is given, this argument is a method
+ * @returns {Promise}
+ */
+ _rpc: function (params, options) {
+ var query = rpc.buildQuery(params);
+ var def = this.call('ajax', 'rpc', query.route, query.params, options);
+ return def ? def.promise() : $.Deferred().promise();
+ },
+ loadFieldView: function (dataset, view_id, view_type, options) {
+ return this.loadViews(dataset.model, dataset.get_context().eval(), [[view_id, view_type]], options).then(function (result) {
+ return result[view_type];
+ });
+ },
+ loadViews: function (modelName, context, views, options) {
+ var def = $.Deferred();
+ this.trigger_up('load_views', {
+ modelName: modelName,
+ context: context,
+ views: views,
+ options: options,
+ on_success: def.resolve.bind(def),
+ });
+ return def;
+ },
+ loadFilters: function (dataset, action_id) {
+ var def = $.Deferred();
+ this.trigger_up('load_filters', {
+ dataset: dataset,
+ action_id: action_id,
+ on_success: def.resolve.bind(def),
+ });
+ return def;
+ },
+ // Session stuff
+ getSession: function () {
+ var session;
+ this.trigger_up('get_session', {
+ callback: function (result) {
+ session = result;
+ }
+ });
+ return session;
+ },
+ /**
+ * Informs the action manager to do an action. This supposes that the action
+ * manager can be found amongst the ancestors of the current widget.
+ * If that's not the case this method will simply return an unresolved
+ * deferred.
+ *
+ * @param {any} action
+ * @param {any} options
+ * @returns {Deferred}
+ */
+ do_action: function (action, options) {
+ var def = $.Deferred();
+
+ this.trigger_up('do_action', {
+ action: action,
+ options: options,
+ on_success: function (result) { def.resolve(result); },
+ on_fail: function (result) { def.reject(result); },
+ });
+ return def;
+ },
+ do_notify: function (title, message, sticky, className) {
+ this.trigger_up('notification', {title: title, message: message, sticky: sticky, className: className});
+ },
+ do_warn: function (title, message, sticky, className) {
+ this.trigger_up('warning', {title: title, message: message, sticky: sticky, className: className});
+ },
+};
+
+return ServicesMixin;
+
+});
diff --git a/addons/web/static/src/js/core/utils.js b/addons/web/static/src/js/core/utils.js
index 1bdd9e70a69..0b4af722203 100644
--- a/addons/web/static/src/js/core/utils.js
+++ b/addons/web/static/src/js/core/utils.js
@@ -301,6 +301,37 @@ 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/report_service.js b/addons/web/static/src/js/services/report_service.js
index 3272eef55bf..a2bb8759a24 100644
--- a/addons/web/static/src/js/services/report_service.js
+++ b/addons/web/static/src/js/services/report_service.js
@@ -6,12 +6,10 @@ odoo.define('web.ReportService', function (require) {
*/
var AbstractService = require('web.AbstractService');
-var session = require('web.session');
-
-var wkhtmltopdfState;
var ReportService = AbstractService.extend({
name: 'report',
+ dependencies: ['ajax'],
/**
* Checks the state of the installation of wkhtmltopdf on the server.
@@ -21,12 +19,12 @@ var ReportService = AbstractService.extend({
* (possible values are 'ok', 'broken', 'install', 'upgrade', 'workers').
*/
checkWkhtmltopdf: function () {
- if (!wkhtmltopdfState) {
- // AAB: for now, services aren't available for each other, so we
- // can't use this._rpc (the ajax service)
- wkhtmltopdfState = session.rpc('/report/check_wkhtmltopdf');
+ if (!this.wkhtmltopdfState) {
+ this.wkhtmltopdfState = this._rpc({
+ route:'/report/check_wkhtmltopdf'
+ });
}
- return wkhtmltopdfState;
+ return this.wkhtmltopdfState;
},
});